Create shared memory region for framework APIs

System server owns a read-write mapping to this region.
App processes are initialized with a read-only mapping.

Shared memory can be used to quickly read information that's
maintained by system server from any app process, without the
overhead of Binder, where the additional features offered by
Binder IPC are not required.

Demonstrate the use of shared memory to implement network clock.

go/ApplicationSharedMemory-bluedoc

Bug: 361329788
Bug: 365575551
Change-Id: Ic7e8231355392880b34a659e8531b3bba0614b27
Flag: com.android.internal.os.application_shared_memory_enabled
Flag: android.os.network_time_uses_shared_memory
Tested: ApplicationSharedMemoryTest
Tested: SystemClockNetworkTimeTest
NO_IFTTT=adding IFTTT
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index 3e58517..9f35c7b 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -32,6 +32,27 @@
     test_suites: ["device-tests"],
 }
 
+// Run just ApplicationSharedMemoryTest with ABI override for 32 bits.
+// This is to test that on systems that support multi-ABI,
+// ApplicationSharedMemory works in app processes launched with a different ABI
+// than that of the system processes.
+android_test {
+    name: "ApplicationSharedMemoryTest32",
+    team: "trendy_team_system_performance",
+    srcs: ["src/com/android/internal/os/ApplicationSharedMemoryTest.java"],
+    libs: ["android.test.runner.stubs.system"],
+    static_libs: [
+        "junit",
+        "androidx.test.rules",
+        "platform-test-annotations",
+    ],
+    manifest: "ApplicationSharedMemoryTest32/AndroidManifest.xml",
+    test_config: "ApplicationSharedMemoryTest32/AndroidTest.xml",
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
+
 android_ravenwood_test {
     name: "InternalTestsRavenwood",
     static_libs: [
@@ -45,3 +66,9 @@
     ],
     auto_gen_config: true,
 }
+
+java_test_helper_library {
+    name: "ApplicationSharedMemoryTestRule",
+    srcs: ["src/com/android/internal/os/ApplicationSharedMemoryTestRule.java"],
+    static_libs: ["junit"],
+}
diff --git a/tests/Internal/ApplicationSharedMemoryTest32/AndroidManifest.xml b/tests/Internal/ApplicationSharedMemoryTest32/AndroidManifest.xml
new file mode 100644
index 0000000..4e1058e
--- /dev/null
+++ b/tests/Internal/ApplicationSharedMemoryTest32/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="com.android.internal.tests">
+    <application
+        android:use32bitAbi="true"
+        android:multiArch="true">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="com.android.internal.tests"
+         android:label="Internal Tests"/>
+</manifest>
diff --git a/tests/Internal/ApplicationSharedMemoryTest32/AndroidTest.xml b/tests/Internal/ApplicationSharedMemoryTest32/AndroidTest.xml
new file mode 100644
index 0000000..9bde8b7
--- /dev/null
+++ b/tests/Internal/ApplicationSharedMemoryTest32/AndroidTest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+<configuration description="Runs tests for internal classes/utilities.">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="ApplicationSharedMemoryTest32.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="InternalTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.internal.tests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+            value="/data/user/0/com.android.internal.tests/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
\ No newline at end of file
diff --git a/tests/Internal/ApplicationSharedMemoryTest32/OWNERS b/tests/Internal/ApplicationSharedMemoryTest32/OWNERS
new file mode 100644
index 0000000..1ff3fac
--- /dev/null
+++ b/tests/Internal/ApplicationSharedMemoryTest32/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/PERFORMANCE_OWNERS
\ No newline at end of file
diff --git a/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
new file mode 100644
index 0000000..e3a129f
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import java.io.IOException;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.junit.Before;
+
+import java.io.FileDescriptor;
+
+/** Tests for {@link TimeoutRecord}. */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ApplicationSharedMemoryTest {
+
+    @Before
+    public void setUp() {
+        // Skip tests if the feature under test is disabled.
+        assumeTrue(Flags.applicationSharedMemoryEnabled());
+    }
+
+    /**
+     * Every application process, including ours, should have had an instance installed at this
+     * point.
+     */
+    @Test
+    public void hasInstance() {
+        // This shouldn't throw and shouldn't return null.
+        assertNotNull(ApplicationSharedMemory.getInstance());
+    }
+
+    /** Any app process should be able to read shared memory values. */
+    @Test
+    public void canRead() {
+        ApplicationSharedMemory instance = ApplicationSharedMemory.getInstance();
+        instance.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
+        // Don't actually care about the value of the above.
+    }
+
+    /** Application processes should not have mutable access. */
+    @Test
+    public void appInstanceNotMutable() {
+        ApplicationSharedMemory instance = ApplicationSharedMemory.getInstance();
+        try {
+            instance.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(17);
+            fail("Attempted mutation in an app process should throw");
+        } catch (Exception expected) {
+        }
+    }
+
+    /** Instances share memory if they share the underlying memory region. */
+    @Test
+    public void instancesShareMemory() throws IOException {
+        ApplicationSharedMemory instance1 = ApplicationSharedMemory.create();
+        ApplicationSharedMemory instance2 =
+                ApplicationSharedMemory.fromFileDescriptor(
+                        instance1.getFileDescriptor(), /* mutable= */ true);
+        ApplicationSharedMemory instance3 =
+                ApplicationSharedMemory.fromFileDescriptor(
+                        instance2.getReadOnlyFileDescriptor(), /* mutable= */ false);
+
+        instance1.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(17);
+        assertEquals(
+                17, instance1.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+        assertEquals(
+                17, instance2.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+        assertEquals(
+                17, instance3.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+
+        instance2.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(24);
+        assertEquals(
+                24, instance1.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+        assertEquals(
+                24, instance2.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+        assertEquals(
+                24, instance3.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+    }
+
+    /** Can't map read-only memory as mutable. */
+    @Test
+    public void readOnlyCantBeMutable() throws IOException {
+        ApplicationSharedMemory readWriteInstance = ApplicationSharedMemory.create();
+        FileDescriptor readOnlyFileDescriptor = readWriteInstance.getReadOnlyFileDescriptor();
+
+        try {
+            ApplicationSharedMemory.fromFileDescriptor(readOnlyFileDescriptor, /* mutable= */ true);
+            fail("Shouldn't be able to map read-only memory as mutable");
+        } catch (Exception expected) {
+        }
+    }
+}
diff --git a/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTestRule.java b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTestRule.java
new file mode 100644
index 0000000..ff2a461
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTestRule.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import com.android.internal.os.ApplicationSharedMemory;
+
+/** Test rule that sets up and tears down ApplicationSharedMemory for test. */
+public class ApplicationSharedMemoryTestRule implements TestRule {
+
+    private ApplicationSharedMemory mSavedInstance;
+
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                setup();
+                try {
+                    base.evaluate(); // Run the test
+                } finally {
+                    teardown();
+                }
+            }
+        };
+    }
+
+    private void setup() {
+        mSavedInstance = ApplicationSharedMemory.sInstance;
+        ApplicationSharedMemory.sInstance = ApplicationSharedMemory.create();
+    }
+
+    private void teardown() {
+        ApplicationSharedMemory.sInstance.close();
+        ApplicationSharedMemory.sInstance = mSavedInstance;
+        mSavedInstance = null;
+    }
+}