Merge "SF: return the active display from getInternalDisplayId"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 75505f3..eab72f4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1237,22 +1237,29 @@
         std::string path(title);
         path.append(" - ").append(String8(service).c_str());
         size_t bytes_written = 0;
-        status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
-        if (status == OK) {
-            dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
-            std::chrono::duration<double> elapsed_seconds;
-            if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH &&
-                service == String16("meminfo")) {
-                // Use a longer timeout for meminfo, since 30s is not always enough.
-                status = dumpsys.writeDump(STDOUT_FILENO, service, 60s,
-                                           /* as_proto = */ false, elapsed_seconds, bytes_written);
-            } else {
-                status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout,
-                                           /* as_proto = */ false, elapsed_seconds, bytes_written);
+        if (PropertiesHelper::IsDryRun()) {
+             dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
+             dumpsys.writeDumpFooter(STDOUT_FILENO, service, std::chrono::milliseconds(1));
+        } else {
+            status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
+            if (status == OK) {
+                dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
+                std::chrono::duration<double> elapsed_seconds;
+                if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH &&
+                    service == String16("meminfo")) {
+                    // Use a longer timeout for meminfo, since 30s is not always enough.
+                    status = dumpsys.writeDump(STDOUT_FILENO, service, 60s,
+                                               /* as_proto = */ false, elapsed_seconds,
+                                                bytes_written);
+                } else {
+                    status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout,
+                                               /* as_proto = */ false, elapsed_seconds,
+                                                bytes_written);
+                }
+                dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);
+                bool dump_complete = (status == OK);
+                dumpsys.stopDumpThread(dump_complete);
             }
-            dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);
-            bool dump_complete = (status == OK);
-            dumpsys.stopDumpThread(dump_complete);
         }
 
         auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -1836,8 +1843,10 @@
     }
 
     /* Run some operations that require root. */
-    ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
-    ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping());
+    if (!PropertiesHelper::IsDryRun()) {
+        ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
+        ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping());
+    }
 
     ds.AddDir(RECOVERY_DIR, true);
     ds.AddDir(RECOVERY_DATA_DIR, true);
diff --git a/data/etc/apex/Android.bp b/data/etc/apex/Android.bp
new file mode 100644
index 0000000..8c4929c
--- /dev/null
+++ b/data/etc/apex/Android.bp
@@ -0,0 +1,34 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+apex_key {
+    name: "com.android.hardware.core_permissions.key",
+    public_key: "com.android.hardware.core_permissions.avbpubkey",
+    private_key: "com.android.hardware.core_permissions.pem",
+}
+
+android_app_certificate {
+    name: "com.android.hardware.core_permissions.certificate",
+    certificate: "com.android.hardware.core_permissions",
+}
+
+apex {
+    name: "com.android.hardware.core_permissions",
+    manifest: "apex_manifest.json",
+    key: "com.android.hardware.core_permissions.key",
+    certificate: ":com.android.hardware.core_permissions.certificate",
+    file_contexts: "file_contexts",
+    updatable: false,
+    // Install the apex in /vendor/apex
+    soc_specific: true,
+    prebuilts: [
+        "handheld_core_hardware.prebuilt.xml",
+        "aosp_excluded_hardware.prebuilt.xml",
+    ],
+}
diff --git a/data/etc/apex/apex_manifest.json b/data/etc/apex/apex_manifest.json
new file mode 100644
index 0000000..5bbf229
--- /dev/null
+++ b/data/etc/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.hardware.core_permissions",
+  "version": 1
+}
diff --git a/data/etc/apex/com.android.hardware.core_permissions.avbpubkey b/data/etc/apex/com.android.hardware.core_permissions.avbpubkey
new file mode 100644
index 0000000..b9164fb
--- /dev/null
+++ b/data/etc/apex/com.android.hardware.core_permissions.avbpubkey
Binary files differ
diff --git a/data/etc/apex/com.android.hardware.core_permissions.pem b/data/etc/apex/com.android.hardware.core_permissions.pem
new file mode 100644
index 0000000..7e2826d
--- /dev/null
+++ b/data/etc/apex/com.android.hardware.core_permissions.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAuJjZgFCp6uX+xgKET1FsXYqEPGfYEWUWJ4VSP+Y5fgth/Om3
+XCRBhKSD4CPyL/E9ohh7eSLRqIzw/idUazgCqk+yYLiVkYZiuY02jcui1/Vze9er
+Nwi0ZSwA+zcvKCEOwZ3PBT6W1kehiQ5PU0IS+78po8LoUrvycmvbXJTHlVt1x2bo
+y2DQmxRjIH9xfACwFwh/JnKyr1O2NGjFbk+z3CYx9l7c0Ew9U/kGL3teSbMEi1le
+2PApAHYUA+kXiwjF07aRUN+zzSdZsI7goQXEsmqGXNy7Fzdp/UDocayBmCdI3a35
+igcPRUryBIf1YdSS+E9DwoXRR7pwzs4ajvVTrzuv7UohTnhPwj95TD0E3Z64r7S7
+AT4jtm9gbkAsKNkKOqioGTEIdmvj2prOA4wOUmVBGwOjGcjsyEPJaa56s0WEXUop
++OD2ZMV3StAIwQ5c/gpFzxWl+qATv3MH9MgwAIjT1TDyXv0R+q9JbFbgTtO748RS
+MZUb1i2odggNQCWRtv8fqJ7c51H7pUpHXCXElXHysFq2oBOY4J1jXr1craxsPn1P
+RcImVwAYTV80jOfmYtWhdJhDavDeD4uinLw+4HeZnFNwZXqCJl0LtLxMDRxoDCr7
+YgT2znh9BM6XXg8jekfkDYb5wyloU1eOZJMxF04pGecDB9n1w/OFBA4v0WECAwEA
+AQKCAgEArjhsRsNaqvzo6L7lWurxCJO73Drx3PD36NLWXsKNjl113LpEOO1q/KI8
+aKXkZMUdM0hB+IEZOSfUJzq9XPge49iV9N0hJJidwpv5WfhQN9xLYx2YVTec8kOG
+pZJeqlQQ1kF3am6484HlfjIIQf8BZaH0zb8df0AtQTp0bTtp5pfMYCbLHW/BUiv6
+pmhBlhQcHZECWCo2ZGzwcSRU+Zi1mthdnTXI17qswv0rjlK0GYCgkFgHwV1ghTPs
+DgjHFIxyES+klJyc2MoDxzQB41dLXkxVhX06Al5lZQUGnIqAQTcKeVZCRrgE/JQQ
+OKCMwglbsIk23XdonnbjEvvIaxY1JHjcnPBpOC4+O11fE0DiHXK3GBj5KlfVvB5D
+oMWko3R/Ky/T/KJhHYXbC1H71oYueCaY57kHFKk2k3qRJG4DU4MY20cfUZ0vp14H
+KJ++gDy0pxxjl4ZUiryBCti5k5GPU8Mm46MLJ/YPdX6Dj1nMtOgGpZkGQYIKPhEI
+qZjZBRyZlHeTJsTMc8Eh3/Krimmra5ID95HfJnTTHHshbRcLwL6/zMTU5fkwarsC
+f4HQ0602/9HqyI8Ty1S9Z4oByjwfW2uDcosnuOPfk/8XwfLWxrf2+TsAd3cXhOKw
+rwUfELzcgYNueLGTJOCsEJfo8NIIEGJCNSgMnWXmIAUIAlrMP8ECggEBAOt9X4Lb
+fkrQLjcQ+K1oOI0Q+43htNRYXiG2IDhmDlA+UnqPP+9j2cVyuo4QYhKTFXRbbezR
+blUHAowd4hKHtODaHMuq90KbeByVNB8idcIb+ELAOK4Ff+qd9ijrBIOuZZQB+KOo
+SlxVjy1LM0QGtUTJRHx4dkCmp+hMqrIc4LQOWd4hV5h85Oj8kp1L1eMvxEStUtwP
+tYR80OoOdDxgXcBHLdPs4rc0WunRabGE+cnCMrTy31D95OWg6aw/+XKSTUS5Hfdy
+4jDIwP8DR6JU71qNgen+fwrHFeDienM40sSpi85/WQndQW5TwOMbDlEfmgn6D4+s
+rVWqFk1XElfwwSkCggEBAMisvdv5fH4UeH+tz5cF5f3ZNY+NX8uj85wCsJAPaAVx
+i3t8BqEKUPA6FPt9SDMWg4QtgSSl0NmhH2F9CcAZUCiTZUrtgqyIo80ALONW1FR9
+ofElvZEeV2IzKn3Ci4HA2pKRtMcjjVLwxzdoHakbPS9CbdCGKYE3W75Huw410IW6
+gV4zQ2mWHT+FxXaatl6Arj0x7DHLeSrMV33uxcYWoclmrAK24uhq2gwhtC8U0SvY
+rtJ7+KpCRd5v3Cyzo2AEbZKyJYVKbMDu9RHDZwkZw6wVqaOKBPJVyC++yidksexX
+ZT0WGX0f23t+2jbbsNY6H27pJm9ApLuAYwiMGv9n/XkCggEADLlAcNyVLUukQ5tq
+JExuScjyHo9kati/dUjW4tU4zsMfR7n3tWKKwK1bQRPHiMNjtF7ASLxkHrn7PEDd
+Fy0367I9Pg/lvjaSPdEd+NSu0icaudiS92wapj2UsE9Kdib1HBMjMQyFwAlrbAIV
+KgbGwomxZpxHn2Shy95glrESvwfLeUIJ7pZI9AG5lkAjtVu+WguXX4aFwzvPOeZA
+B4cZaasu4bV55nYwt1N2R34s1ObmQHqi8EhXlsSj+4eVXchj3mO2J8mQSRx/uQef
+VjkKmbTtoQv8J0PsfbMe9JzMXo3enPCqiernfyONV3f9xQpVE1bsglHNJ8TB4bnj
+pta+SQKCAQEArDqNrFkAftkk3jgXnX9jeC3O6UilugoZj4FDdjCyz1E3LCEzM02+
+T58Z2QoaSDZ/Y5cGaqShjdbaLvp4vtU61chDPD6CU3/mTZBj9i3UiDtXHLeObhlD
+WDWft1WcFB2nufmx1OPvbArYf/Ys1rFZHtF9nGU5A/y2EaZQpY6MS+nZFDcdGWbL
+7XPrGLMJ6Cu63yyUkdwXPyMnyB6AwVU1P7yNzrqWHnFueNEIawwLxfzvdhkOP1on
+yxPoPLlkc4j5XdjlmPNaSXANB1TUfpwNMwlYkdJoEnCLImc16v9iMPyFGBt6fsgz
+wFcMA98jc4lo5vDVmtA5Ue+Lj49nsGLYyQKCAQEAoB21vLDm7sLcZhzHFnh7h3L0
+wLMFMyQ0QDMWOqrlABcME21CWhibu8XwouF+hHLKX9GjaBB3ujkyeun1x8MpGQwT
+1SrMVN4osHpFrCh85uK5mabsqC8YjICIncU1U1ykxDV9v7TXuXgMtUIMKPFcUQLv
+ckf/PNE1Fvt5ESn2GIxk+ibM0L2kzHgDFgwiPx+k8GmJt5VZSXwehPmH6jgyCBIv
+kPHos1Q/z2LtfdUZcGhwX88mBNBpk3UXjiU8qO+ddoXCRgbThFDqYvOcdacbKGc0
+upFMhNsTWocn7CW0rbzusTsTt6bSWCGas8f9G9CMNN6rp8SW7Qc04m6sXVjPbw==
+-----END RSA PRIVATE KEY-----
diff --git a/data/etc/apex/com.android.hardware.core_permissions.pk8 b/data/etc/apex/com.android.hardware.core_permissions.pk8
new file mode 100644
index 0000000..4497844
--- /dev/null
+++ b/data/etc/apex/com.android.hardware.core_permissions.pk8
Binary files differ
diff --git a/data/etc/apex/com.android.hardware.core_permissions.x509.pem b/data/etc/apex/com.android.hardware.core_permissions.x509.pem
new file mode 100644
index 0000000..57a311d
--- /dev/null
+++ b/data/etc/apex/com.android.hardware.core_permissions.x509.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF7zCCA9cCFD7UZbcK7ZVyJegJvOs8e5T/n0DLMA0GCSqGSIb3DQEBCwUAMIGy
+MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
+bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
+MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEuMCwGA1UEAwwlY29t
+LmFuZHJvaWQuaGFyZHdhcmUuY29yZV9wZXJtaXNzaW9uczAgFw0yMTA4MjYyMDM2
+NTBaGA80NzU5MDcyMzIwMzY1MFowgbIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
+YWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRy
+b2lkMRAwDgYDVQQLDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu
+ZHJvaWQuY29tMS4wLAYDVQQDDCVjb20uYW5kcm9pZC5oYXJkd2FyZS5jb3JlX3Bl
+cm1pc3Npb25zMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1ZGPs2s2
+R9/+jd3dHLH0nt4Z5WWduQ4Wqy6H9oojktTCv0IJekfiC6L0VHOvW9DKtSEGWTJg
+sJdoEpd2KDhNxAdjbE6k50AfKD0CAE0MAX2MyJIpUz7b6p6p7kxB7xpJohSGDOSZ
+otuMJ16W99PElwOtQr99Wztf+WehmEkKYDUVA0V3+UP3s0quAo3ZrFYgZxXy51YF
+/rcM+HFNrpBRNZRhm3n/2uM4vqt+HTjUFvPuxFIq44DH64ofD7OYBOTbq7rU3lFr
+jEP4IlVH1mjyirh0pWIzXtqhcj2yfV8HjqnwHz+DhIA7kI1hZNOTotTiYKZszboy
+mvo8YA+fuSDCohFxVJVHZGFu+FA+m353ZPLTDTee+d3F9JGbLxa3NoPAgDIyU/tR
+KD7+fKxfr32coZzf3bDi9T4CHBoHKoikRWqOAhkZ/xGvjX12bI6eS1QP9THGu3d6
+o+ZzSZDPR9GSu8ggFtkRGgaIIbgV3y4XUKqLK3+pyg14tHwqvSYMItZ7oOYKMT47
+R9qb8MlvAWS2ooZlJwSkGSM3y0pTwy3rKXug+Cpvx7zjc3Nm/srzdPAmrEvBTZZN
+k0J1oYKqiNHV1N3PIlMRE2d4g7MVF4GEwrAgL126JZOOmzefkP59eAPNrH6Coyka
+3pAVnifemHYZlhWpbKO6pnuxEwvBGXvJlgkCAwEAATANBgkqhkiG9w0BAQsFAAOC
+AgEAfc6TD6fURc9sKHn5TSi30rIlarR6h1TbxFL4PQroEXWafkNmOMujdyV6YZlZ
+j/lzU4ko+Pn3y1aU4r88PkrjkR2Ttf01H3wj8IkTVyl3kN3o/Ud2jJygL3SbjMS1
+CAxiotvPA3lst3KN3nnyFLe02EB4OIi8fS14/kUVFVz+djpGDhi/lJUzcTCzO1NR
+yoC3RuAHBrly1+0QYcXiRTtvgnXrUqsZery8pysR7OK8fWkJzQ6I6OipElrcRzfK
+qoRq/u7nn9FJxXauVjM5HNCPq1VsB8+/FQqP2CgvxNPtkxy8AkXS3hKMBC0Id0Mo
+sDsAr88QDnCb5DqrWSMDKTKA0k+2a6QwS72ANkK5Uq2Hf/xUVjn+yHLSFuo4pCYe
+TX4ZCjK8My+XPWEiHBV/AELAWA9SxxZFhHmyuPj3MwN5uJcPNPsQepnhpeGv0Dlp
+J7UfzkdAZvOyQ+9CAQfu8+e2MilLHC3uqfnGLpHGkcHoN93rmH1x14JdapKKk3XL
+4Wo0RzEjGQekjMF0topQSqDTukOMdtZ1T0WTVOstYLlNqlhn0ZVbwVANGklN/fDW
+d6OpayLPuuaLBAM6Ivnv7n4XA49ojd9Gw305Ha/ATEoH9KwImFjvUa4d9SkR0haP
+hAiGhpaWViEe2zXOpzLhePgqc2gJ/IO7vIgrVks0iVeBoWE=
+-----END CERTIFICATE-----
diff --git a/data/etc/apex/file_contexts b/data/etc/apex/file_contexts
new file mode 100644
index 0000000..6524a5e
--- /dev/null
+++ b/data/etc/apex/file_contexts
@@ -0,0 +1,2 @@
+(/.*)?		u:object_r:vendor_file:s0
+/etc(/.*)?	u:object_r:vendor_configs_file:s0
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7036d05..06594d7 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -114,7 +114,6 @@
         "ParcelFileDescriptor.cpp",
         "PersistableBundle.cpp",
         "ProcessState.cpp",
-        "RpcAddress.cpp",
         "RpcSession.cpp",
         "RpcServer.cpp",
         "RpcState.cpp",
@@ -257,6 +256,7 @@
     ],
     srcs: [
         "RpcTransportTls.cpp",
+        "RpcCertificateUtils.cpp",
     ],
 }
 
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 1100d72..92df874 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -156,7 +156,7 @@
     return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
 }
 
-sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, const RpcAddress& address) {
+sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, uint64_t address) {
     LOG_ALWAYS_FATAL_IF(session == nullptr, "BpBinder::create null session");
 
     // These are not currently tracked, since there is no UID or other
@@ -193,7 +193,7 @@
     return std::holds_alternative<RpcHandle>(mHandle);
 }
 
-const RpcAddress& BpBinder::rpcAddress() const {
+uint64_t BpBinder::rpcAddress() const {
     return std::get<RpcHandle>(mHandle).address;
 }
 
@@ -510,7 +510,7 @@
 {
     ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
     if (CC_UNLIKELY(isRpcBinder())) {
-        (void)rpcSession()->sendDecStrong(rpcAddress());
+        (void)rpcSession()->sendDecStrong(this);
         return;
     }
     IF_ALOGV() {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index fa9f3a9..9e04ffe 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1426,6 +1426,25 @@
     return ret;
 }
 
+#ifndef __ANDROID_VNDK__
+status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
+                                              uint32_t *async_received)
+{
+    int ret = 0;
+    binder_frozen_status_info info;
+    info.pid = pid;
+
+#if defined(__ANDROID__)
+    if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
+        ret = -errno;
+#endif
+    *sync_received = info.sync_recv;
+    *async_received = info.async_recv;
+
+    return ret;
+}
+#endif
+
 status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
     struct binder_freeze_info info;
     int ret = 0;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 99b6be8..7575252 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -205,11 +205,11 @@
         if (binder) {
             status_t status = writeInt32(1); // non-null
             if (status != OK) return status;
-            RpcAddress address = RpcAddress::zero();
+            uint64_t address;
             // TODO(b/167966510): need to undo this if the Parcel is not sent
             status = mSession->state()->onBinderLeaving(mSession, binder, &address);
             if (status != OK) return status;
-            status = address.writeToParcel(this);
+            status = writeUint64(address);
             if (status != OK) return status;
         } else {
             status_t status = writeInt32(0); // null
@@ -237,7 +237,7 @@
                     return INVALID_OPERATION;
                 }
             }
-            const int32_t handle = proxy ? proxy->getPrivateAccessorForId().binderHandle() : 0;
+            const int32_t handle = proxy ? proxy->getPrivateAccessor().binderHandle() : 0;
             obj.hdr.type = BINDER_TYPE_HANDLE;
             obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
             obj.handle = handle;
@@ -279,15 +279,15 @@
     if (isForRpc()) {
         LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel");
 
-        int32_t isNull;
-        status_t status = readInt32(&isNull);
+        int32_t isPresent;
+        status_t status = readInt32(&isPresent);
         if (status != OK) return status;
 
         sp<IBinder> binder;
 
-        if (isNull & 1) {
-            auto addr = RpcAddress::zero();
-            if (status_t status = addr.readFromParcel(*this); status != OK) return status;
+        if (isPresent & 1) {
+            uint64_t addr;
+            if (status_t status = readUint64(&addr); status != OK) return status;
             if (status_t status = mSession->state()->onBinderEntering(mSession, addr, &binder);
                 status != OK)
                 return status;
@@ -572,7 +572,7 @@
     LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
 
     if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
-        markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcSession());
+        markForRpc(binder->remoteBinder()->getPrivateAccessor().rpcSession());
     }
 }
 
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 8ab0e88..94b2806 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -212,7 +212,7 @@
     binder_node_info_for_ref info;
     memset(&info, 0, sizeof(binder_node_info_for_ref));
 
-    info.handle = binder->getPrivateAccessorForId().binderHandle();
+    info.handle = binder->getPrivateAccessor().binderHandle();
 
     status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info);
 
@@ -301,7 +301,7 @@
                    return nullptr;
             }
 
-            sp<BpBinder> b = BpBinder::create(handle);
+            sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
             e->binder = b.get();
             if (b) e->refs = b->getWeakRefs();
             result = b;
diff --git a/libs/binder/RpcAddress.cpp b/libs/binder/RpcAddress.cpp
deleted file mode 100644
index ffc94b9..0000000
--- a/libs/binder/RpcAddress.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.
- */
-
-#include <binder/RpcAddress.h>
-
-#include <android-base/hex.h>
-#include <binder/Parcel.h>
-
-#include "Debug.h"
-#include "RpcState.h"
-#include "RpcWireFormat.h"
-
-namespace android {
-
-RpcAddress RpcAddress::zero() {
-    return RpcAddress();
-}
-
-bool RpcAddress::isZero() const {
-    RpcWireAddress ZERO{.options = 0};
-    return memcmp(mRawAddr.get(), &ZERO, sizeof(RpcWireAddress)) == 0;
-}
-
-static void ReadRandomBytes(uint8_t* buf, size_t len) {
-    int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
-    if (fd == -1) {
-        ALOGE("%s: cannot read /dev/urandom", __func__);
-        return;
-    }
-
-    size_t n;
-    while ((n = TEMP_FAILURE_RETRY(read(fd, buf, len))) > 0) {
-        len -= n;
-        buf += n;
-    }
-    if (len > 0) {
-        ALOGW("%s: there are %d bytes skipped", __func__, (int)len);
-    }
-    close(fd);
-}
-
-RpcAddress RpcAddress::random(bool forServer) {
-    // The remainder of this header acts as reserved space for different kinds
-    // of binder objects.
-    uint64_t options = RPC_WIRE_ADDRESS_OPTION_CREATED;
-
-    // servers and clients allocate addresses independently, so this bit can
-    // tell you where an address originates
-    if (forServer) options |= RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
-
-    RpcAddress ret;
-    RpcWireAddress* raw = ret.mRawAddr.get();
-
-    raw->options = options;
-    ReadRandomBytes(raw->address, sizeof(raw->address));
-
-    LOG_RPC_DETAIL("Creating new address: %s", ret.toString().c_str());
-    return ret;
-}
-
-bool RpcAddress::isForServer() const {
-    return mRawAddr.get()->options & RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
-}
-
-bool RpcAddress::isRecognizedType() const {
-    uint64_t allKnownOptions = RPC_WIRE_ADDRESS_OPTION_CREATED | RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
-    return (mRawAddr.get()->options & ~allKnownOptions) == 0;
-}
-
-RpcAddress RpcAddress::fromRawEmbedded(const RpcWireAddress* raw) {
-    RpcAddress addr;
-    memcpy(addr.mRawAddr.get(), raw, sizeof(RpcWireAddress));
-    return addr;
-}
-
-const RpcWireAddress& RpcAddress::viewRawEmbedded() const {
-    return *mRawAddr.get();
-}
-
-bool RpcAddress::operator<(const RpcAddress& rhs) const {
-    return std::memcmp(mRawAddr.get(), rhs.mRawAddr.get(), sizeof(RpcWireAddress)) < 0;
-}
-
-std::string RpcAddress::toString() const {
-    return base::HexString(mRawAddr.get(), sizeof(RpcWireAddress));
-}
-
-status_t RpcAddress::writeToParcel(Parcel* parcel) const {
-    return parcel->write(mRawAddr.get(), sizeof(RpcWireAddress));
-}
-
-status_t RpcAddress::readFromParcel(const Parcel& parcel) {
-    return parcel.read(mRawAddr.get(), sizeof(RpcWireAddress));
-}
-
-RpcAddress::~RpcAddress() {}
-RpcAddress::RpcAddress() : mRawAddr(std::make_shared<RpcWireAddress>()) {}
-
-} // namespace android
diff --git a/libs/binder/RpcCertificateUtils.cpp b/libs/binder/RpcCertificateUtils.cpp
new file mode 100644
index 0000000..d91736c
--- /dev/null
+++ b/libs/binder/RpcCertificateUtils.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcCertificateUtils"
+#include <log/log.h>
+
+#include <binder/RpcCertificateUtils.h>
+
+#include "Utils.h"
+
+namespace android {
+
+namespace {
+
+bssl::UniquePtr<X509> fromPem(const std::vector<uint8_t>& cert) {
+    if (cert.size() > std::numeric_limits<int>::max()) return nullptr;
+    bssl::UniquePtr<BIO> certBio(BIO_new_mem_buf(cert.data(), static_cast<int>(cert.size())));
+    return bssl::UniquePtr<X509>(PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr));
+}
+
+bssl::UniquePtr<X509> fromDer(const std::vector<uint8_t>& cert) {
+    if (cert.size() > std::numeric_limits<long>::max()) return nullptr;
+    const unsigned char* data = cert.data();
+    auto expectedEnd = data + cert.size();
+    bssl::UniquePtr<X509> ret(d2i_X509(nullptr, &data, static_cast<long>(cert.size())));
+    if (data != expectedEnd) {
+        ALOGE("%s: %td bytes remaining!", __PRETTY_FUNCTION__, expectedEnd - data);
+        return nullptr;
+    }
+    return ret;
+}
+
+} // namespace
+
+bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& cert,
+                                             RpcCertificateFormat format) {
+    switch (format) {
+        case RpcCertificateFormat::PEM:
+            return fromPem(cert);
+        case RpcCertificateFormat::DER:
+            return fromDer(cert);
+    }
+    LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
+}
+
+std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format) {
+    bssl::UniquePtr<BIO> certBio(BIO_new(BIO_s_mem()));
+    switch (format) {
+        case RpcCertificateFormat::PEM: {
+            TEST_AND_RETURN({}, PEM_write_bio_X509(certBio.get(), x509));
+        } break;
+        case RpcCertificateFormat::DER: {
+            TEST_AND_RETURN({}, i2d_X509_bio(certBio.get(), x509));
+        } break;
+        default: {
+            LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
+        }
+    }
+    const uint8_t* data;
+    size_t len;
+    TEST_AND_RETURN({}, BIO_mem_contents(certBio.get(), &data, &len));
+    return std::vector<uint8_t>(data, data + len);
+}
+
+} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index ad9ba96..5733993 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -23,6 +23,8 @@
 #include <thread>
 #include <vector>
 
+#include <android-base/file.h>
+#include <android-base/hex.h>
 #include <android-base/scopeguard.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
@@ -139,20 +141,11 @@
     return ret;
 }
 
-std::string RpcServer::getCertificate(CertificateFormat format) {
+std::vector<uint8_t> RpcServer::getCertificate(RpcCertificateFormat format) {
     std::lock_guard<std::mutex> _l(mLock);
     return mCtx->getCertificate(format);
 }
 
-status_t RpcServer::addTrustedPeerCertificate(CertificateFormat format, std::string_view cert) {
-    std::lock_guard<std::mutex> _l(mLock);
-    // Ensure that join thread is not running or shutdown trigger is not set up. In either case,
-    // it means there are child threads running. It is invalid to add trusted peer certificates
-    // after join thread and/or child threads are running to avoid race condition.
-    if (mJoinThreadRunning || mShutdownTrigger != nullptr) return INVALID_OPERATION;
-    return mCtx->addTrustedPeerCertificate(format, cert);
-}
-
 static void joinRpcServer(sp<RpcServer>&& thiz) {
     thiz->join();
 }
@@ -290,17 +283,29 @@
         }
     }
 
+    std::vector<uint8_t> sessionId;
+    if (status == OK) {
+        if (header.sessionIdSize > 0) {
+            sessionId.resize(header.sessionIdSize);
+            status = client->interruptableReadFully(server->mShutdownTrigger.get(),
+                                                    sessionId.data(), sessionId.size());
+            if (status != OK) {
+                ALOGE("Failed to read session ID for client connecting to RPC server: %s",
+                      statusToString(status).c_str());
+                // still need to cleanup before we can return
+            }
+        }
+    }
+
     bool incoming = false;
     uint32_t protocolVersion = 0;
-    RpcAddress sessionId = RpcAddress::zero();
     bool requestingNewSession = false;
 
     if (status == OK) {
         incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
         protocolVersion = std::min(header.version,
                                    server->mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION));
-        sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
-        requestingNewSession = sessionId.isZero();
+        requestingNewSession = sessionId.empty();
 
         if (requestingNewSession) {
             RpcNewSessionResponse response{
@@ -342,15 +347,26 @@
                 return;
             }
 
+            // Uniquely identify session at the application layer. Even if a
+            // client/server use the same certificates, if they create multiple
+            // sessions, we still want to distinguish between them.
+            constexpr size_t kSessionIdSize = 32;
+            sessionId.resize(kSessionIdSize);
             size_t tries = 0;
             do {
                 // don't block if there is some entropy issue
                 if (tries++ > 5) {
-                    ALOGE("Cannot find new address: %s", sessionId.toString().c_str());
+                    ALOGE("Cannot find new address: %s",
+                          base::HexString(sessionId.data(), sessionId.size()).c_str());
                     return;
                 }
 
-                sessionId = RpcAddress::random(true /*forServer*/);
+                base::unique_fd fd(TEMP_FAILURE_RETRY(
+                        open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)));
+                if (!base::ReadFully(fd, sessionId.data(), sessionId.size())) {
+                    ALOGE("Could not read from /dev/urandom to create session ID");
+                    return;
+                }
             } while (server->mSessions.end() != server->mSessions.find(sessionId));
 
             session = RpcSession::make();
@@ -370,7 +386,7 @@
             auto it = server->mSessions.find(sessionId);
             if (it == server->mSessions.end()) {
                 ALOGE("Cannot add thread, no record of session with ID %s",
-                      sessionId.toString().c_str());
+                      base::HexString(sessionId.data(), sessionId.size()).c_str());
                 return;
             }
             session = it->second;
@@ -432,16 +448,17 @@
 }
 
 void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) {
-    auto id = session->mId;
-    LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID");
-    LOG_RPC_DETAIL("Dropping session with address %s", id->toString().c_str());
+    const std::vector<uint8_t>& id = session->mId;
+    LOG_ALWAYS_FATAL_IF(id.empty(), "Server sessions must be initialized with ID");
+    LOG_RPC_DETAIL("Dropping session with address %s",
+                   base::HexString(id.data(), id.size()).c_str());
 
     std::lock_guard<std::mutex> _l(mLock);
-    auto it = mSessions.find(*id);
+    auto it = mSessions.find(id);
     LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %s",
-                        id->toString().c_str());
+                        base::HexString(id.data(), id.size()).c_str());
     LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %s",
-                        id->toString().c_str());
+                        base::HexString(id.data(), id.size()).c_str());
     (void)mSessions.erase(it);
 }
 
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index c57b749..38958c9 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -26,8 +26,10 @@
 
 #include <string_view>
 
+#include <android-base/hex.h>
 #include <android-base/macros.h>
 #include <android_runtime/vm.h>
+#include <binder/BpBinder.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcTransportRaw.h>
@@ -64,23 +66,12 @@
 
 sp<RpcSession> RpcSession::make() {
     // Default is without TLS.
-    return make(RpcTransportCtxFactoryRaw::make(), std::nullopt, std::nullopt);
+    return make(RpcTransportCtxFactoryRaw::make());
 }
 
-sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory,
-                                std::optional<CertificateFormat> serverCertificateFormat,
-                                std::optional<std::string> serverCertificate) {
+sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
     auto ctx = rpcTransportCtxFactory->newClientCtx();
     if (ctx == nullptr) return nullptr;
-    LOG_ALWAYS_FATAL_IF(serverCertificateFormat.has_value() != serverCertificate.has_value());
-    if (serverCertificateFormat.has_value() && serverCertificate.has_value()) {
-        status_t status =
-                ctx->addTrustedPeerCertificate(*serverCertificateFormat, *serverCertificate);
-        if (status != OK) {
-            ALOGE("Cannot add trusted server certificate: %s", statusToString(status).c_str());
-            return nullptr;
-        }
-    }
     return sp<RpcSession>::make(std::move(ctx));
 }
 
@@ -143,7 +134,7 @@
 }
 
 status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
-    return setupClient([&](const RpcAddress& sessionId, bool incoming) -> status_t {
+    return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
         // std::move'd from fd becomes -1 (!ok())
         if (!fd.ok()) {
             fd = request();
@@ -201,7 +192,7 @@
 
     if (wait) {
         LOG_ALWAYS_FATAL_IF(mShutdownListener == nullptr, "Shutdown listener not installed");
-        mShutdownListener->waitForShutdown(_l);
+        mShutdownListener->waitForShutdown(_l, sp<RpcSession>::fromExisting(this));
 
         LOG_ALWAYS_FATAL_IF(!mThreads.empty(), "Shutdown failed");
     }
@@ -225,7 +216,11 @@
                              sp<RpcSession>::fromExisting(this), reply, flags);
 }
 
-status_t RpcSession::sendDecStrong(const RpcAddress& address) {
+status_t RpcSession::sendDecStrong(const BpBinder* binder) {
+    return sendDecStrong(binder->getPrivateAccessor().rpcAddress());
+}
+
+status_t RpcSession::sendDecStrong(uint64_t address) {
     ExclusiveConnection connection;
     status_t status = ExclusiveConnection::find(sp<RpcSession>::fromExisting(this),
                                                 ConnectionUse::CLIENT_REFCOUNT, &connection);
@@ -244,29 +239,30 @@
                                                 ConnectionUse::CLIENT, &connection);
     if (status != OK) return status;
 
-    mId = RpcAddress::zero();
-    status = state()->getSessionId(connection.get(), sp<RpcSession>::fromExisting(this),
-                                   &mId.value());
+    status = state()->getSessionId(connection.get(), sp<RpcSession>::fromExisting(this), &mId);
     if (status != OK) return status;
 
-    LOG_RPC_DETAIL("RpcSession %p has id %s", this, mId->toString().c_str());
+    LOG_RPC_DETAIL("RpcSession %p has id %s", this,
+                   base::HexString(mId.data(), mId.size()).c_str());
     return OK;
 }
 
 void RpcSession::WaitForShutdownListener::onSessionAllIncomingThreadsEnded(
         const sp<RpcSession>& session) {
     (void)session;
-    mShutdown = true;
 }
 
 void RpcSession::WaitForShutdownListener::onSessionIncomingThreadEnded() {
     mCv.notify_all();
 }
 
-void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock) {
-    while (!mShutdown) {
+void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock,
+                                                          const sp<RpcSession>& session) {
+    while (session->mIncomingConnections.size() > 0) {
         if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) {
-            ALOGE("Waiting for RpcSession to shut down (1s w/o progress).");
+            ALOGE("Waiting for RpcSession to shut down (1s w/o progress): %zu incoming connections "
+                  "still.",
+                  session->mIncomingConnections.size());
         }
     }
 }
@@ -408,8 +404,8 @@
     return server;
 }
 
-status_t RpcSession::setupClient(
-        const std::function<status_t(const RpcAddress& sessionId, bool incoming)>& connectAndInit) {
+status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId,
+                                                              bool incoming)>& connectAndInit) {
     {
         std::lock_guard<std::mutex> _l(mMutex);
         LOG_ALWAYS_FATAL_IF(mOutgoingConnections.size() != 0,
@@ -418,8 +414,7 @@
     }
     if (auto status = initShutdownTrigger(); status != OK) return status;
 
-    if (status_t status = connectAndInit(RpcAddress::zero(), false /*incoming*/); status != OK)
-        return status;
+    if (status_t status = connectAndInit({}, false /*incoming*/); status != OK) return status;
 
     {
         ExclusiveConnection connection;
@@ -460,26 +455,25 @@
 
     // we've already setup one client
     for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
-        if (status_t status = connectAndInit(mId.value(), false /*incoming*/); status != OK)
-            return status;
+        if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
     }
 
     for (size_t i = 0; i < mMaxThreads; i++) {
-        if (status_t status = connectAndInit(mId.value(), true /*incoming*/); status != OK)
-            return status;
+        if (status_t status = connectAndInit(mId, true /*incoming*/); status != OK) return status;
     }
 
     return OK;
 }
 
 status_t RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
-    return setupClient([&](const RpcAddress& sessionId, bool incoming) {
+    return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) {
         return setupOneSocketConnection(addr, sessionId, incoming);
     });
 }
 
 status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
-                                              const RpcAddress& sessionId, bool incoming) {
+                                              const std::vector<uint8_t>& sessionId,
+                                              bool incoming) {
     for (size_t tries = 0; tries < 5; tries++) {
         if (tries > 0) usleep(10000);
 
@@ -537,7 +531,7 @@
     return UNKNOWN_ERROR;
 }
 
-status_t RpcSession::initAndAddConnection(unique_fd fd, const RpcAddress& sessionId,
+status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_t>& sessionId,
                                           bool incoming) {
     LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr);
     auto server = mCtx->newTransport(std::move(fd), mShutdownTrigger.get());
@@ -548,13 +542,20 @@
 
     LOG_RPC_DETAIL("Socket at client with RpcTransport %p", server.get());
 
+    if (sessionId.size() > std::numeric_limits<uint16_t>::max()) {
+        ALOGE("Session ID too big %zu", sessionId.size());
+        return BAD_VALUE;
+    }
+
     RpcConnectionHeader header{
             .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
             .options = 0,
+            .sessionIdSize = static_cast<uint16_t>(sessionId.size()),
     };
-    memcpy(&header.sessionId, &sessionId.viewRawEmbedded(), sizeof(RpcWireAddress));
 
-    if (incoming) header.options |= RPC_CONNECTION_OPTION_INCOMING;
+    if (incoming) {
+        header.options |= RPC_CONNECTION_OPTION_INCOMING;
+    }
 
     auto sendHeaderStatus =
             server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header));
@@ -564,6 +565,18 @@
         return sendHeaderStatus;
     }
 
+    if (sessionId.size() > 0) {
+        auto sendSessionIdStatus =
+                server->interruptableWriteFully(mShutdownTrigger.get(), sessionId.data(),
+                                                sessionId.size());
+        if (sendSessionIdStatus != OK) {
+            ALOGE("Could not write session ID ('%s') to socket: %s",
+                  base::HexString(sessionId.data(), sessionId.size()).c_str(),
+                  statusToString(sendSessionIdStatus).c_str());
+            return sendSessionIdStatus;
+        }
+    }
+
     LOG_RPC_DETAIL("Socket at client: header sent");
 
     if (incoming) {
@@ -636,7 +649,7 @@
 }
 
 bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListener>& eventListener,
-                              const RpcAddress& sessionId) {
+                              const std::vector<uint8_t>& sessionId) {
     LOG_ALWAYS_FATAL_IF(mForServer != nullptr);
     LOG_ALWAYS_FATAL_IF(server == nullptr);
     LOG_ALWAYS_FATAL_IF(mEventListener != nullptr);
@@ -697,7 +710,7 @@
     return false;
 }
 
-std::string RpcSession::getCertificate(CertificateFormat format) {
+std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) {
     return mCtx->getCertificate(format);
 }
 
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index b58f1b3..11a083a 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -52,11 +52,11 @@
 RpcState::~RpcState() {}
 
 status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
-                                   RpcAddress* outAddress) {
+                                   uint64_t* outAddress) {
     bool isRemote = binder->remoteBinder();
     bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder();
 
-    if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcSession() != session) {
+    if (isRpc && binder->remoteBinder()->getPrivateAccessor().rpcSession() != session) {
         // We need to be able to send instructions over the socket for how to
         // connect to a different server, and we also need to let the host
         // process know that this is happening.
@@ -84,12 +84,10 @@
     for (auto& [addr, node] : mNodeForAddress) {
         if (binder == node.binder) {
             if (isRpc) {
-                const RpcAddress& actualAddr =
-                        binder->remoteBinder()->getPrivateAccessorForId().rpcAddress();
-                // TODO(b/182939933): this is only checking integrity of data structure
-                // a different data structure doesn't need this
-                LOG_ALWAYS_FATAL_IF(addr < actualAddr, "Address mismatch");
-                LOG_ALWAYS_FATAL_IF(actualAddr < addr, "Address mismatch");
+                // check integrity of data structure
+                uint64_t actualAddr = binder->remoteBinder()->getPrivateAccessor().rpcAddress();
+                LOG_ALWAYS_FATAL_IF(addr != actualAddr, "Address mismatch %" PRIu64 " vs %" PRIu64,
+                                    addr, actualAddr);
             }
             node.timesSent++;
             node.sentRef = binder; // might already be set
@@ -101,8 +99,29 @@
 
     bool forServer = session->server() != nullptr;
 
-    for (size_t tries = 0; tries < 5; tries++) {
-        auto&& [it, inserted] = mNodeForAddress.insert({RpcAddress::random(forServer),
+    // arbitrary limit for maximum number of nodes in a process (otherwise we
+    // might run out of addresses)
+    if (mNodeForAddress.size() > 100000) {
+        return NO_MEMORY;
+    }
+
+    while (true) {
+        RpcWireAddress address{
+                .options = RPC_WIRE_ADDRESS_OPTION_CREATED,
+                .address = mNextId,
+        };
+        if (forServer) {
+            address.options |= RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
+        }
+
+        // avoid ubsan abort
+        if (mNextId >= std::numeric_limits<uint32_t>::max()) {
+            mNextId = 0;
+        } else {
+            mNextId++;
+        }
+
+        auto&& [it, inserted] = mNodeForAddress.insert({RpcWireAddress::toRaw(address),
                                                         BinderNode{
                                                                 .binder = binder,
                                                                 .timesSent = 1,
@@ -112,18 +131,10 @@
             *outAddress = it->first;
             return OK;
         }
-
-        // well, we don't have visibility into the header here, but still
-        static_assert(sizeof(RpcWireAddress) == 40, "this log needs updating");
-        ALOGW("2**256 is 1e77. If you see this log, you probably have some entropy issue, or maybe "
-              "you witness something incredible!");
     }
-
-    ALOGE("Unable to create an address in order to send out %p", binder.get());
-    return WOULD_BLOCK;
 }
 
-status_t RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address,
+status_t RpcState::onBinderEntering(const sp<RpcSession>& session, uint64_t address,
                                     sp<IBinder>* out) {
     // ensure that: if we want to use addresses for something else in the future (for
     //   instance, allowing transitive binder sends), that we don't accidentally
@@ -133,8 +144,11 @@
     //   if we communicate with a binder, it could always be proxying
     //   information. However, we want to make sure that isn't done on accident
     //   by a client.
-    if (!address.isRecognizedType()) {
-        ALOGE("Address is of an unknown type, rejecting: %s", address.toString().c_str());
+    RpcWireAddress addr = RpcWireAddress::fromRaw(address);
+    constexpr uint32_t kKnownOptions =
+            RPC_WIRE_ADDRESS_OPTION_CREATED | RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
+    if (addr.options & ~kKnownOptions) {
+        ALOGE("Address is of an unknown type, rejecting: %" PRIu64, address);
         return BAD_VALUE;
     }
 
@@ -159,9 +173,9 @@
 
     // we don't know about this binder, so the other side of the connection
     // should have created it.
-    if (address.isForServer() == !!session->server()) {
-        ALOGE("Server received unrecognized address which we should own the creation of %s.",
-              address.toString().c_str());
+    if ((addr.options & RPC_WIRE_ADDRESS_OPTION_FOR_SERVER) == !!session->server()) {
+        ALOGE("Server received unrecognized address which we should own the creation of %" PRIu64,
+              address);
         return BAD_VALUE;
     }
 
@@ -170,7 +184,7 @@
 
     // Currently, all binders are assumed to be part of the same session (no
     // device global binders in the RPC world).
-    it->second.binder = *out = BpBinder::create(session, it->first);
+    it->second.binder = *out = BpBinder::PrivateAccessor::create(session, it->first);
     it->second.timesRecd = 1;
     return OK;
 }
@@ -241,9 +255,8 @@
             desc = "(null)";
         }
 
-        ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a:%s type:%s",
-              node.binder.unsafe_get(), node.timesSent, node.timesRecd, address.toString().c_str(),
-              desc);
+        ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a: %" PRIu64 " type: %s",
+              node.binder.unsafe_get(), node.timesSent, node.timesRecd, address, desc);
     }
     ALOGE("END DUMP OF RpcState");
 }
@@ -360,8 +373,8 @@
     data.markForRpc(session);
     Parcel reply;
 
-    status_t status = transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT,
-                                      data, session, &reply, 0);
+    status_t status =
+            transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_ROOT, data, session, &reply, 0);
     if (status != OK) {
         ALOGE("Error getting root object: %s", statusToString(status).c_str());
         return nullptr;
@@ -376,9 +389,8 @@
     data.markForRpc(session);
     Parcel reply;
 
-    status_t status =
-            transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS,
-                            data, session, &reply, 0);
+    status_t status = transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data,
+                                      session, &reply, 0);
     if (status != OK) {
         ALOGE("Error getting max threads: %s", statusToString(status).c_str());
         return status;
@@ -397,20 +409,19 @@
 }
 
 status_t RpcState::getSessionId(const sp<RpcSession::RpcConnection>& connection,
-                                const sp<RpcSession>& session, RpcAddress* sessionIdOut) {
+                                const sp<RpcSession>& session, std::vector<uint8_t>* sessionIdOut) {
     Parcel data;
     data.markForRpc(session);
     Parcel reply;
 
-    status_t status =
-            transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID,
-                            data, session, &reply, 0);
+    status_t status = transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data,
+                                      session, &reply, 0);
     if (status != OK) {
         ALOGE("Error getting session ID: %s", statusToString(status).c_str());
         return status;
     }
 
-    return sessionIdOut->readFromParcel(reply);
+    return reply.readByteVector(sessionIdOut);
 }
 
 status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection,
@@ -426,26 +437,26 @@
         return BAD_TYPE;
     }
 
-    RpcAddress address = RpcAddress::zero();
+    uint64_t address;
     if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status;
 
     return transactAddress(connection, address, code, data, session, reply, flags);
 }
 
 status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connection,
-                                   const RpcAddress& address, uint32_t code, const Parcel& data,
+                                   uint64_t address, uint32_t code, const Parcel& data,
                                    const sp<RpcSession>& session, Parcel* reply, uint32_t flags) {
     LOG_ALWAYS_FATAL_IF(!data.isForRpc());
     LOG_ALWAYS_FATAL_IF(data.objectsCount() != 0);
 
     uint64_t asyncNumber = 0;
 
-    if (!address.isZero()) {
+    if (address != 0) {
         std::unique_lock<std::mutex> _l(mNodeMutex);
         if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
         auto it = mNodeForAddress.find(address);
-        LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending transact on unknown address %s",
-                            address.toString().c_str());
+        LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
+                            "Sending transact on unknown address %" PRIu64, address);
 
         if (flags & IBinder::FLAG_ONEWAY) {
             asyncNumber = it->second.asyncNumber;
@@ -466,8 +477,9 @@
             .command = RPC_COMMAND_TRANSACT,
             .bodySize = static_cast<uint32_t>(sizeof(RpcWireTransaction) + data.dataSize()),
     };
+
     RpcWireTransaction transaction{
-            .address = address.viewRawEmbedded(),
+            .address = RpcWireAddress::fromRaw(address),
             .code = code,
             .flags = flags,
             .asyncNumber = asyncNumber,
@@ -557,15 +569,14 @@
 }
 
 status_t RpcState::sendDecStrong(const sp<RpcSession::RpcConnection>& connection,
-                                 const sp<RpcSession>& session, const RpcAddress& addr) {
+                                 const sp<RpcSession>& session, uint64_t addr) {
     {
         std::lock_guard<std::mutex> _l(mNodeMutex);
         if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
         auto it = mNodeForAddress.find(addr);
-        LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending dec strong on unknown address %s",
-                            addr.toString().c_str());
-        LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %s",
-                            addr.toString().c_str());
+        LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
+                            "Sending dec strong on unknown address %" PRIu64, addr);
+        LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %" PRIu64, addr);
 
         it->second.timesRecd--;
         LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(it),
@@ -579,8 +590,7 @@
     if (status_t status = rpcSend(connection, session, "dec ref header", &cmd, sizeof(cmd));
         status != OK)
         return status;
-    if (status_t status = rpcSend(connection, session, "dec ref body", &addr.viewRawEmbedded(),
-                                  sizeof(RpcWireAddress));
+    if (status_t status = rpcSend(connection, session, "dec ref body", &addr, sizeof(addr));
         status != OK)
         return status;
     return OK;
@@ -685,14 +695,12 @@
     }
     RpcWireTransaction* transaction = reinterpret_cast<RpcWireTransaction*>(transactionData.data());
 
-    // TODO(b/182939933): heap allocation just for lookup in mNodeForAddress,
-    // maybe add an RpcAddress 'view' if the type remains 'heavy'
-    auto addr = RpcAddress::fromRawEmbedded(&transaction->address);
+    uint64_t addr = RpcWireAddress::toRaw(transaction->address);
     bool oneway = transaction->flags & IBinder::FLAG_ONEWAY;
 
     status_t replyStatus = OK;
     sp<IBinder> target;
-    if (!addr.isZero()) {
+    if (addr != 0) {
         if (!targetRef) {
             replyStatus = onBinderEntering(session, addr, &target);
         } else {
@@ -708,21 +716,21 @@
             // (any binder which is being transacted on should be holding a
             // strong ref count), so in either case, terminating the
             // session.
-            ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
-                  addr.toString().c_str());
+            ALOGE("While transacting, binder has been deleted at address %" PRIu64 ". Terminating!",
+                  addr);
             (void)session->shutdownAndWait(false);
             replyStatus = BAD_VALUE;
         } else if (target->localBinder() == nullptr) {
-            ALOGE("Unknown binder address or non-local binder, not address %s. Terminating!",
-                  addr.toString().c_str());
+            ALOGE("Unknown binder address or non-local binder, not address %" PRIu64
+                  ". Terminating!",
+                  addr);
             (void)session->shutdownAndWait(false);
             replyStatus = BAD_VALUE;
         } else if (oneway) {
             std::unique_lock<std::mutex> _l(mNodeMutex);
             auto it = mNodeForAddress.find(addr);
             if (it->second.binder.promote() != target) {
-                ALOGE("Binder became invalid during transaction. Bad client? %s",
-                      addr.toString().c_str());
+                ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr);
                 replyStatus = BAD_VALUE;
             } else if (transaction->asyncNumber != it->second.asyncNumber) {
                 // we need to process some other asynchronous transaction
@@ -734,8 +742,8 @@
                 });
 
                 size_t numPending = it->second.asyncTodo.size();
-                LOG_RPC_DETAIL("Enqueuing %" PRId64 " on %s (%zu pending)",
-                               transaction->asyncNumber, addr.toString().c_str(), numPending);
+                LOG_RPC_DETAIL("Enqueuing %" PRIu64 " on %" PRIu64 " (%zu pending)",
+                               transaction->asyncNumber, addr, numPending);
 
                 constexpr size_t kArbitraryOnewayCallTerminateLevel = 10000;
                 constexpr size_t kArbitraryOnewayCallWarnLevel = 1000;
@@ -792,7 +800,7 @@
                     // for client connections, this should always report the value
                     // originally returned from the server, so this is asserting
                     // that it exists
-                    replyStatus = session->mId.value().writeToParcel(&reply);
+                    replyStatus = reply.writeByteVector(session->mId);
                     break;
                 }
                 default: {
@@ -820,8 +828,8 @@
             ALOGW("Oneway call failed with error: %d", replyStatus);
         }
 
-        LOG_RPC_DETAIL("Processed async transaction %" PRId64 " on %s", transaction->asyncNumber,
-                       addr.toString().c_str());
+        LOG_RPC_DETAIL("Processed async transaction %" PRIu64 " on %" PRIu64,
+                       transaction->asyncNumber, addr);
 
         // Check to see if there is another asynchronous transaction to process.
         // This behavior differs from binder behavior, since in the binder
@@ -847,8 +855,8 @@
 
             if (it->second.asyncTodo.size() == 0) return OK;
             if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
-                LOG_RPC_DETAIL("Found next async transaction %" PRId64 " on %s",
-                               it->second.asyncNumber, addr.toString().c_str());
+                LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64,
+                               it->second.asyncNumber, addr);
 
                 // justification for const_cast (consider avoiding priority_queue):
                 // - AsyncTodo operator< doesn't depend on 'data' or 'ref' objects
@@ -904,7 +912,7 @@
         status != OK)
         return status;
 
-    if (command.bodySize < sizeof(RpcWireAddress)) {
+    if (command.bodySize != sizeof(RpcWireAddress)) {
         ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireAddress. Terminating!",
               sizeof(RpcWireAddress), command.bodySize);
         (void)session->shutdownAndWait(false);
@@ -912,31 +920,32 @@
     }
     RpcWireAddress* address = reinterpret_cast<RpcWireAddress*>(commandData.data());
 
-    // TODO(b/182939933): heap allocation just for lookup
-    auto addr = RpcAddress::fromRawEmbedded(address);
+    uint64_t addr = RpcWireAddress::toRaw(*address);
+
     std::unique_lock<std::mutex> _l(mNodeMutex);
     auto it = mNodeForAddress.find(addr);
     if (it == mNodeForAddress.end()) {
-        ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str());
+        ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr);
         return OK;
     }
 
     sp<IBinder> target = it->second.binder.promote();
     if (target == nullptr) {
-        ALOGE("While requesting dec strong, binder has been deleted at address %s. Terminating!",
-              addr.toString().c_str());
+        ALOGE("While requesting dec strong, binder has been deleted at address %" PRIu64
+              ". Terminating!",
+              addr);
         _l.unlock();
         (void)session->shutdownAndWait(false);
         return BAD_VALUE;
     }
 
     if (it->second.timesSent == 0) {
-        ALOGE("No record of sending binder, but requested decStrong: %s", addr.toString().c_str());
+        ALOGE("No record of sending binder, but requested decStrong: %" PRIu64, addr);
         return OK;
     }
 
-    LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %s",
-                        addr.toString().c_str());
+    LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %" PRIu64,
+                        addr);
 
     it->second.timesSent--;
     sp<IBinder> tempHold = tryEraseNode(it);
@@ -946,7 +955,7 @@
     return OK;
 }
 
-sp<IBinder> RpcState::tryEraseNode(std::map<RpcAddress, BinderNode>::iterator& it) {
+sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) {
     sp<IBinder> ref;
 
     if (it->second.timesSent == 0) {
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 1446eec..dcfb569 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -73,17 +73,17 @@
     status_t getMaxThreads(const sp<RpcSession::RpcConnection>& connection,
                            const sp<RpcSession>& session, size_t* maxThreadsOut);
     status_t getSessionId(const sp<RpcSession::RpcConnection>& connection,
-                          const sp<RpcSession>& session, RpcAddress* sessionIdOut);
+                          const sp<RpcSession>& session, std::vector<uint8_t>* sessionIdOut);
 
     [[nodiscard]] status_t transact(const sp<RpcSession::RpcConnection>& connection,
                                     const sp<IBinder>& address, uint32_t code, const Parcel& data,
                                     const sp<RpcSession>& session, Parcel* reply, uint32_t flags);
     [[nodiscard]] status_t transactAddress(const sp<RpcSession::RpcConnection>& connection,
-                                           const RpcAddress& address, uint32_t code,
-                                           const Parcel& data, const sp<RpcSession>& session,
-                                           Parcel* reply, uint32_t flags);
+                                           uint64_t address, uint32_t code, const Parcel& data,
+                                           const sp<RpcSession>& session, Parcel* reply,
+                                           uint32_t flags);
     [[nodiscard]] status_t sendDecStrong(const sp<RpcSession::RpcConnection>& connection,
-                                         const sp<RpcSession>& session, const RpcAddress& address);
+                                         const sp<RpcSession>& session, uint64_t address);
 
     enum class CommandType {
         ANY,
@@ -99,15 +99,15 @@
      * ownership to the outgoing binder.
      */
     [[nodiscard]] status_t onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
-                                           RpcAddress* outAddress);
+                                           uint64_t* outAddress);
 
     /**
      * Called by Parcel for incoming binders. This either returns the refcount
      * to the process, if this process already has one, or it takes ownership of
      * that refcount
      */
-    [[nodiscard]] status_t onBinderEntering(const sp<RpcSession>& session,
-                                            const RpcAddress& address, sp<IBinder>* out);
+    [[nodiscard]] status_t onBinderEntering(const sp<RpcSession>& session, uint64_t address,
+                                            sp<IBinder>* out);
 
     size_t countBinders();
     void dump();
@@ -221,15 +221,16 @@
     // happens, and there is a strong reference to the binder kept by
     // binderNode, this returns that strong reference, so that it can be
     // dropped after any locks are removed.
-    sp<IBinder> tryEraseNode(std::map<RpcAddress, BinderNode>::iterator& it);
+    sp<IBinder> tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it);
     // true - success
     // false - session shutdown, halt
     [[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node);
 
     std::mutex mNodeMutex;
     bool mTerminated = false;
+    uint32_t mNextId = 0;
     // binders known by both sides of a session
-    std::map<RpcAddress, BinderNode> mNodeForAddress;
+    std::map<uint64_t, BinderNode> mNodeForAddress;
 };
 
 } // namespace android
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 930df12..c012df8 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -111,8 +111,7 @@
     std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, FdTrigger*) const {
         return std::make_unique<RpcTransportRaw>(std::move(fd));
     }
-    std::string getCertificate(CertificateFormat) const override { return {}; }
-    status_t addTrustedPeerCertificate(CertificateFormat, std::string_view) override { return OK; }
+    std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
 
 } // namespace
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index e6cb04e..d40cfc8 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -22,10 +22,12 @@
 #include <openssl/bn.h>
 #include <openssl/ssl.h>
 
+#include <binder/RpcCertificateUtils.h>
 #include <binder/RpcTransportTls.h>
 
 #include "FdTrigger.h"
 #include "RpcState.h"
+#include "Utils.h"
 
 #define SHOULD_LOG_TLS_DETAIL false
 
@@ -35,14 +37,6 @@
 #define LOG_TLS_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
 #endif
 
-#define TEST_AND_RETURN(value, expr)            \
-    do {                                        \
-        if (!(expr)) {                          \
-            ALOGE("Failed to call: %s", #expr); \
-            return value;                       \
-        }                                       \
-    } while (0)
-
 using android::base::ErrnoError;
 using android::base::Error;
 using android::base::Result;
@@ -453,31 +447,54 @@
 public:
     template <typename Impl,
               typename = std::enable_if_t<std::is_base_of_v<RpcTransportCtxTls, Impl>>>
-    static std::unique_ptr<RpcTransportCtxTls> create();
+    static std::unique_ptr<RpcTransportCtxTls> create(
+            std::shared_ptr<RpcCertificateVerifier> verifier);
     std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd,
                                                FdTrigger* fdTrigger) const override;
-    std::string getCertificate(CertificateFormat) const override;
-    status_t addTrustedPeerCertificate(CertificateFormat, std::string_view cert) override;
+    std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override;
 
 protected:
+    static ssl_verify_result_t sslCustomVerify(SSL* ssl, uint8_t* outAlert);
     virtual void preHandshake(Ssl* ssl) const = 0;
     bssl::UniquePtr<SSL_CTX> mCtx;
+    std::shared_ptr<RpcCertificateVerifier> mCertVerifier;
 };
 
-std::string RpcTransportCtxTls::getCertificate(CertificateFormat) const {
-    // TODO(b/195166979): return certificate here
-    return {};
+std::vector<uint8_t> RpcTransportCtxTls::getCertificate(RpcCertificateFormat format) const {
+    X509* x509 = SSL_CTX_get0_certificate(mCtx.get()); // does not own
+    return serializeCertificate(x509, format);
 }
 
-status_t RpcTransportCtxTls::addTrustedPeerCertificate(CertificateFormat, std::string_view) {
-    // TODO(b/195166979): set certificate here
-    return OK;
+// Verify by comparing the leaf of peer certificate with every certificate in
+// mTrustedPeerCertificates. Does not support certificate chains.
+ssl_verify_result_t RpcTransportCtxTls::sslCustomVerify(SSL* ssl, uint8_t* outAlert) {
+    LOG_ALWAYS_FATAL_IF(outAlert == nullptr);
+    const char* logPrefix = SSL_is_server(ssl) ? "Server" : "Client";
+
+    bssl::UniquePtr<X509> peerCert(SSL_get_peer_certificate(ssl)); // Does not set error queue
+    LOG_ALWAYS_FATAL_IF(peerCert == nullptr,
+                        "%s: libssl should not ask to verify non-existing cert", logPrefix);
+
+    auto ctx = SSL_get_SSL_CTX(ssl); // Does not set error queue
+    LOG_ALWAYS_FATAL_IF(ctx == nullptr);
+    // void* -> RpcTransportCtxTls*
+    auto rpcTransportCtxTls = reinterpret_cast<RpcTransportCtxTls*>(SSL_CTX_get_app_data(ctx));
+    LOG_ALWAYS_FATAL_IF(rpcTransportCtxTls == nullptr);
+
+    status_t verifyStatus = rpcTransportCtxTls->mCertVerifier->verify(peerCert.get(), outAlert);
+    if (verifyStatus == OK) {
+        return ssl_verify_ok;
+    }
+    LOG_TLS_DETAIL("%s: Failed to verify client: status = %s, alert = %s", logPrefix,
+                   statusToString(verifyStatus).c_str(), SSL_alert_desc_string_long(*outAlert));
+    return ssl_verify_invalid;
 }
 
 // Common implementation for creating server and client contexts. The child class, |Impl|, is
 // provided as a template argument so that this function can initialize an |Impl| object.
 template <typename Impl, typename>
-std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create() {
+std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create(
+        std::shared_ptr<RpcCertificateVerifier> verifier) {
     bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
     TEST_AND_RETURN(nullptr, ctx != nullptr);
 
@@ -488,10 +505,10 @@
     TEST_AND_RETURN(nullptr, SSL_CTX_use_PrivateKey(ctx.get(), evp_pkey.get()));
     TEST_AND_RETURN(nullptr, SSL_CTX_use_certificate(ctx.get(), cert.get()));
 
-    // TODO(b/195166979): peer should send certificate in a different channel, and this class
-    //  should verify it here.
-    SSL_CTX_set_custom_verify(ctx.get(), SSL_VERIFY_PEER,
-                              [](SSL*, uint8_t*) -> ssl_verify_result_t { return ssl_verify_ok; });
+    // Enable two-way authentication by setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT on server.
+    // Client ignores SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag.
+    SSL_CTX_set_custom_verify(ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+                              sslCustomVerify);
 
     // Require at least TLS 1.3
     TEST_AND_RETURN(nullptr, SSL_CTX_set_min_proto_version(ctx.get(), TLS1_3_VERSION));
@@ -501,7 +518,10 @@
     }
 
     auto ret = std::make_unique<Impl>();
+    // RpcTransportCtxTls* -> void*
+    TEST_AND_RETURN(nullptr, SSL_CTX_set_app_data(ctx.get(), reinterpret_cast<void*>(ret.get())));
     ret->mCtx = std::move(ctx);
+    ret->mCertVerifier = std::move(verifier);
     return ret;
 }
 
@@ -533,19 +553,25 @@
 } // namespace
 
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newServerCtx() const {
-    return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>();
+    return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(mCertVerifier);
 }
 
 std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newClientCtx() const {
-    return android::RpcTransportCtxTls::create<RpcTransportCtxTlsClient>();
+    return android::RpcTransportCtxTls::create<RpcTransportCtxTlsClient>(mCertVerifier);
 }
 
 const char* RpcTransportCtxFactoryTls::toCString() const {
     return "tls";
 }
 
-std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTls::make() {
-    return std::unique_ptr<RpcTransportCtxFactoryTls>(new RpcTransportCtxFactoryTls());
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTls::make(
+        std::shared_ptr<RpcCertificateVerifier> verifier) {
+    if (verifier == nullptr) {
+        ALOGE("%s: Must provide a certificate verifier", __PRETTY_FUNCTION__);
+        return nullptr;
+    }
+    return std::unique_ptr<RpcTransportCtxFactoryTls>(
+            new RpcTransportCtxFactoryTls(std::move(verifier)));
 }
 
 } // namespace android
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 067c4ad..a87aa07 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -20,18 +20,23 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic error "-Wpadded"
 
-enum : uint8_t {
-    RPC_CONNECTION_OPTION_INCOMING = 0x1, // default is outgoing
-};
+constexpr uint8_t RPC_CONNECTION_OPTION_INCOMING = 0x1; // default is outgoing
 
-constexpr uint64_t RPC_WIRE_ADDRESS_OPTION_CREATED = 1 << 0; // distinguish from '0' address
-constexpr uint64_t RPC_WIRE_ADDRESS_OPTION_FOR_SERVER = 1 << 1;
+constexpr uint32_t RPC_WIRE_ADDRESS_OPTION_CREATED = 1 << 0; // distinguish from '0' address
+constexpr uint32_t RPC_WIRE_ADDRESS_OPTION_FOR_SERVER = 1 << 1;
 
 struct RpcWireAddress {
-    uint64_t options;
-    uint8_t address[32];
+    uint32_t options;
+    uint32_t address;
+
+    static inline RpcWireAddress fromRaw(uint64_t raw) {
+        return *reinterpret_cast<RpcWireAddress*>(&raw);
+    }
+    static inline uint64_t toRaw(RpcWireAddress addr) {
+        return *reinterpret_cast<uint64_t*>(&addr);
+    }
 };
-static_assert(sizeof(RpcWireAddress) == 40);
+static_assert(sizeof(RpcWireAddress) == sizeof(uint64_t));
 
 /**
  * This is sent to an RpcServer in order to request a new connection is created,
@@ -39,12 +44,13 @@
  */
 struct RpcConnectionHeader {
     uint32_t version; // maximum supported by caller
-    uint8_t reserver0[4];
-    RpcWireAddress sessionId;
     uint8_t options;
-    uint8_t reserved1[7];
+    uint8_t reservered[9];
+    // Follows is sessionIdSize bytes.
+    // if size is 0, this is requesting a new session.
+    uint16_t sessionIdSize;
 };
-static_assert(sizeof(RpcConnectionHeader) == 56);
+static_assert(sizeof(RpcConnectionHeader) == 16);
 
 /**
  * In response to an RpcConnectionHeader which corresponds to a new session,
@@ -122,7 +128,7 @@
 
     uint8_t data[];
 };
-static_assert(sizeof(RpcWireTransaction) == 72);
+static_assert(sizeof(RpcWireTransaction) == 40);
 
 struct RpcWireReply {
     int32_t status; // transact return
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index 1e383da..ff2fad8 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -19,6 +19,15 @@
 
 #include <android-base/result.h>
 #include <android-base/unique_fd.h>
+#include <log/log.h>
+
+#define TEST_AND_RETURN(value, expr)            \
+    do {                                        \
+        if (!(expr)) {                          \
+            ALOGE("Failed to call: %s", #expr); \
+            return value;                       \
+        }                                       \
+    } while (0)
 
 namespace android {
 
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 9dea3b4..793795e 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -74,6 +74,8 @@
     //
     // Indicates whether the process has received any sync calls since last
     // freeze (cleared at freeze/unfreeze)
+    // bit 0: received sync transaction after being frozen
+    // bit 1: new pending sync transaction during freezing
     //
     __u32 sync_recv;
     //
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index b58cb7e..c0454b6 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
 #include <utils/KeyedVector.h>
 #include <utils/Mutex.h>
 #include <utils/threads.h>
@@ -40,9 +39,6 @@
 class BpBinder : public IBinder
 {
 public:
-    static sp<BpBinder> create(int32_t handle);
-    static sp<BpBinder> create(const sp<RpcSession>& session, const RpcAddress& address);
-
     /**
      * Return value:
      * true - this is associated with a socket RpcSession
@@ -117,42 +113,49 @@
         KeyedVector<const void*, entry_t> mObjects;
     };
 
-    class PrivateAccessorForId {
+    class PrivateAccessor {
     private:
         friend class BpBinder;
         friend class ::android::Parcel;
         friend class ::android::ProcessState;
+        friend class ::android::RpcSession;
         friend class ::android::RpcState;
-        explicit PrivateAccessorForId(const BpBinder* binder) : mBinder(binder) {}
+        explicit PrivateAccessor(const BpBinder* binder) : mBinder(binder) {}
+
+        static sp<BpBinder> create(int32_t handle) { return BpBinder::create(handle); }
+        static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address) {
+            return BpBinder::create(session, address);
+        }
 
         // valid if !isRpcBinder
         int32_t binderHandle() const { return mBinder->binderHandle(); }
 
         // valid if isRpcBinder
-        const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); }
+        uint64_t rpcAddress() const { return mBinder->rpcAddress(); }
         const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
 
         const BpBinder* mBinder;
     };
-    const PrivateAccessorForId getPrivateAccessorForId() const {
-        return PrivateAccessorForId(this);
-    }
+    const PrivateAccessor getPrivateAccessor() const { return PrivateAccessor(this); }
 
 private:
-    friend PrivateAccessorForId;
+    friend PrivateAccessor;
     friend class sp<BpBinder>;
 
+    static sp<BpBinder> create(int32_t handle);
+    static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address);
+
     struct BinderHandle {
         int32_t handle;
     };
     struct RpcHandle {
         sp<RpcSession> session;
-        RpcAddress address;
+        uint64_t address;
     };
     using Handle = std::variant<BinderHandle, RpcHandle>;
 
     int32_t binderHandle() const;
-    const RpcAddress& rpcAddress() const;
+    uint64_t rpcAddress() const;
     const sp<RpcSession>& rpcSession() const;
 
     explicit BpBinder(Handle&& handle);
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 20a9f36..065e6e3 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -53,6 +53,13 @@
     // Provide information about the state of a frozen process
     static  status_t            getProcessFreezeInfo(pid_t pid, bool *sync_received,
                                                     bool *async_received);
+
+    // TODO: Remove the above legacy duplicated function in next version
+#ifndef __ANDROID_VNDK__
+    static  status_t            getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
+                                                    uint32_t *async_received);
+#endif
+
             sp<ProcessState>    process();
             
             status_t            clearLastError();
diff --git a/libs/binder/include/binder/RpcAddress.h b/libs/binder/include/binder/RpcAddress.h
deleted file mode 100644
index e428908..0000000
--- a/libs/binder/include/binder/RpcAddress.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <memory>
-
-#include <utils/Errors.h>
-
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
-namespace android {
-
-class Parcel;
-struct RpcWireAddress;
-
-/**
- * This class represents an identifier across an RPC boundary.
- */
-class RpcAddress {
-public:
-    /**
-     * The zero address is used for special RPC transactions, but it might also
-     * be used in conjunction with readFromParcel.
-     */
-    static RpcAddress zero();
-
-    bool isZero() const;
-
-    /**
-     * Create a new random address.
-     */
-    static RpcAddress random(bool forServer);
-
-    /**
-     * Whether this address was created with 'bool forServer' true
-     */
-    bool isForServer() const;
-
-    /**
-     * Whether this address is one that could be created with this version of
-     * libbinder.
-     */
-    bool isRecognizedType() const;
-
-    /**
-     * Creates a new address as a copy of an embedded object.
-     */
-    static RpcAddress fromRawEmbedded(const RpcWireAddress* raw);
-    const RpcWireAddress& viewRawEmbedded() const;
-
-    bool operator<(const RpcAddress& rhs) const;
-    std::string toString() const;
-
-    status_t writeToParcel(Parcel* parcel) const;
-    status_t readFromParcel(const Parcel& parcel);
-
-    ~RpcAddress();
-
-private:
-    RpcAddress();
-
-    std::shared_ptr<RpcWireAddress> mRawAddr;
-};
-
-} // namespace android
diff --git a/libs/binder/include/binder/RpcCertificateFormat.h b/libs/binder/include/binder/RpcCertificateFormat.h
new file mode 100644
index 0000000..bc9d814
--- /dev/null
+++ b/libs/binder/include/binder/RpcCertificateFormat.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Formats for serializing TLS certificate.
+
+#pragma once
+
+#include <string>
+
+namespace android {
+
+enum class RpcCertificateFormat {
+    PEM,
+    DER,
+};
+
+static inline std::string PrintToString(RpcCertificateFormat format) {
+    switch (format) {
+        case RpcCertificateFormat::PEM:
+            return "PEM";
+        case RpcCertificateFormat::DER:
+            return "DER";
+        default:
+            return "<unknown>";
+    }
+}
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcCertificateVerifier.h b/libs/binder/include/binder/RpcCertificateVerifier.h
new file mode 100644
index 0000000..97af31c
--- /dev/null
+++ b/libs/binder/include/binder/RpcCertificateVerifier.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <openssl/ssl.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+// An interface with a function that verifies a peer certificate. It is a wrapper over the custom
+// verify function (see SSL_CTX_set_custom_verify).
+class RpcCertificateVerifier {
+public:
+    virtual ~RpcCertificateVerifier() = default;
+    virtual status_t verify(const X509* peerCert, uint8_t* outAlert) = 0;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index d0e4e27..fb2cf23 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -17,7 +17,6 @@
 
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
 #include <binder/RpcSession.h>
 #include <binder/RpcTransport.h>
 #include <utils/Errors.h>
@@ -136,13 +135,7 @@
     /**
      * See RpcTransportCtx::getCertificate
      */
-    std::string getCertificate(CertificateFormat);
-
-    /**
-     * See RpcTransportCtx::addTrustedPeerCertificate.
-     * Thread-safe. This is only possible before the server is join()-ing.
-     */
-    status_t addTrustedPeerCertificate(CertificateFormat, std::string_view cert);
+    std::vector<uint8_t> getCertificate(RpcCertificateFormat);
 
     /**
      * Runs join() in a background thread. Immediately returns.
@@ -201,7 +194,7 @@
     std::map<std::thread::id, std::thread> mConnectingThreads;
     sp<IBinder> mRootObject;
     wp<IBinder> mRootObjectWeak;
-    std::map<RpcAddress, sp<RpcSession>> mSessions;
+    std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
     std::unique_ptr<FdTrigger> mShutdownTrigger;
     std::condition_variable mShutdownCv;
 };
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index d92af0a..6a29c05 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -17,7 +17,6 @@
 
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
 #include <binder/RpcTransport.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
@@ -54,12 +53,10 @@
     // Create an RpcSession with default configuration (raw sockets).
     static sp<RpcSession> make();
 
-    // Create an RpcSession with the given configuration. |serverCertificateFormat| and
+    // Create an RpcSession with the given configuration. |serverRpcCertificateFormat| and
     // |serverCertificate| must have values or be nullopt simultaneously. If they have values, set
     // server certificate.
-    static sp<RpcSession> make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory,
-                               std::optional<CertificateFormat> serverCertificateFormat,
-                               std::optional<std::string> serverCertificate);
+    static sp<RpcSession> make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
 
     /**
      * Set the maximum number of threads allowed to be made (for things like callbacks).
@@ -134,7 +131,7 @@
     /**
      * See RpcTransportCtx::getCertificate
      */
-    std::string getCertificate(CertificateFormat);
+    std::vector<uint8_t> getCertificate(RpcCertificateFormat);
 
     /**
      * Shuts down the service.
@@ -154,7 +151,13 @@
 
     [[nodiscard]] status_t transact(const sp<IBinder>& binder, uint32_t code, const Parcel& data,
                                     Parcel* reply, uint32_t flags);
-    [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
+
+    /**
+     * Generally, you should not call this, unless you are testing error
+     * conditions, as this is called automatically by BpBinders when they are
+     * deleted (this is also why a raw pointer is used here)
+     */
+    [[nodiscard]] status_t sendDecStrong(const BpBinder* binder);
 
     ~RpcSession();
 
@@ -173,6 +176,8 @@
     friend RpcState;
     explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
 
+    [[nodiscard]] status_t sendDecStrong(uint64_t address);
+
     class EventListener : public virtual RefBase {
     public:
         virtual void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) = 0;
@@ -183,12 +188,12 @@
     public:
         void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
         void onSessionIncomingThreadEnded() override;
-        void waitForShutdown(std::unique_lock<std::mutex>& lock);
+        void waitForShutdown(std::unique_lock<std::mutex>& lock, const sp<RpcSession>& session);
 
     private:
         std::condition_variable mCv;
-        volatile bool mShutdown = false;
     };
+    friend WaitForShutdownListener;
 
     struct RpcConnection : public RefBase {
         std::unique_ptr<RpcTransport> rpcTransport;
@@ -222,19 +227,21 @@
     static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result);
 
     [[nodiscard]] status_t setupClient(
-            const std::function<status_t(const RpcAddress& sessionId, bool incoming)>&
+            const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>&
                     connectAndInit);
     [[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address);
     [[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address,
-                                                    const RpcAddress& sessionId, bool incoming);
-    [[nodiscard]] status_t initAndAddConnection(base::unique_fd fd, const RpcAddress& sessionId,
+                                                    const std::vector<uint8_t>& sessionId,
+                                                    bool incoming);
+    [[nodiscard]] status_t initAndAddConnection(base::unique_fd fd,
+                                                const std::vector<uint8_t>& sessionId,
                                                 bool incoming);
     [[nodiscard]] status_t addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport);
     [[nodiscard]] status_t addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport,
                                                  bool init);
     [[nodiscard]] bool setForServer(const wp<RpcServer>& server,
                                     const wp<RpcSession::EventListener>& eventListener,
-                                    const RpcAddress& sessionId);
+                                    const std::vector<uint8_t>& sessionId);
     sp<RpcConnection> assignIncomingConnectionToThisThread(
             std::unique_ptr<RpcTransport> rpcTransport);
     [[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection);
@@ -291,7 +298,7 @@
     sp<WaitForShutdownListener> mShutdownListener; // used for client sessions
     wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client
 
-    std::optional<RpcAddress> mId;
+    std::vector<uint8_t> mId;
 
     std::unique_ptr<FdTrigger> mShutdownTrigger;
 
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 8d08b34..1c0bb18 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -25,15 +25,12 @@
 #include <android-base/unique_fd.h>
 #include <utils/Errors.h>
 
+#include <binder/RpcCertificateFormat.h>
+
 namespace android {
 
 class FdTrigger;
 
-enum class CertificateFormat {
-    PEM,
-    // TODO(b/195166979): support other formats, e.g. DER
-};
-
 // Represents a socket connection.
 // No thread-safety is guaranteed for these APIs.
 class RpcTransport {
@@ -76,19 +73,8 @@
     // Implementation details:
     // - For raw sockets, this always returns empty string.
     // - For TLS, this returns the certificate. See RpcTransportTls for details.
-    [[nodiscard]] virtual std::string getCertificate(CertificateFormat format) const = 0;
-
-    // Add a trusted peer certificate. Peers presenting this certificate are accepted.
-    //
-    // Caller must ensure that newTransport() are called after all trusted peer certificates
-    // are added. Otherwise, RpcTransport-s created before may not trust peer certificates
-    // added later.
-    //
-    // Implementation details:
-    // - For raw sockets, this always returns OK.
-    // - For TLS, this adds trusted peer certificate. See RpcTransportTls for details.
-    [[nodiscard]] virtual status_t addTrustedPeerCertificate(CertificateFormat format,
-                                                             std::string_view cert) = 0;
+    [[nodiscard]] virtual std::vector<uint8_t> getCertificate(
+            RpcCertificateFormat format) const = 0;
 
 protected:
     RpcTransportCtx() = default;
diff --git a/libs/binder/include_tls/binder/RpcCertificateUtils.h b/libs/binder/include_tls/binder/RpcCertificateUtils.h
new file mode 100644
index 0000000..8d07835
--- /dev/null
+++ b/libs/binder/include_tls/binder/RpcCertificateUtils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Utilities for serializing and deserializing X509 certificates.
+
+#pragma once
+
+#include <vector>
+
+#include <openssl/ssl.h>
+
+#include <binder/RpcCertificateFormat.h>
+
+namespace android {
+
+bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& cert,
+                                             RpcCertificateFormat format);
+
+std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format);
+
+} // namespace android
diff --git a/libs/binder/include_tls/binder/RpcTransportTls.h b/libs/binder/include_tls/binder/RpcTransportTls.h
index 531aaa9..f26a3e9 100644
--- a/libs/binder/include_tls/binder/RpcTransportTls.h
+++ b/libs/binder/include_tls/binder/RpcTransportTls.h
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include <binder/RpcCertificateVerifier.h>
 #include <binder/RpcTransport.h>
 
 namespace android {
@@ -25,14 +26,17 @@
 // RpcTransportCtxFactory with TLS enabled with self-signed certificate.
 class RpcTransportCtxFactoryTls : public RpcTransportCtxFactory {
 public:
-    static std::unique_ptr<RpcTransportCtxFactory> make();
+    static std::unique_ptr<RpcTransportCtxFactory> make(std::shared_ptr<RpcCertificateVerifier>);
 
     std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
     std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
     const char* toCString() const override;
 
 private:
-    RpcTransportCtxFactoryTls() = default;
+    RpcTransportCtxFactoryTls(std::shared_ptr<RpcCertificateVerifier> verifier)
+          : mCertVerifier(std::move(verifier)){};
+
+    std::shared_ptr<RpcCertificateVerifier> mCertVerifier;
 };
 
 } // namespace android
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index cb330a6..7e8e3a5 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -112,8 +112,7 @@
     FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
 };
 pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::add_service;
-pub use native::Binder;
+pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder};
 pub use parcel::Parcel;
 pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
 pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
@@ -129,7 +128,10 @@
 /// The public API usable outside AIDL-generated interface crates.
 pub mod public_api {
     pub use super::parcel::ParcelFileDescriptor;
-    pub use super::{add_service, get_interface, wait_for_interface};
+    pub use super::{
+        add_service, force_lazy_services_persist, get_interface, register_lazy_service,
+        wait_for_interface,
+    };
     pub use super::{
         BinderFeatures, DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder,
         Status, StatusCode, Strong, ThreadState, Weak, WpIBinder,
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index a0dfeec..e7c3396 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode};
+use crate::binder::{
+    AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode,
+};
 use crate::error::{status_result, status_t, Result, StatusCode};
 use crate::parcel::{Parcel, Serialize};
 use crate::proxy::SpIBinder;
@@ -321,7 +323,12 @@
     /// contains a `T` pointer in its user data. fd should be a non-owned file
     /// descriptor, and args must be an array of null-terminated string
     /// poiinters with length num_args.
-    unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t {
+    unsafe extern "C" fn on_dump(
+        binder: *mut sys::AIBinder,
+        fd: i32,
+        args: *mut *const c_char,
+        num_args: u32,
+    ) -> status_t {
         if fd < 0 {
             return StatusCode::UNEXPECTED_NULL as status_t;
         }
@@ -447,6 +454,41 @@
     status_result(status)
 }
 
+/// Register a dynamic service via the LazyServiceRegistrar.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier. The service process
+/// will be shut down once all registered services are no longer in use.
+///
+/// If any service in the process is registered as lazy, all should be, otherwise
+/// the process may be shut down while a service is in use.
+pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+    let instance = CString::new(identifier).unwrap();
+    let status = unsafe {
+        // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
+        // string pointers. Caller retains ownership of both
+        // pointers. `AServiceManager_registerLazyService` creates a new strong reference
+        // and copies the string, so both pointers need only be valid until the
+        // call returns.
+
+        sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr())
+    };
+    status_result(status)
+}
+
+/// Prevent a process which registers lazy services from being shut down even when none
+/// of the services is in use.
+///
+/// If persist is true then shut down will be blocked until this function is called again with
+/// persist false. If this is to be the initial state, call this function before calling
+/// register_lazy_service.
+pub fn force_lazy_services_persist(persist: bool) {
+    unsafe {
+        // Safety: No borrowing or transfer of ownership occurs here.
+        sys::AServiceManager_forceLazyServicesPersist(persist)
+    }
+}
+
 /// Tests often create a base BBinder instance; so allowing the unit
 /// type to be remotable translates nicely to Binder::new(()).
 impl Remotable for () {
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 13ea827..1968058 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -120,9 +120,12 @@
     host_supported: true,
     unstable: true,
     srcs: [
+        "BinderRpcTestClientInfo.aidl",
+        "BinderRpcTestServerInfo.aidl",
         "IBinderRpcCallback.aidl",
         "IBinderRpcSession.aidl",
         "IBinderRpcTest.aidl",
+        "ParcelableCertificateData.aidl",
     ],
     backend: {
         java: {
@@ -150,6 +153,7 @@
 
     srcs: [
         "binderRpcTest.cpp",
+        "RpcCertificateVerifierSimple.cpp",
     ],
     shared_libs: [
         "libbinder",
diff --git a/libs/binder/tests/BinderRpcTestClientInfo.aidl b/libs/binder/tests/BinderRpcTestClientInfo.aidl
new file mode 100644
index 0000000..b4baebc
--- /dev/null
+++ b/libs/binder/tests/BinderRpcTestClientInfo.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import ParcelableCertificateData;
+
+parcelable BinderRpcTestClientInfo {
+    ParcelableCertificateData[] certs;
+}
diff --git a/libs/binder/tests/BinderRpcTestServerInfo.aidl b/libs/binder/tests/BinderRpcTestServerInfo.aidl
new file mode 100644
index 0000000..00dc0bc
--- /dev/null
+++ b/libs/binder/tests/BinderRpcTestServerInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import ParcelableCertificateData;
+
+parcelable BinderRpcTestServerInfo {
+    long port;
+    ParcelableCertificateData cert;
+}
diff --git a/libs/binder/tests/ParcelableCertificateData.aidl b/libs/binder/tests/ParcelableCertificateData.aidl
new file mode 100644
index 0000000..38c382e
--- /dev/null
+++ b/libs/binder/tests/ParcelableCertificateData.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcelable ParcelableCertificateData {
+    byte[] data;
+}
diff --git a/libs/binder/tests/RpcCertificateVerifierSimple.cpp b/libs/binder/tests/RpcCertificateVerifierSimple.cpp
new file mode 100644
index 0000000..4694d1b
--- /dev/null
+++ b/libs/binder/tests/RpcCertificateVerifierSimple.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "RpcCertificateVerifierSimple"
+#include <log/log.h>
+
+#include <binder/RpcCertificateUtils.h>
+
+#include "RpcCertificateVerifierSimple.h"
+
+namespace android {
+
+status_t RpcCertificateVerifierSimple::verify(const X509* peerCert, uint8_t* outAlert) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    for (const auto& trustedCert : mTrustedPeerCertificates) {
+        if (0 == X509_cmp(trustedCert.get(), peerCert)) {
+            return OK;
+        }
+    }
+    *outAlert = SSL_AD_CERTIFICATE_UNKNOWN;
+    return PERMISSION_DENIED;
+}
+
+status_t RpcCertificateVerifierSimple::addTrustedPeerCertificate(RpcCertificateFormat format,
+                                                                 const std::vector<uint8_t>& cert) {
+    bssl::UniquePtr<X509> x509 = deserializeCertificate(cert, format);
+    if (x509 == nullptr) {
+        ALOGE("Certificate is not in the proper format %s", PrintToString(format).c_str());
+        return BAD_VALUE;
+    }
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTrustedPeerCertificates.push_back(std::move(x509));
+    return OK;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/RpcCertificateVerifierSimple.h b/libs/binder/tests/RpcCertificateVerifierSimple.h
new file mode 100644
index 0000000..1f2e531
--- /dev/null
+++ b/libs/binder/tests/RpcCertificateVerifierSimple.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <string_view>
+#include <vector>
+
+#include <openssl/ssl.h>
+
+#include <binder/RpcCertificateFormat.h>
+#include <binder/RpcCertificateVerifier.h>
+
+namespace android {
+
+// A simple certificate verifier for testing.
+// Keep a list of leaf certificates as trusted. No certificate chain support.
+//
+// All APIs are thread-safe. However, if verify() and addTrustedPeerCertificate() are called
+// simultaneously in different threads, it is not deterministic whether verify() will use the
+// certificate being added.
+class RpcCertificateVerifierSimple : public RpcCertificateVerifier {
+public:
+    status_t verify(const X509*, uint8_t*) override;
+
+    // Add a trusted peer certificate. Peers presenting this certificate are accepted.
+    //
+    // Caller must ensure that RpcTransportCtx::newTransport() are called after all trusted peer
+    // certificates are added. Otherwise, RpcTransport-s created before may not trust peer
+    // certificates added later.
+    [[nodiscard]] status_t addTrustedPeerCertificate(RpcCertificateFormat format,
+                                                     const std::vector<uint8_t>& cert);
+
+private:
+    std::mutex mMutex; // for below
+    std::vector<bssl::UniquePtr<X509>> mTrustedPeerCertificates;
+};
+
+} // namespace android
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index eea7d8c..639876f 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -465,31 +465,30 @@
 
 TEST_F(BinderLibTest, Freeze) {
     Parcel data, reply, replypid;
-    std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
+    std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
 
-    //Pass test on devices where the freezer is not supported
+    // Pass test on devices where the cgroup v2 freezer is not supported
     if (freezer_file.fail()) {
         GTEST_SKIP();
         return;
     }
 
-    std::string freezer_enabled;
-    std::getline(freezer_file, freezer_enabled);
-
-    //Pass test on devices where the freezer is disabled
-    if (freezer_enabled != "1") {
-        GTEST_SKIP();
-        return;
-    }
-
     EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
     int32_t pid = replypid.readInt32();
     for (int i = 0; i < 10; i++) {
         EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
     }
-    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
-    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
-    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 1, 1000));
+
+    // Pass test on devices where BINDER_FREEZE ioctl is not supported
+    int ret = IPCThreadState::self()->freeze(pid, false, 0);
+    if (ret != 0) {
+        GTEST_SKIP();
+        return;
+    }
+
+    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
     EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
 
     bool sync_received, async_received;
@@ -500,6 +499,14 @@
     EXPECT_EQ(sync_received, 1);
     EXPECT_EQ(async_received, 0);
 
+    uint32_t sync_received2, async_received2;
+
+    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received2,
+                &async_received2));
+
+    EXPECT_EQ(sync_received2, 1);
+    EXPECT_EQ(async_received2, 0);
+
     EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
     EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
 }
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 7c405d3..a4e37ad 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <BinderRpcTestClientInfo.h>
+#include <BinderRpcTestServerInfo.h>
 #include <BnBinderRpcCallback.h>
 #include <BnBinderRpcSession.h>
 #include <BnBinderRpcTest.h>
@@ -40,14 +42,20 @@
 #include <thread>
 #include <type_traits>
 
+#include <poll.h>
 #include <sys/prctl.h>
 #include <unistd.h>
 
+#include "../FdTrigger.h"
 #include "../RpcSocketAddress.h" // for testing preconnected clients
 #include "../RpcState.h"   // for debugging
 #include "../vm_sockets.h" // for VMADDR_*
+#include "RpcCertificateVerifierSimple.h"
 
 using namespace std::chrono_literals;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
 
 namespace android {
 
@@ -61,12 +69,17 @@
     return {RpcSecurity::RAW, RpcSecurity::TLS};
 }
 
-static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(RpcSecurity rpcSecurity) {
+static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
+        RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr) {
     switch (rpcSecurity) {
         case RpcSecurity::RAW:
             return RpcTransportCtxFactoryRaw::make();
-        case RpcSecurity::TLS:
-            return RpcTransportCtxFactoryTls::make();
+        case RpcSecurity::TLS: {
+            if (verifier == nullptr) {
+                verifier = std::make_shared<RpcCertificateVerifierSimple>();
+            }
+            return RpcTransportCtxFactoryTls::make(std::move(verifier));
+        }
         default:
             LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
     }
@@ -304,14 +317,17 @@
 class Process {
 public:
     Process(Process&&) = default;
-    Process(const std::function<void(android::base::borrowed_fd /* writeEnd */)>& f) {
-        android::base::unique_fd writeEnd;
-        CHECK(android::base::Pipe(&mReadEnd, &writeEnd)) << strerror(errno);
+    Process(const std::function<void(android::base::borrowed_fd /* writeEnd */,
+                                     android::base::borrowed_fd /* readEnd */)>& f) {
+        android::base::unique_fd childWriteEnd;
+        android::base::unique_fd childReadEnd;
+        CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd)) << strerror(errno);
+        CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd)) << strerror(errno);
         if (0 == (mPid = fork())) {
             // racey: assume parent doesn't crash before this is set
             prctl(PR_SET_PDEATHSIG, SIGHUP);
 
-            f(writeEnd);
+            f(childWriteEnd, childReadEnd);
 
             exit(0);
         }
@@ -322,16 +338,20 @@
         }
     }
     android::base::borrowed_fd readEnd() { return mReadEnd; }
+    android::base::borrowed_fd writeEnd() { return mWriteEnd; }
 
 private:
     pid_t mPid = 0;
     android::base::unique_fd mReadEnd;
+    android::base::unique_fd mWriteEnd;
 };
 
 static std::string allocateSocketAddress() {
     static size_t id = 0;
     std::string temp = getenv("TMPDIR") ?: "/tmp";
-    return temp + "/binderRpcTest_" + std::to_string(id++);
+    auto ret = temp + "/binderRpcTest_" + std::to_string(id++);
+    unlink(ret.c_str());
+    return ret;
 };
 
 static unsigned int allocateVsockPort() {
@@ -434,16 +454,17 @@
     }
 }
 
-static base::unique_fd connectToUds(const char* addrStr) {
-    UnixSocketAddress addr(addrStr);
+static base::unique_fd connectTo(const RpcSocketAddress& addr) {
     base::unique_fd serverFd(
             TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
     int savedErrno = errno;
-    CHECK(serverFd.ok()) << "Could not create socket " << addrStr << ": " << strerror(savedErrno);
+    CHECK(serverFd.ok()) << "Could not create socket " << addr.toString() << ": "
+                         << strerror(savedErrno);
 
     if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
         int savedErrno = errno;
-        LOG(FATAL) << "Could not connect to socket " << addrStr << ": " << strerror(savedErrno);
+        LOG(FATAL) << "Could not connect to socket " << addr.toString() << ": "
+                   << strerror(savedErrno);
     }
     return serverFd;
 }
@@ -461,6 +482,37 @@
         return PrintToString(type) + "_" + newFactory(security)->toCString();
     }
 
+    static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
+        uint64_t length = str.length();
+        CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
+        CHECK(android::base::WriteFully(fd, str.data(), str.length()));
+    }
+
+    static inline std::string readString(android::base::borrowed_fd fd) {
+        uint64_t length;
+        CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
+        std::string ret(length, '\0');
+        CHECK(android::base::ReadFully(fd, ret.data(), length));
+        return ret;
+    }
+
+    static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
+        Parcel parcel;
+        CHECK_EQ(OK, parcelable.writeToParcel(&parcel));
+        writeString(fd,
+                    std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize()));
+    }
+
+    template <typename T>
+    static inline T readFromFd(android::base::borrowed_fd fd) {
+        std::string data = readString(fd);
+        Parcel parcel;
+        CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
+        T object;
+        CHECK_EQ(OK, object.readFromParcel(&parcel));
+        return object;
+    }
+
     // This creates a new process serving an interface on a certain number of
     // threads.
     ProcessSession createRpcTestSocketServerProcess(
@@ -472,11 +524,12 @@
 
         unsigned int vsockPort = allocateVsockPort();
         std::string addr = allocateSocketAddress();
-        unlink(addr.c_str());
 
         auto ret = ProcessSession{
-                .host = Process([&](android::base::borrowed_fd writeEnd) {
-                    sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity));
+                .host = Process([&](android::base::borrowed_fd writeEnd,
+                                    android::base::borrowed_fd readEnd) {
+                    auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
+                    sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
 
                     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
                     server->setMaxThreads(options.numThreads);
@@ -501,7 +554,20 @@
                             LOG_ALWAYS_FATAL("Unknown socket type");
                     }
 
-                    CHECK(android::base::WriteFully(writeEnd, &outPort, sizeof(outPort)));
+                    BinderRpcTestServerInfo serverInfo;
+                    serverInfo.port = static_cast<int64_t>(outPort);
+                    serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM);
+                    writeToFd(writeEnd, serverInfo);
+                    auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd);
+
+                    if (rpcSecurity == RpcSecurity::TLS) {
+                        for (const auto& clientCert : clientInfo.certs) {
+                            CHECK_EQ(OK,
+                                     certVerifier
+                                             ->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+                                                                         clientCert.data));
+                        }
+                    }
 
                     configure(server);
 
@@ -512,24 +578,41 @@
                 }),
         };
 
-        // always read socket, so that we have waited for the server to start
-        unsigned int outPort = 0;
-        CHECK(android::base::ReadFully(ret.host.readEnd(), &outPort, sizeof(outPort)));
+        std::vector<sp<RpcSession>> sessions;
+        auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
+        for (size_t i = 0; i < options.numSessions; i++) {
+            sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
+        }
+
+        auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret.host.readEnd());
+        BinderRpcTestClientInfo clientInfo;
+        for (const auto& session : sessions) {
+            auto& parcelableCert = clientInfo.certs.emplace_back();
+            parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
+        }
+        writeToFd(ret.host.writeEnd(), clientInfo);
+
+        CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
         if (socketType == SocketType::INET) {
-            CHECK_NE(0, outPort);
+            CHECK_NE(0, serverInfo.port);
+        }
+
+        if (rpcSecurity == RpcSecurity::TLS) {
+            const auto& serverCert = serverInfo.cert.data;
+            CHECK_EQ(OK,
+                     certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+                                                             serverCert));
         }
 
         status_t status;
 
-        for (size_t i = 0; i < options.numSessions; i++) {
-            sp<RpcSession> session =
-                    RpcSession::make(newFactory(rpcSecurity), std::nullopt, std::nullopt);
+        for (const auto& session : sessions) {
             session->setMaxThreads(options.numIncomingConnections);
 
             switch (socketType) {
                 case SocketType::PRECONNECTED:
                     status = session->setupPreconnectedClient({}, [=]() {
-                        return connectToUds(addr.c_str());
+                        return connectTo(UnixSocketAddress(addr.c_str()));
                     });
                     if (status == OK) goto success;
                     break;
@@ -542,7 +625,7 @@
                     if (status == OK) goto success;
                     break;
                 case SocketType::INET:
-                    status = session->setupInetClient("127.0.0.1", outPort);
+                    status = session->setupInetClient("127.0.0.1", serverInfo.port);
                     if (status == OK) goto success;
                     break;
                 default:
@@ -1208,16 +1291,17 @@
     }
     server->start();
 
-    sp<RpcSession> session =
-            RpcSession::make(RpcTransportCtxFactoryRaw::make(), std::nullopt, std::nullopt);
+    sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
     status_t status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
     while (!server->shutdown()) usleep(10000);
     ALOGE("Detected vsock loopback supported: %s", statusToString(status).c_str());
     return status == OK;
 }
 
-static std::vector<SocketType> testSocketTypes() {
-    std::vector<SocketType> ret = {SocketType::PRECONNECTED, SocketType::UNIX, SocketType::INET};
+static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
+    std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET};
+
+    if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED);
 
     static bool hasVsockLoopback = testSupportVsockLoopback();
 
@@ -1286,7 +1370,6 @@
 
 TEST_P(BinderRpcSimple, Shutdown) {
     auto addr = allocateSocketAddress();
-    unlink(addr.c_str());
     auto server = RpcServer::make(newFactory(GetParam()));
     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
     ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
@@ -1351,6 +1434,314 @@
 INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()),
                         BinderRpcSimple::PrintTestParam);
 
+class RpcTransportTest
+      : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity, RpcCertificateFormat>> {
+public:
+    using ConnectToServer = std::function<base::unique_fd()>;
+    static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+        auto [socketType, rpcSecurity, certificateFormat] = info.param;
+        return PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString() + "_" +
+                PrintToString(certificateFormat);
+    }
+    void TearDown() override {
+        for (auto& server : mServers) server->shutdown();
+    }
+
+    // A server that handles client socket connections.
+    class Server {
+    public:
+        explicit Server() {}
+        Server(Server&&) = default;
+        ~Server() { shutdown(); }
+        [[nodiscard]] AssertionResult setUp() {
+            auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+            auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
+            rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+            switch (socketType) {
+                case SocketType::PRECONNECTED: {
+                    return AssertionFailure() << "Not supported by this test";
+                } break;
+                case SocketType::UNIX: {
+                    auto addr = allocateSocketAddress();
+                    auto status = rpcServer->setupUnixDomainServer(addr.c_str());
+                    if (status != OK) {
+                        return AssertionFailure()
+                                << "setupUnixDomainServer: " << statusToString(status);
+                    }
+                    mConnectToServer = [addr] {
+                        return connectTo(UnixSocketAddress(addr.c_str()));
+                    };
+                } break;
+                case SocketType::VSOCK: {
+                    auto port = allocateVsockPort();
+                    auto status = rpcServer->setupVsockServer(port);
+                    if (status != OK) {
+                        return AssertionFailure() << "setupVsockServer: " << statusToString(status);
+                    }
+                    mConnectToServer = [port] {
+                        return connectTo(VsockSocketAddress(VMADDR_CID_LOCAL, port));
+                    };
+                } break;
+                case SocketType::INET: {
+                    unsigned int port;
+                    auto status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port);
+                    if (status != OK) {
+                        return AssertionFailure() << "setupInetServer: " << statusToString(status);
+                    }
+                    mConnectToServer = [port] {
+                        const char* addr = kLocalInetAddress;
+                        auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
+                        if (aiStart == nullptr) return base::unique_fd{};
+                        for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+                            auto fd = connectTo(
+                                    InetSocketAddress(ai->ai_addr, ai->ai_addrlen, addr, port));
+                            if (fd.ok()) return fd;
+                        }
+                        ALOGE("None of the socket address resolved for %s:%u can be connected",
+                              addr, port);
+                        return base::unique_fd{};
+                    };
+                }
+            }
+            mFd = rpcServer->releaseServer();
+            if (!mFd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
+            mCtx = newFactory(rpcSecurity, mCertVerifier)->newServerCtx();
+            if (mCtx == nullptr) return AssertionFailure() << "newServerCtx";
+            mSetup = true;
+            return AssertionSuccess();
+        }
+        RpcTransportCtx* getCtx() const { return mCtx.get(); }
+        std::shared_ptr<RpcCertificateVerifierSimple> getCertVerifier() const {
+            return mCertVerifier;
+        }
+        ConnectToServer getConnectToServerFn() { return mConnectToServer; }
+        void start() {
+            LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
+            mThread = std::make_unique<std::thread>(&Server::run, this);
+        }
+        void run() {
+            LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
+
+            std::vector<std::thread> threads;
+            while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) {
+                base::unique_fd acceptedFd(
+                        TEMP_FAILURE_RETRY(accept4(mFd.get(), nullptr, nullptr /*length*/,
+                                                   SOCK_CLOEXEC | SOCK_NONBLOCK)));
+                threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd));
+            }
+
+            for (auto& thread : threads) thread.join();
+        }
+        void handleOne(android::base::unique_fd acceptedFd) {
+            ASSERT_TRUE(acceptedFd.ok());
+            auto serverTransport = mCtx->newTransport(std::move(acceptedFd), mFdTrigger.get());
+            if (serverTransport == nullptr) return; // handshake failed
+            std::string message(kMessage);
+            ASSERT_EQ(OK,
+                      serverTransport->interruptableWriteFully(mFdTrigger.get(), message.data(),
+                                                               message.size()));
+        }
+        void shutdown() {
+            mFdTrigger->trigger();
+            if (mThread != nullptr) {
+                mThread->join();
+                mThread = nullptr;
+            }
+        }
+
+    private:
+        std::unique_ptr<std::thread> mThread;
+        ConnectToServer mConnectToServer;
+        std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
+        base::unique_fd mFd;
+        std::unique_ptr<RpcTransportCtx> mCtx;
+        std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
+                std::make_shared<RpcCertificateVerifierSimple>();
+        bool mSetup = false;
+    };
+
+    class Client {
+    public:
+        explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {}
+        Client(Client&&) = default;
+        [[nodiscard]] AssertionResult setUp() {
+            auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+            mFd = mConnectToServer();
+            if (!mFd.ok()) return AssertionFailure() << "Cannot connect to server";
+            mFdTrigger = FdTrigger::make();
+            mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx();
+            if (mCtx == nullptr) return AssertionFailure() << "newClientCtx";
+            return AssertionSuccess();
+        }
+        RpcTransportCtx* getCtx() const { return mCtx.get(); }
+        std::shared_ptr<RpcCertificateVerifierSimple> getCertVerifier() const {
+            return mCertVerifier;
+        }
+        void run(bool handshakeOk = true, bool readOk = true) {
+            auto clientTransport = mCtx->newTransport(std::move(mFd), mFdTrigger.get());
+            if (clientTransport == nullptr) {
+                ASSERT_FALSE(handshakeOk) << "newTransport returns nullptr, but it shouldn't";
+                return;
+            }
+            ASSERT_TRUE(handshakeOk) << "newTransport does not return nullptr, but it should";
+            std::string expectedMessage(kMessage);
+            std::string readMessage(expectedMessage.size(), '\0');
+            status_t readStatus =
+                    clientTransport->interruptableReadFully(mFdTrigger.get(), readMessage.data(),
+                                                            readMessage.size());
+            if (readOk) {
+                ASSERT_EQ(OK, readStatus);
+                ASSERT_EQ(readMessage, expectedMessage);
+            } else {
+                ASSERT_NE(OK, readStatus);
+            }
+        }
+
+    private:
+        ConnectToServer mConnectToServer;
+        base::unique_fd mFd;
+        std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
+        std::unique_ptr<RpcTransportCtx> mCtx;
+        std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
+                std::make_shared<RpcCertificateVerifierSimple>();
+    };
+
+    // Make A trust B.
+    template <typename A, typename B>
+    status_t trust(A* a, B* b) {
+        auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+        if (rpcSecurity != RpcSecurity::TLS) return OK;
+        auto bCert = b->getCtx()->getCertificate(certificateFormat);
+        return a->getCertVerifier()->addTrustedPeerCertificate(certificateFormat, bCert);
+    }
+
+    static constexpr const char* kMessage = "hello";
+    std::vector<std::unique_ptr<Server>> mServers;
+};
+
+TEST_P(RpcTransportTest, GoodCertificate) {
+    auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+    ASSERT_TRUE(server->setUp());
+
+    Client client(server->getConnectToServerFn());
+    ASSERT_TRUE(client.setUp());
+
+    ASSERT_EQ(OK, trust(&client, server));
+    ASSERT_EQ(OK, trust(server, &client));
+
+    server->start();
+    client.run();
+}
+
+TEST_P(RpcTransportTest, MultipleClients) {
+    auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+    ASSERT_TRUE(server->setUp());
+
+    std::vector<Client> clients;
+    for (int i = 0; i < 2; i++) {
+        auto& client = clients.emplace_back(server->getConnectToServerFn());
+        ASSERT_TRUE(client.setUp());
+        ASSERT_EQ(OK, trust(&client, server));
+        ASSERT_EQ(OK, trust(server, &client));
+    }
+
+    server->start();
+    for (auto& client : clients) client.run();
+}
+
+TEST_P(RpcTransportTest, UntrustedServer) {
+    auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+
+    auto untrustedServer = mServers.emplace_back(std::make_unique<Server>()).get();
+    ASSERT_TRUE(untrustedServer->setUp());
+
+    Client client(untrustedServer->getConnectToServerFn());
+    ASSERT_TRUE(client.setUp());
+
+    ASSERT_EQ(OK, trust(untrustedServer, &client));
+
+    untrustedServer->start();
+
+    // For TLS, this should reject the certificate. For RAW sockets, it should pass because
+    // the client can't verify the server's identity.
+    bool handshakeOk = rpcSecurity != RpcSecurity::TLS;
+    client.run(handshakeOk);
+}
+TEST_P(RpcTransportTest, MaliciousServer) {
+    auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+    auto validServer = mServers.emplace_back(std::make_unique<Server>()).get();
+    ASSERT_TRUE(validServer->setUp());
+
+    auto maliciousServer = mServers.emplace_back(std::make_unique<Server>()).get();
+    ASSERT_TRUE(maliciousServer->setUp());
+
+    Client client(maliciousServer->getConnectToServerFn());
+    ASSERT_TRUE(client.setUp());
+
+    ASSERT_EQ(OK, trust(&client, validServer));
+    ASSERT_EQ(OK, trust(validServer, &client));
+    ASSERT_EQ(OK, trust(maliciousServer, &client));
+
+    maliciousServer->start();
+
+    // For TLS, this should reject the certificate. For RAW sockets, it should pass because
+    // the client can't verify the server's identity.
+    bool handshakeOk = rpcSecurity != RpcSecurity::TLS;
+    client.run(handshakeOk);
+}
+
+TEST_P(RpcTransportTest, UntrustedClient) {
+    auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+    auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+    ASSERT_TRUE(server->setUp());
+
+    Client client(server->getConnectToServerFn());
+    ASSERT_TRUE(client.setUp());
+
+    ASSERT_EQ(OK, trust(&client, server));
+
+    server->start();
+
+    // For TLS, Client should be able to verify server's identity, so client should see
+    // do_handshake() successfully executed. However, server shouldn't be able to verify client's
+    // identity and should drop the connection, so client shouldn't be able to read anything.
+    bool readOk = rpcSecurity != RpcSecurity::TLS;
+    client.run(true, readOk);
+}
+
+TEST_P(RpcTransportTest, MaliciousClient) {
+    auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+    auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+    ASSERT_TRUE(server->setUp());
+
+    Client validClient(server->getConnectToServerFn());
+    ASSERT_TRUE(validClient.setUp());
+    Client maliciousClient(server->getConnectToServerFn());
+    ASSERT_TRUE(maliciousClient.setUp());
+
+    ASSERT_EQ(OK, trust(&validClient, server));
+    ASSERT_EQ(OK, trust(&maliciousClient, server));
+
+    server->start();
+
+    // See UntrustedClient.
+    bool readOk = rpcSecurity != RpcSecurity::TLS;
+    maliciousClient.run(true, readOk);
+}
+
+std::vector<RpcCertificateFormat> testRpcCertificateFormats() {
+    return {
+            RpcCertificateFormat::PEM,
+            RpcCertificateFormat::DER,
+    };
+}
+
+INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest,
+                        ::testing::Combine(::testing::ValuesIn(testSocketTypes(false)),
+                                           ::testing::ValuesIn(RpcSecurityValues()),
+                                           ::testing::ValuesIn(testRpcCertificateFormats())),
+                        RpcTransportTest::PrintParamInfo);
+
 } // namespace android
 
 int main(int argc, char** argv) {
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 7fd9f6b..8bf04cc 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -36,8 +36,7 @@
 
 void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
     if (provider.ConsumeBool()) {
-        auto session =
-                RpcSession::make(RpcTransportCtxFactoryRaw::make(), std::nullopt, std::nullopt);
+        auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
         CHECK_EQ(OK, session->addNullDebuggingClient());
         p->markForRpc(session);
         fillRandomParcelData(p, std::move(provider));
diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
index b1263e8..6f054d2 100644
--- a/libs/binder/tests/unit_fuzzers/Android.bp
+++ b/libs/binder/tests/unit_fuzzers/Android.bp
@@ -51,7 +51,6 @@
 cc_fuzz {
     name: "binder_bpBinderFuzz",
     defaults: ["binder_fuzz_defaults"],
-    host_supported: false,
     srcs: ["BpBinderFuzz.cpp"],
 }
 
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index c50279b..95582bf 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -19,8 +19,15 @@
 #include <commonFuzzHelpers.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <android-base/logging.h>
 #include <binder/BpBinder.h>
 #include <binder/IServiceManager.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+
+#include <signal.h>
+#include <sys/prctl.h>
+#include <thread>
 
 namespace android {
 
@@ -28,13 +35,31 @@
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
     FuzzedDataProvider fdp(data, size);
 
-    // TODO: In the future it would be more effective to fork a new process and then pass a BBinder
-    // to your process. Right now this is not implemented because it would involved fuzzing IPC on a
-    // forked process, and libfuzzer will not be able to handle code coverage. This would lead to
-    // crashes that are not easy to diagnose.
-    int32_t handle = fdp.ConsumeIntegralInRange<int32_t>(0, 1024);
-    sp<BpBinder> bpbinder = BpBinder::create(handle);
-    if (bpbinder == nullptr) return 0;
+    std::string addr = std::string(getenv("TMPDIR") ?: "/tmp") + "/binderRpcBenchmark";
+    (void)unlink(addr.c_str());
+
+    sp<RpcServer> server = RpcServer::make();
+
+    // use RPC binder because fuzzer can't get coverage from another process.
+    auto thread = std::thread([&]() {
+        prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
+        server->setRootObject(sp<BBinder>::make());
+        server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+        CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
+        server->join();
+    });
+
+    sp<RpcSession> session = RpcSession::make();
+    status_t status;
+    for (size_t tries = 0; tries < 5; tries++) {
+        usleep(10000);
+        status = session->setupUnixDomainClient(addr.c_str());
+        if (status == OK) goto success;
+    }
+    LOG(FATAL) << "Unable to connect";
+success:
+
+    sp<BpBinder> bpBinder = session->getRootObject()->remoteBinder();
 
     // To prevent memory from running out from calling too many add item operations.
     const uint32_t MAX_RUNS = 2048;
@@ -43,12 +68,16 @@
 
     while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
         if (fdp.ConsumeBool()) {
-            callArbitraryFunction(&fdp, gBPBinderOperations, bpbinder, s_recipient);
+            callArbitraryFunction(&fdp, gBPBinderOperations, bpBinder, s_recipient);
         } else {
-            callArbitraryFunction(&fdp, gIBinderOperations, bpbinder.get());
+            callArbitraryFunction(&fdp, gIBinderOperations, bpBinder.get());
         }
     }
 
+    CHECK(session->shutdownAndWait(true)) << "couldn't shutdown session";
+    CHECK(server->shutdown()) << "couldn't shutdown server";
+    thread.join();
+
     return 0;
 }
 } // namespace android
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
index 6ca0e2f..741987f 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
@@ -52,7 +52,7 @@
                     const sp<IBinder::DeathRecipient>& s_recipient) -> void {
                      // Clean up possible leftover memory.
                      wp<IBinder::DeathRecipient> outRecipient(nullptr);
-                     bpbinder->sendObituary();
+                     if (!bpbinder->isRpcBinder()) bpbinder->sendObituary();
                      bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&kBpBinderCookie), 0,
                                              &outRecipient);
 
@@ -72,7 +72,9 @@
                  [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
                     const sp<IBinder::DeathRecipient>&) -> void { bpbinder->remoteBinder(); },
                  [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
-                    const sp<IBinder::DeathRecipient>&) -> void { bpbinder->sendObituary(); },
+                    const sp<IBinder::DeathRecipient>&) -> void {
+                     if (!bpbinder->isRpcBinder()) bpbinder->sendObituary();
+                 },
                  [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
                     const sp<IBinder::DeathRecipient>&) -> void {
                      uint32_t uid = fdp->ConsumeIntegral<uint32_t>();
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index df308d8..5fe5e71 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -645,7 +645,10 @@
                     slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
             return BAD_VALUE;
         } else if (!mSlots[slot].mBufferState.isDequeued()) {
-            BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
+            // TODO(http://b/140581935): This message is BQ_LOGW because it
+            // often logs when no actionable errors are present. Return to
+            // using BQ_LOGE after ensuring this only logs during errors.
+            BQ_LOGW("detachBuffer: slot %d is not owned by the producer "
                     "(state = %s)", slot, mSlots[slot].mBufferState.string());
             return BAD_VALUE;
         } else if (!mSlots[slot].mRequestBufferCalled) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9da7192..058e099 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4870,6 +4870,18 @@
     mBlockUntrustedTouchesMode = mode;
 }
 
+std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked(
+        const sp<IBinder>& token) {
+    for (auto& [displayId, state] : mTouchStatesByDisplay) {
+        for (TouchedWindow& w : state.windows) {
+            if (w.windowHandle->getToken() == token) {
+                return std::make_pair(&state, &w);
+            }
+        }
+    }
+    return std::make_pair(nullptr, nullptr);
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                                          bool isDragDrop) {
     if (fromToken == toToken) {
@@ -4882,58 +4894,43 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<WindowInfoHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
-        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken);
-        if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
-            ALOGW("Cannot transfer focus because from or to window not found.");
+        // Find the target touch state and touched window by fromToken.
+        auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken);
+        if (state == nullptr || touchedWindow == nullptr) {
+            ALOGD("Focus transfer failed because from window is not being touched.");
             return false;
         }
+
+        const int32_t displayId = state->displayId;
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
+        if (toWindowHandle == nullptr) {
+            ALOGW("Cannot transfer focus because to window not found.");
+            return false;
+        }
+
         if (DEBUG_FOCUS) {
             ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
-                  fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
-        }
-        if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Cannot transfer focus because windows are on different displays.");
-            }
-            return false;
+                  touchedWindow->windowHandle->getName().c_str(),
+                  toWindowHandle->getName().c_str());
         }
 
-        bool found = false;
-        for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
-            TouchState& state = pair.second;
-            for (size_t i = 0; i < state.windows.size(); i++) {
-                const TouchedWindow& touchedWindow = state.windows[i];
-                if (touchedWindow.windowHandle == fromWindowHandle) {
-                    int32_t oldTargetFlags = touchedWindow.targetFlags;
-                    BitSet32 pointerIds = touchedWindow.pointerIds;
+        // Erase old window.
+        int32_t oldTargetFlags = touchedWindow->targetFlags;
+        BitSet32 pointerIds = touchedWindow->pointerIds;
+        state->removeWindowByToken(fromToken);
 
-                    state.windows.erase(state.windows.begin() + i);
+        // Add new window.
+        int32_t newTargetFlags = oldTargetFlags &
+                (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
+                 InputTarget::FLAG_DISPATCH_AS_IS);
+        state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
 
-                    int32_t newTargetFlags = oldTargetFlags &
-                            (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
-                             InputTarget::FLAG_DISPATCH_AS_IS);
-                    state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
-
-                    // Store the dragging window.
-                    if (isDragDrop) {
-                        mDragState = std::make_unique<DragState>(toWindowHandle);
-                    }
-
-                    found = true;
-                    goto Found;
-                }
-            }
-        }
-    Found:
-
-        if (!found) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Focus transfer failed because from window did not have focus.");
-            }
-            return false;
+        // Store the dragging window.
+        if (isDragDrop) {
+            mDragState = std::make_unique<DragState>(toWindowHandle);
         }
 
+        // Synthesize cancel for old window and down for new window.
         sp<Connection> fromConnection = getConnectionLocked(fromToken);
         sp<Connection> toConnection = getConnectionLocked(toToken);
         if (fromConnection != nullptr && toConnection != nullptr) {
@@ -4961,27 +4958,20 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+        auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(),
+                               [](const auto& pair) { return pair.second.windows.size() == 1; });
+        if (it == mTouchStatesByDisplay.end()) {
+            ALOGW("Cannot transfer touch state because there is no exact window being touched");
+            return false;
+        }
+        const int32_t displayId = it->first;
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
         if (toWindowHandle == nullptr) {
             ALOGW("Could not find window associated with token=%p", destChannelToken.get());
             return false;
         }
 
-        const int32_t displayId = toWindowHandle->getInfo()->displayId;
-
-        auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-        if (touchStateIt == mTouchStatesByDisplay.end()) {
-            ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
-                  displayId);
-            return false;
-        }
-
-        TouchState& state = touchStateIt->second;
-        if (state.windows.size() != 1) {
-            ALOGW("Cannot transfer touch state because there are %zu windows being touched",
-                  state.windows.size());
-            return false;
-        }
+        TouchState& state = it->second;
         const TouchedWindow& touchedWindow = state.windows[0];
         fromToken = touchedWindow.windowHandle->getToken();
     } // release lock
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 86a3873..c09a89c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -644,6 +644,10 @@
                                              DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
                                              bool handled) REQUIRES(mLock);
 
+    // Find touched state and touched window by token.
+    std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
+            REQUIRES(mLock);
+
     // Statistics gathering.
     LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
     LatencyTracker mLatencyTracker GUARDED_BY(mLock);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f997cb7..0a91bde 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -47,7 +47,8 @@
 static const int32_t DEVICE_ID = 1;
 
 // An arbitrary display id.
-static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t SECOND_DISPLAY_ID = 1;
 
 // An arbitrary injector pid / uid pair that has permission to inject events.
 static const int32_t INJECTOR_PID = 999;
@@ -946,6 +947,15 @@
         mInfo.displayId = displayId;
     }
 
+    sp<FakeWindowHandle> clone(
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+            const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+        sp<FakeWindowHandle> handle =
+                new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)",
+                                     displayId, mInfo.token);
+        return handle;
+    }
+
     void setFocusable(bool focusable) { mInfo.focusable = focusable; }
 
     void setVisible(bool visible) { mInfo.visible = visible; }
@@ -2006,6 +2016,134 @@
     secondWindow->assertNoEvents();
 }
 
+// This case will create two windows and one mirrored window on the default display and mirror
+// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put
+// the windows info of second display before default display.
+TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> firstWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> mirrorWindowInPrimary =
+            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> firstWindowInSecondary =
+            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> secondWindowInSecondary =
+            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    // Update window info, let it find window handle of second display first.
+    mDispatcher->setInputWindows(
+            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+             {ADISPLAY_ID_DEFAULT,
+              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // Transfer touch focus
+    ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(),
+                                                secondWindowInPrimary->getToken()));
+    // The first window gets cancel.
+    firstWindowInPrimary->consumeMotionCancel();
+    secondWindowInPrimary->consumeMotionDown();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionMove();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionUp();
+}
+
+// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use
+// 'transferTouch' api.
+TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> firstWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> mirrorWindowInPrimary =
+            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> firstWindowInSecondary =
+            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> secondWindowInSecondary =
+            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    // Update window info, let it find window handle of second display first.
+    mDispatcher->setInputWindows(
+            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+             {ADISPLAY_ID_DEFAULT,
+              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+    // Touch on second display.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+    // Transfer touch focus
+    ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken()));
+
+    // The first window gets cancel.
+    firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID);
+    secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                SECOND_DISPLAY_ID, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
+}
+
 TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -2926,7 +3064,6 @@
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
-    static constexpr int32_t SECOND_DISPLAY_ID = 1;
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
@@ -3089,8 +3226,6 @@
 
 class InputFilterTest : public InputDispatcherTest {
 protected:
-    static constexpr int32_t SECOND_DISPLAY_ID = 1;
-
     void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
         NotifyMotionArgs motionArgs;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 58bb41a..a63145a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -28,51 +28,36 @@
 
 // Geometrical space to which content is projected.
 // For example, this can be the layer space or the physical display space.
-struct ProjectionSpace {
+class ProjectionSpace {
+public:
     ProjectionSpace() = default;
-    ProjectionSpace(ui::Size size, Rect content)
-          : bounds(std::move(size)), content(std::move(content)) {}
-
-    // Bounds of this space. Always starts at (0,0).
-    Rect bounds;
-
-    // Rect onto which content is projected.
-    Rect content;
-
-    // The orientation of this space. This value is meaningful only in relation to the rotation
-    // of another projection space and it's used to determine the rotating transformation when
-    // mapping between the two.
-    // As a convention when using this struct orientation = 0 for the "oriented*" projection
-    // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
-    // of the display space will become 90, while  the orientation of the layer stack space will
-    // remain the same.
-    ui::Rotation orientation = ui::ROTATION_0;
+    ProjectionSpace(ui::Size size, Rect content) : mBounds(size), mContent(std::move(content)) {}
 
     // Returns a transform which maps this.content into destination.content
     // and also rotates according to this.orientation and destination.orientation
     ui::Transform getTransform(const ProjectionSpace& destination) const {
-        ui::Rotation rotation = destination.orientation - orientation;
+        ui::Rotation rotation = destination.getOrientation() - mOrientation;
 
         // Compute a transformation which rotates the destination in a way it has the same
         // orientation as us.
         const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
         ui::Transform inverseRotatingTransform;
-        inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
-                                     destination.bounds.height());
+        inverseRotatingTransform.set(inverseRotationFlags, destination.getBounds().width,
+                                     destination.getBounds().height);
         // The destination content rotated so it has the same orientation as us.
-        Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
+        Rect orientedDestContent = inverseRotatingTransform.transform(destination.getContent());
 
         // Compute translation from the source content to (0, 0).
-        const float sourceX = content.left;
-        const float sourceY = content.top;
+        const float sourceX = mContent.left;
+        const float sourceY = mContent.top;
         ui::Transform sourceTranslation;
         sourceTranslation.set(-sourceX, -sourceY);
 
         // Compute scaling transform which maps source content to destination content, assuming
         // they are both at (0, 0).
         ui::Transform scale;
-        const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
-        const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
+        const float scaleX = static_cast<float>(orientedDestContent.width()) / mContent.width();
+        const float scaleY = static_cast<float>(orientedDestContent.height()) / mContent.height();
         scale.set(scaleX, 0, 0, scaleY);
 
         // Compute translation from (0, 0) to the orientated destination content.
@@ -83,8 +68,8 @@
 
         // Compute rotation transform.
         const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
-        auto orientedDestWidth = destination.bounds.width();
-        auto orientedDestHeight = destination.bounds.height();
+        auto orientedDestWidth = destination.getBounds().width;
+        auto orientedDestHeight = destination.getBounds().height;
         if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
             std::swap(orientedDestWidth, orientedDestHeight);
         }
@@ -98,9 +83,39 @@
     }
 
     bool operator==(const ProjectionSpace& other) const {
-        return bounds == other.bounds && content == other.content &&
-                orientation == other.orientation;
+        return mBounds == other.mBounds && mContent == other.mContent &&
+                mOrientation == other.mOrientation;
     }
+
+    void setBounds(ui::Size newBounds) { mBounds = std::move(newBounds); }
+
+    void setContent(Rect newContent) { mContent = std::move(newContent); }
+
+    void setOrientation(ui::Rotation newOrientation) { mOrientation = newOrientation; }
+
+    Rect getBoundsAsRect() const { return Rect(mBounds.getWidth(), mBounds.getHeight()); }
+
+    const ui::Size& getBounds() const { return mBounds; }
+
+    const Rect& getContent() const { return mContent; }
+
+    ui::Rotation getOrientation() const { return mOrientation; }
+
+private:
+    // Bounds of this space. Always starts at (0,0).
+    ui::Size mBounds = ui::Size();
+
+    // Rect onto which content is projected.
+    Rect mContent = Rect();
+
+    // The orientation of this space. This value is meaningful only in relation to the rotation
+    // of another projection space and it's used to determine the rotating transformation when
+    // mapping between the two.
+    // As a convention when using this struct orientation = 0 for the "oriented*" projection
+    // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
+    // of the display space will become 90, while  the orientation of the layer stack space will
+    // remain the same.
+    ui::Rotation mOrientation = ui::ROTATION_0;
 };
 
 } // namespace compositionengine
@@ -108,8 +123,8 @@
 inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
     return android::base::
             StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
-                         to_string(space.bounds).c_str(), to_string(space.content).c_str(),
-                         toCString(space.orientation));
+                         to_string(space.getBoundsAsRect()).c_str(),
+                         to_string(space.getContent()).c_str(), toCString(space.getOrientation()));
 }
 
 // Defining PrintTo helps with Google Tests.
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 6b9ea87..02fa49f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -53,7 +53,7 @@
     mId = args.id;
     mPowerAdvisor = args.powerAdvisor;
     editState().isSecure = args.isSecure;
-    editState().displaySpace.bounds = Rect(args.pixels);
+    editState().displaySpace.setBounds(args.pixels);
     setName(args.name);
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 31b699e..eb3f3b1 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -156,38 +156,40 @@
                            const Rect& orientedDisplaySpaceRect) {
     auto& outputState = editState();
 
-    outputState.displaySpace.orientation = orientation;
-    LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+    outputState.displaySpace.setOrientation(orientation);
+    LOG_FATAL_IF(outputState.displaySpace.getBoundsAsRect() == Rect::INVALID_RECT,
                  "The display bounds are unknown.");
 
     // Compute orientedDisplaySpace
-    ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+    ui::Size orientedSize = outputState.displaySpace.getBounds();
     if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
         std::swap(orientedSize.width, orientedSize.height);
     }
-    outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
-    outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+    outputState.orientedDisplaySpace.setBounds(orientedSize);
+    outputState.orientedDisplaySpace.setContent(orientedDisplaySpaceRect);
 
     // Compute displaySpace.content
     const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
     ui::Transform rotation;
     if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
-        const auto displaySize = outputState.displaySpace.bounds;
+        const auto displaySize = outputState.displaySpace.getBoundsAsRect();
         rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
     }
-    outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+    outputState.displaySpace.setContent(rotation.transform(orientedDisplaySpaceRect));
 
     // Compute framebufferSpace
-    outputState.framebufferSpace.orientation = orientation;
-    LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+    outputState.framebufferSpace.setOrientation(orientation);
+    LOG_FATAL_IF(outputState.framebufferSpace.getBoundsAsRect() == Rect::INVALID_RECT,
                  "The framebuffer bounds are unknown.");
-    const auto scale =
-            getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds);
-    outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+    const auto scale = getScale(outputState.displaySpace.getBoundsAsRect(),
+                                outputState.framebufferSpace.getBoundsAsRect());
+    outputState.framebufferSpace.setContent(
+            outputState.displaySpace.getContent().scale(scale.x, scale.y));
 
     // Compute layerStackSpace
-    outputState.layerStackSpace.content = layerStackSpaceRect;
-    outputState.layerStackSpace.bounds = layerStackSpaceRect;
+    outputState.layerStackSpace.setContent(layerStackSpaceRect);
+    outputState.layerStackSpace.setBounds(
+            ui::Size(layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight()));
 
     outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
     outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
@@ -200,21 +202,21 @@
     auto& state = editState();
 
     // Update framebuffer space
-    const Rect newBounds(size);
-    state.framebufferSpace.bounds = newBounds;
+    const ui::Size newBounds(size);
+    state.framebufferSpace.setBounds(newBounds);
 
     // Update display space
-    state.displaySpace.bounds = newBounds;
+    state.displaySpace.setBounds(newBounds);
     state.transform = state.layerStackSpace.getTransform(state.displaySpace);
 
     // Update oriented display space
-    const auto orientation = state.displaySpace.orientation;
+    const auto orientation = state.displaySpace.getOrientation();
     ui::Size orientedSize = size;
     if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
         std::swap(orientedSize.width, orientedSize.height);
     }
-    const Rect newOrientedBounds(orientedSize);
-    state.orientedDisplaySpace.bounds = newOrientedBounds;
+    const ui::Size newOrientedBounds(orientedSize);
+    state.orientedDisplaySpace.setBounds(newOrientedBounds);
 
     if (mPlanner) {
         mPlanner->setDisplaySize(size);
@@ -349,7 +351,7 @@
 void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
     mRenderSurface = std::move(surface);
     const auto size = mRenderSurface->getSize();
-    editState().framebufferSpace.bounds = Rect(size);
+    editState().framebufferSpace.setBounds(size);
     if (mPlanner) {
         mPlanner->setDisplaySize(size);
     }
@@ -370,7 +372,7 @@
 
 Region Output::getDirtyRegion() const {
     const auto& outputState = getState();
-    return outputState.dirtyRegion.intersect(outputState.layerStackSpace.content);
+    return outputState.dirtyRegion.intersect(outputState.layerStackSpace.getContent());
 }
 
 bool Output::includesLayer(ui::LayerFilter filter) const {
@@ -450,7 +452,7 @@
 
     // Compute the resulting coverage for this output, and store it for later
     const ui::Transform& tr = outputState.transform;
-    Region undefinedRegion{outputState.displaySpace.bounds};
+    Region undefinedRegion{outputState.displaySpace.getBoundsAsRect()};
     undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
 
     outputState.undefinedRegion = undefinedRegion;
@@ -647,7 +649,7 @@
     // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
     const auto& outputState = getState();
     Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
-    drawRegion.andSelf(outputState.displaySpace.bounds);
+    drawRegion.andSelf(outputState.displaySpace.getBoundsAsRect());
     if (drawRegion.isEmpty()) {
         return;
     }
@@ -665,7 +667,7 @@
     outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
     outputLayerState.coveredRegion = coveredRegion;
     outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
-            visibleNonShadowRegion.intersect(outputState.layerStackSpace.content));
+            visibleNonShadowRegion.intersect(outputState.layerStackSpace.getContent()));
     outputLayerState.shadowRegion = shadowRegion;
 }
 
@@ -1041,10 +1043,10 @@
     ALOGV("hasClientComposition");
 
     renderengine::DisplaySettings clientCompositionDisplay;
-    clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
-    clientCompositionDisplay.clip = outputState.layerStackSpace.content;
+    clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent();
+    clientCompositionDisplay.clip = outputState.layerStackSpace.getContent();
     clientCompositionDisplay.orientation =
-            ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
+            ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
     clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
             ? outputState.dataspace
             : ui::Dataspace::UNKNOWN;
@@ -1138,7 +1140,7 @@
     ALOGV("Rendering client layers");
 
     const auto& outputState = getState();
-    const Region viewportRegion(outputState.layerStackSpace.content);
+    const Region viewportRegion(outputState.layerStackSpace.getContent());
     bool firstLayer = true;
 
     bool disableBlurs = false;
@@ -1201,7 +1203,7 @@
                                                outputState.needsFiltering,
                                        .isSecure = outputState.isSecure,
                                        .supportsProtectedContent = supportsProtectedContent,
-                                       .viewport = outputState.layerStackSpace.content,
+                                       .viewport = outputState.layerStackSpace.getContent(),
                                        .dataspace = outputDataspace,
                                        .realContentIsVisible = realContentIsVisible,
                                        .clearContent = !clientComposition,
@@ -1308,7 +1310,7 @@
 
 void Output::dirtyEntireOutput() {
     auto& outputState = editState();
-    outputState.dirtyRegion.set(outputState.displaySpace.bounds);
+    outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect());
 }
 
 void Output::chooseCompositionStrategy() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 56e9d27..ecfd901 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -79,7 +79,7 @@
     FloatRect activeCropFloat =
             reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
 
-    const Rect& viewport = getOutput().getState().layerStackSpace.content;
+    const Rect& viewport = getOutput().getState().layerStackSpace.getContent();
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
     const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
     // Transform to screen space.
@@ -136,7 +136,7 @@
          * buffer
          */
         uint32_t invTransformOrient =
-                ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
+                ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
         // calculate the inverse transform
         if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
             invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -192,7 +192,7 @@
     Rect activeCrop = layerState.geomCrop;
     if (!activeCrop.isEmpty() && bufferSize.isValid()) {
         activeCrop = layerTransform.transform(activeCrop);
-        if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) {
+        if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) {
             activeCrop.clear();
         }
         activeCrop = inverseLayerTransform.transform(activeCrop, true);
@@ -228,7 +228,7 @@
         geomLayerBounds.bottom += outset;
     }
     Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))};
-    if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
+    if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) {
         frame.clear();
     }
     const ui::Transform displayTransform{outputState.transform};
@@ -628,7 +628,7 @@
     const auto& outputState = getOutput().getState();
 
     Rect frame = layerFEState->cursorFrame;
-    frame.intersect(outputState.layerStackSpace.content, &frame);
+    frame.intersect(outputState.layerStackSpace.getContent(), &frame);
     Rect position = outputState.transform.transform(frame);
 
     if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 5bbd58e..e6b716e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -159,13 +159,13 @@
 void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
                        const OutputCompositionState& outputState) {
     ATRACE_CALL();
-    const Rect& viewport = outputState.layerStackSpace.content;
+    const Rect& viewport = outputState.layerStackSpace.getContent();
     const ui::Dataspace& outputDataspace = outputState.dataspace;
     const ui::Transform::RotationFlags orientation =
-            ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
+            ui::Transform::toRotationFlags(outputState.framebufferSpace.getOrientation());
 
     renderengine::DisplaySettings displaySettings{
-            .physicalDisplay = outputState.framebufferSpace.content,
+            .physicalDisplay = outputState.framebufferSpace.getContent(),
             .clip = viewport,
             .outputDataspace = outputDataspace,
             .orientation = orientation,
@@ -282,7 +282,7 @@
         mOutputSpace = outputState.framebufferSpace;
         mTexture = texture;
         mTexture->setReadyFence(mDrawFence);
-        mOutputSpace.orientation = outputState.framebufferSpace.orientation;
+        mOutputSpace.setOrientation(outputState.framebufferSpace.getOrientation());
         mOutputDataspace = outputDataspace;
         mOrientation = orientation;
         mSkipCount = 0;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index f2978f9..ed235b8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -876,7 +876,7 @@
 
     mDisplay->editState().isEnabled = true;
     mDisplay->editState().usesClientComposition = false;
-    mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+    mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
     mDisplay->finishFrame({});
@@ -894,7 +894,7 @@
 
     gpuDisplay->editState().isEnabled = true;
     gpuDisplay->editState().usesClientComposition = false;
-    gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+    gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
     gpuDisplay->finishFrame({});
@@ -912,9 +912,8 @@
 
     gpuDisplay->editState().isEnabled = true;
     gpuDisplay->editState().usesClientComposition = false;
-    gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+    gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
-
     gpuDisplay->finishFrame({});
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index c8c6012..53b71b7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -158,7 +158,7 @@
         mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
         mLayerFEState.geomBufferTransform = TR_IDENT;
 
-        mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
+        mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
     }
 
     FloatRect calculateOutputSourceCrop() {
@@ -229,7 +229,7 @@
 
         mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.displaySpace.orientation = toRotation(entry.display);
+        mOutputState.displaySpace.setOrientation(toRotation(entry.display));
 
         EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
     }
@@ -243,7 +243,7 @@
 }
 
 TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
-    mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
+    mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540});
 
     const FloatRect expected{0.f, 0.f, 960.f, 540.f};
     EXPECT_THAT(calculateOutputSourceCrop(), expected);
@@ -265,7 +265,7 @@
         mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
         mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
 
-        mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
+        mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
         mOutputState.transform = ui::Transform{TR_IDENT};
     }
 
@@ -313,7 +313,7 @@
 }
 
 TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
-    mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
+    mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540});
     const Rect expected{0, 0, 960, 540};
     EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
@@ -399,7 +399,7 @@
 
         mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.displaySpace.orientation = toRotation(entry.display);
+        mOutputState.displaySpace.setOrientation(toRotation(entry.display));
         mOutputState.transform = ui::Transform{entry.display};
 
         const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
@@ -511,7 +511,7 @@
 
         mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.displaySpace.orientation = toRotation(entry.display);
+        mOutputState.displaySpace.setOrientation(toRotation(entry.display));
         mOutputState.transform = ui::Transform{entry.display};
 
         const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
@@ -942,7 +942,7 @@
     // This test simulates a scenario where displayInstallOrientation is set to
     // ROT_90. This only has an effect on the transform; orientation stays 0 (see
     // DisplayDevice::setProjection).
-    mOutputState.displaySpace.orientation = ui::ROTATION_0;
+    mOutputState.displaySpace.setOrientation(ui::ROTATION_0);
     mOutputState.transform = ui::Transform{TR_ROT_90};
     // Buffers are pre-rotated based on the transform hint (ROT_90); their
     // geomBufferTransform is set to the inverse transform.
@@ -1237,7 +1237,7 @@
 
         mLayerFEState.cursorFrame = kDefaultCursorFrame;
 
-        mOutputState.layerStackSpace.content = kDefaultDisplayViewport;
+        mOutputState.layerStackSpace.setContent(kDefaultDisplayViewport);
         mOutputState.transform = ui::Transform{kDefaultTransform};
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index e1c9aaa..bec7479 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -143,7 +143,8 @@
                 std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
         mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
 
-        mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
+        mOutput->editState().displaySpace.setBounds(
+                ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight()));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
     }
 
@@ -289,8 +290,10 @@
 
 TEST_F(OutputTest, setProjectionWorks) {
     const Rect displayRect{0, 0, 1000, 2000};
-    mOutput->editState().displaySpace.bounds = displayRect;
-    mOutput->editState().framebufferSpace.bounds = displayRect;
+    mOutput->editState().displaySpace.setBounds(
+            ui::Size(displayRect.getWidth(), displayRect.getHeight()));
+    mOutput->editState().framebufferSpace.setBounds(
+            ui::Size(displayRect.getWidth(), displayRect.getHeight()));
 
     const ui::Rotation orientation = ui::ROTATION_90;
     const Rect frame{50, 60, 100, 100};
@@ -298,28 +301,29 @@
 
     mOutput->setProjection(orientation, viewport, frame);
 
-    EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
-    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
-    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+    EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation());
+    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent());
+    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent());
 
     const auto state = mOutput->getState();
-    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
-    EXPECT_EQ(viewport, state.layerStackSpace.content);
-    EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+    EXPECT_EQ(viewport, state.layerStackSpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect());
 
-    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
-    EXPECT_EQ(frame, state.orientedDisplaySpace.content);
-    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+    EXPECT_EQ(frame, state.orientedDisplaySpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect());
 
-    EXPECT_EQ(displayRect, state.displaySpace.bounds);
-    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
-    EXPECT_EQ(orientation, state.displaySpace.orientation);
+    EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent());
+    EXPECT_EQ(orientation, state.displaySpace.getOrientation());
 
-    EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
-    EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content);
-    EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+    EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect());
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.getContent());
+    EXPECT_EQ(orientation, state.framebufferSpace.getOrientation());
 
-    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+    EXPECT_EQ(state.displaySpace.getContent(),
+              state.transform.transform(state.layerStackSpace.getContent()));
 
     EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint());
 }
@@ -327,8 +331,10 @@
 TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
     const Rect displayRect{0, 0, 1000, 2000};
     const Rect framebufferRect{0, 0, 500, 1000};
-    mOutput->editState().displaySpace.bounds = displayRect;
-    mOutput->editState().framebufferSpace.bounds = framebufferRect;
+    mOutput->editState().displaySpace.setBounds(
+            ui::Size(displayRect.getWidth(), displayRect.getHeight()));
+    mOutput->editState().framebufferSpace.setBounds(
+            ui::Size(framebufferRect.getWidth(), framebufferRect.getHeight()));
 
     const ui::Rotation orientation = ui::ROTATION_90;
     const Rect frame{50, 60, 100, 100};
@@ -336,28 +342,29 @@
 
     mOutput->setProjection(orientation, viewport, frame);
 
-    EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
-    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
-    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+    EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation());
+    EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent());
+    EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent());
 
     const auto state = mOutput->getState();
-    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
-    EXPECT_EQ(viewport, state.layerStackSpace.content);
-    EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+    EXPECT_EQ(viewport, state.layerStackSpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect());
 
-    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
-    EXPECT_EQ(frame, state.orientedDisplaySpace.content);
-    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+    EXPECT_EQ(frame, state.orientedDisplaySpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect());
 
-    EXPECT_EQ(displayRect, state.displaySpace.bounds);
-    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
-    EXPECT_EQ(orientation, state.displaySpace.orientation);
+    EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+    EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent());
+    EXPECT_EQ(orientation, state.displaySpace.getOrientation());
 
-    EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds);
-    EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content);
-    EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+    EXPECT_EQ(framebufferRect, state.framebufferSpace.getBoundsAsRect());
+    EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.getContent());
+    EXPECT_EQ(orientation, state.framebufferSpace.getOrientation());
 
-    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+    EXPECT_EQ(state.displaySpace.getContent(),
+              state.transform.transform(state.layerStackSpace.getContent()));
 }
 
 /*
@@ -365,16 +372,16 @@
  */
 
 TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
-    mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
-    mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
-    mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
-    mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
-    mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
-    mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
-    mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
-    mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
-    mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
-    mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
+    mOutput->editState().layerStackSpace.setContent(Rect(0, 0, 2000, 1000));
+    mOutput->editState().layerStackSpace.setBounds(ui::Size(2000, 1000));
+    mOutput->editState().orientedDisplaySpace.setContent(Rect(0, 0, 1800, 900));
+    mOutput->editState().orientedDisplaySpace.setBounds(ui::Size(2000, 1000));
+    mOutput->editState().framebufferSpace.setContent(Rect(0, 0, 900, 1800));
+    mOutput->editState().framebufferSpace.setBounds(ui::Size(1000, 2000));
+    mOutput->editState().framebufferSpace.setOrientation(ui::ROTATION_90);
+    mOutput->editState().displaySpace.setContent(Rect(0, 0, 900, 1800));
+    mOutput->editState().displaySpace.setBounds(ui::Size(1000, 2000));
+    mOutput->editState().displaySpace.setOrientation(ui::ROTATION_90);
 
     const ui::Size newDisplaySize{500, 1000};
 
@@ -385,20 +392,21 @@
     const auto state = mOutput->getState();
 
     const Rect displayRect(newDisplaySize);
-    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
-    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
-    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getContent());
+    EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getBoundsAsRect());
 
-    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
-    EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+    EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+    EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.getBoundsAsRect());
 
-    EXPECT_EQ(displayRect, state.displaySpace.bounds);
-    EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+    EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+    EXPECT_EQ(ui::ROTATION_90, state.displaySpace.getOrientation());
 
-    EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
-    EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+    EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect());
+    EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.getOrientation());
 
-    EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+    EXPECT_EQ(state.displaySpace.getContent(),
+              state.transform.transform(state.layerStackSpace.getContent()));
 
     EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
 }
@@ -562,7 +570,7 @@
 
     mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
 
-    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
+    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.getBoundsAsRect());
 }
 
 /*
@@ -571,7 +579,7 @@
 
 TEST_F(OutputTest, getDirtyRegion) {
     const Rect viewport{100, 200};
-    mOutput->editState().layerStackSpace.content = viewport;
+    mOutput->editState().layerStackSpace.setContent(viewport);
     mOutput->editState().dirtyRegion.set(50, 300);
 
     // The dirty region should be clipped to the display bounds.
@@ -1055,7 +1063,8 @@
     OutputRebuildLayerStacksTest() {
         mOutput.mState.isEnabled = true;
         mOutput.mState.transform = kIdentityTransform;
-        mOutput.mState.displaySpace.bounds = kOutputBounds;
+        mOutput.mState.displaySpace.setBounds(
+                ui::Size(kOutputBounds.getWidth(), kOutputBounds.getHeight()));
 
         mRefreshArgs.updatingOutputGeometryThisFrame = true;
 
@@ -1258,8 +1267,8 @@
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
                 .WillRepeatedly(Return(&mLayer.outputLayer));
 
-        mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300);
-        mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300);
+        mOutput.mState.displaySpace.setBounds(ui::Size(200, 300));
+        mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 200, 300));
         mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
 
         mLayer.layerFEState.isVisible = true;
@@ -1339,7 +1348,7 @@
 }
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
-    mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0);
+    mOutput.mState.displaySpace.setBounds(ui::Size(0, 0));
 
     ensureOutputLayerIfVisible();
 }
@@ -1536,7 +1545,7 @@
     mLayer.layerFEState.contentDirty = true;
     mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
 
-    mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
+    mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
     mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
 
     EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
@@ -1562,7 +1571,7 @@
     mLayer.layerFEState.contentDirty = true;
     mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
 
-    mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
+    mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
     mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
 
     EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
@@ -2977,11 +2986,11 @@
         mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
         mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
 
-        mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
-        mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
-        mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
-        mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
-        mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+        mOutput.mState.orientedDisplaySpace.setContent(kDefaultOutputFrame);
+        mOutput.mState.layerStackSpace.setContent(kDefaultOutputViewport);
+        mOutput.mState.framebufferSpace.setContent(kDefaultOutputDestinationClip);
+        mOutput.mState.displaySpace.setContent(kDefaultOutputDestinationClip);
+        mOutput.mState.displaySpace.setOrientation(kDefaultOutputOrientation);
         mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
         mOutput.mState.dataspace = kDefaultOutputDataspace;
         mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
@@ -3704,12 +3713,12 @@
 struct GenerateClientCompositionRequestsTest_ThreeLayers
       : public GenerateClientCompositionRequestsTest {
     GenerateClientCompositionRequestsTest_ThreeLayers() {
-        mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
-        mOutput.mState.layerStackSpace.content = kDisplayViewport;
-        mOutput.mState.displaySpace.content = kDisplayDestinationClip;
+        mOutput.mState.orientedDisplaySpace.setContent(kDisplayFrame);
+        mOutput.mState.layerStackSpace.setContent(kDisplayViewport);
+        mOutput.mState.displaySpace.setContent(kDisplayDestinationClip);
         mOutput.mState.transform =
                 ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)};
-        mOutput.mState.displaySpace.orientation = kDisplayOrientation;
+        mOutput.mState.displaySpace.setOrientation(kDisplayOrientation);
         mOutput.mState.needsFiltering = false;
         mOutput.mState.isSecure = false;
 
@@ -4309,11 +4318,11 @@
     const ui::Rotation kPortraitOrientation = ui::ROTATION_90;
     constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
 
-    mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
-    mOutput.mState.layerStackSpace.content = kPortraitViewport;
-    mOutput.mState.displaySpace.content = kPortraitDestinationClip;
+    mOutput.mState.orientedDisplaySpace.setContent(kPortraitFrame);
+    mOutput.mState.layerStackSpace.setContent(kPortraitViewport);
+    mOutput.mState.displaySpace.setContent(kPortraitDestinationClip);
     mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)};
-    mOutput.mState.displaySpace.orientation = kPortraitOrientation;
+    mOutput.mState.displaySpace.setOrientation(kPortraitOrientation);
     mOutput.mState.needsFiltering = false;
     mOutput.mState.isSecure = true;
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
index 704f5a8..19b3928 100644
--- a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
@@ -53,40 +53,40 @@
 
 TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) {
     ProjectionSpace space;
-    space.content = Rect(100, 200);
-    space.bounds = Rect(100, 200);
+    space.setContent(Rect(100, 200));
+    space.setBounds(ui::Size(100, 200));
 
     const ui::Transform identity;
     for (int rotation = 0; rotation <= 3; rotation++) {
-        space.orientation = ui::Rotation(rotation);
+        space.setOrientation(ui::Rotation(rotation));
         EXPECT_EQ(space.getTransform(space), identity);
     }
 }
 
 TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) {
     ProjectionSpace source;
-    source.content = Rect(10, 10, 20, 20);
-    source.bounds = Rect(100, 200);
+    source.setContent(Rect(10, 10, 20, 20));
+    source.setBounds(ui::Size(100, 200));
 
     ProjectionSpace dest;
-    dest.content = Rect(10, 20, 30, 20);
-    dest.bounds = source.bounds;
+    dest.setContent(Rect(10, 20, 30, 20));
+    dest.setBounds(source.getBounds());
 
     const auto transform = source.getTransform(dest);
-    EXPECT_EQ(transform.transform(source.content), dest.content);
+    EXPECT_EQ(transform.transform(source.getContent()), dest.getContent());
 }
 
 TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) {
     ProjectionSpace source;
-    source.content = Rect(0, 0, 20, 20);
-    source.bounds = Rect(100, 200);
+    source.setContent(Rect(0, 0, 20, 20));
+    source.setBounds(ui::Size(100, 200));
 
     ProjectionSpace dest;
-    dest.content = Rect(0, 0, 40, 30);
-    dest.bounds = source.bounds;
+    dest.setContent(Rect(0, 0, 40, 30));
+    dest.setBounds(source.getBounds());
 
     const auto transform = source.getTransform(dest);
-    EXPECT_EQ(transform.transform(source.content), dest.content);
+    EXPECT_EQ(transform.transform(source.getContent()), dest.getContent());
 }
 
 TEST(ProjectionSpaceTest, getSideStripTest) {
@@ -99,7 +99,7 @@
 
 void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) {
     const auto transform = source.getTransform(dest);
-    EXPECT_EQ(transform.transform(source.content), dest.content)
+    EXPECT_EQ(transform.transform(source.getContent()), dest.getContent())
             << "Source content doesn't map to dest content when projecting " << to_string(source)
             << " onto " << to_string(dest);
 
@@ -113,8 +113,8 @@
     //      |     |                |       *
     //      +-----+                +-------*
     // source(ROTATION_0)      dest (ROTATION_90)
-    const auto sourceStrip = getSideStrip(source.content, source.orientation);
-    const auto destStrip = getSideStrip(dest.content, dest.orientation);
+    const auto sourceStrip = getSideStrip(source.getContent(), source.getOrientation());
+    const auto destStrip = getSideStrip(dest.getContent(), dest.getOrientation());
     ASSERT_NE(sourceStrip, Rect::INVALID_RECT);
     ASSERT_NE(destStrip, Rect::INVALID_RECT);
     const auto mappedStrip = transform.transform(sourceStrip);
@@ -126,16 +126,16 @@
 
 TEST(ProjectionSpaceTest, getTransformWithOrienations) {
     ProjectionSpace source;
-    source.bounds = Rect(12, 13, 678, 789);
-    source.content = Rect(40, 50, 234, 343);
+    source.setBounds(ui::Size(666, 776));
+    source.setContent(Rect(40, 50, 234, 343));
     ProjectionSpace dest;
-    dest.bounds = Rect(17, 18, 879, 564);
-    dest.content = Rect(43, 52, 432, 213);
+    dest.setBounds(ui::Size(862, 546));
+    dest.setContent(Rect(43, 52, 432, 213));
 
     for (int sourceRot = 0; sourceRot <= 3; sourceRot++) {
-        source.orientation = ui::Rotation(sourceRot);
+        source.setOrientation(ui::Rotation(sourceRot));
         for (int destRot = 0; destRot <= 3; destRot++) {
-            dest.orientation = ui::Rotation(destRot);
+            dest.setOrientation(ui::Rotation(destRot));
             testTransform(source, dest);
         }
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 818334d..ecb05f8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -123,7 +123,7 @@
         // set up minimium params needed for rendering
         mOutputState.dataspace = ui::Dataspace::SRGB;
         mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
-        mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+        mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
         mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10));
     }
 }
@@ -349,9 +349,9 @@
                 const std::vector<const renderengine::LayerSettings*>& layers,
                 const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
                 base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
-        EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
-        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
-        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+        EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+        EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
                   displaySettings.orientation);
         EXPECT_EQ(0.5f, layers[0]->alpha);
         EXPECT_EQ(0.75f, layers[1]->alpha);
@@ -401,9 +401,9 @@
                 const std::vector<const renderengine::LayerSettings*>& layers,
                 const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
                 base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
-        EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
-        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
-        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+        EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+        EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
                   displaySettings.orientation);
         EXPECT_EQ(0.5f, layers[0]->alpha);
         EXPECT_EQ(0.75f, layers[1]->alpha);
@@ -456,9 +456,9 @@
                 const std::vector<const renderengine::LayerSettings*>& layers,
                 const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
                 base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
-        EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
-        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
-        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+        EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+        EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
                   displaySettings.orientation);
         EXPECT_EQ(0.5f, layers[0]->alpha);
         EXPECT_EQ(0.75f, layers[1]->alpha);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 0d5e362..9b0a75f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -143,7 +143,7 @@
         // set up minimium params needed for rendering
         mOutputState.dataspace = ui::Dataspace::SRGB;
         mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
-        mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+        mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
     }
 }
 
@@ -503,7 +503,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+    mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
@@ -516,7 +516,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
+    mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180);
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
@@ -544,7 +544,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
+    mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270);
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 802a17d..2f8a90f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -116,11 +116,11 @@
 }
 
 int DisplayDevice::getWidth() const {
-    return mCompositionDisplay->getState().displaySpace.bounds.getWidth();
+    return mCompositionDisplay->getState().displaySpace.getBounds().width;
 }
 
 int DisplayDevice::getHeight() const {
-    return mCompositionDisplay->getState().displaySpace.bounds.getHeight();
+    return mCompositionDisplay->getState().displaySpace.getBounds().height;
 }
 
 void DisplayDevice::setDisplayName(const std::string& displayName) {
@@ -262,13 +262,15 @@
     if (!orientedDisplaySpaceRect.isValid()) {
         // The destination frame can be invalid if it has never been set,
         // in that case we assume the whole display size.
-        orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
+        orientedDisplaySpaceRect =
+                getCompositionDisplay()->getState().displaySpace.getBoundsAsRect();
     }
 
     if (layerStackSpaceRect.isEmpty()) {
         // The layerStackSpaceRect can be invalid if it has never been set, in that case
         // we assume the whole framebuffer size.
-        layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
+        layerStackSpaceRect =
+                getCompositionDisplay()->getState().framebufferSpace.getBoundsAsRect();
         if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
             std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
         }
@@ -336,8 +338,8 @@
     return mCompositionDisplay->isSecure();
 }
 
-const Rect& DisplayDevice::getBounds() const {
-    return mCompositionDisplay->getState().displaySpace.bounds;
+const Rect DisplayDevice::getBounds() const {
+    return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
 }
 
 const Region& DisplayDevice::getUndefinedRegion() const {
@@ -361,11 +363,11 @@
 }
 
 const Rect& DisplayDevice::getLayerStackSpaceRect() const {
-    return mCompositionDisplay->getState().layerStackSpace.content;
+    return mCompositionDisplay->getState().layerStackSpace.getContent();
 }
 
 const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const {
-    return mCompositionDisplay->getState().orientedDisplaySpace.content;
+    return mCompositionDisplay->getState().orientedDisplaySpace.getContent();
 }
 
 bool DisplayDevice::hasWideColorGamut() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 43a6bd5..9de578f 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -156,8 +156,8 @@
     // Return true if intent is supported by the display.
     bool hasRenderIntent(ui::RenderIntent intent) const;
 
-    const Rect& getBounds() const;
-    const Rect& bounds() const { return getBounds(); }
+    const Rect getBounds() const;
+    const Rect bounds() const { return getBounds(); }
 
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
index 9fe30f8..3d24ecb 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
@@ -84,10 +84,10 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+        EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.getOrientation());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent());
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
@@ -96,13 +96,14 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+        EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.getOrientation());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
         // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
         // size width and height swapped
         EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
-                  compositionState.orientedDisplaySpace.content);
-        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+                  compositionState.orientedDisplaySpace.getContent());
+        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+                  compositionState.layerStackSpace.getContent());
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
@@ -111,9 +112,9 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+        EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.getOrientation());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent());
         EXPECT_EQ(false, compositionState.needsFiltering);
     }
 
@@ -122,13 +123,14 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+        EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.getOrientation());
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
         // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
         // size width and height swapped
         EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
-                  compositionState.orientedDisplaySpace.content);
-        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+                  compositionState.orientedDisplaySpace.getContent());
+        EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+                  compositionState.layerStackSpace.getContent());
         EXPECT_EQ(false, compositionState.needsFiltering);
     }