Loading core/java/android/app/ActivityThread.java +12 −0 Original line number Diff line number Diff line Loading @@ -230,6 +230,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; import com.android.internal.os.ApplicationSharedMemory; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.DebugStore; Loading Loading @@ -1301,6 +1302,7 @@ public final class ActivityThread extends ClientTransactionHandler long[] disabledCompatChanges, long[] loggableCompatChanges, SharedMemory serializedSystemFontMap, FileDescriptor applicationSharedMemoryFd, long startRequestedElapsedTime, long startRequestedUptime) { if (services != null) { Loading Loading @@ -1329,6 +1331,16 @@ public final class ActivityThread extends ClientTransactionHandler ServiceManager.initServiceCache(services); } // This must be initialized as early as possible to ensure availability for any // downstream callers. if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()) { ApplicationSharedMemory instance = ApplicationSharedMemory.fromFileDescriptor( applicationSharedMemoryFd, /* mutable= */ false); instance.closeFileDescriptor(); ApplicationSharedMemory.setInstance(instance); } setCoreSettings(coreSettings); AppBindData data = new AppBindData(); Loading core/java/android/app/IApplicationThread.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -92,6 +92,7 @@ oneway interface IApplicationThread { in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions, in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges, in long[] loggableCompatChanges, in SharedMemory serializedSystemFontMap, in FileDescriptor applicationSharedMemoryFd, long startRequestedElapsedTime, long startRequestedUptime); void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs); void scheduleExit(); Loading core/java/android/os/SystemClock.java +61 −59 Original line number Diff line number Diff line Loading @@ -28,6 +28,8 @@ import android.location.LocationTime; import android.text.format.DateUtils; import android.util.Slog; import com.android.internal.os.ApplicationSharedMemory; import dalvik.annotation.optimization.CriticalNative; import java.time.Clock; Loading Loading @@ -323,30 +325,33 @@ public final class SystemClock { } /** * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized * using a remote network source outside the device. * <p> * While the time returned by {@link System#currentTimeMillis()} can be * adjusted by the user, the time returned by this method cannot be adjusted * by the user. * <p> * This performs no blocking network operations and returns values based on * a recent successful synchronization event; it will either return a valid * time or throw. * <p> * Note that synchronization may occur using an insecure network protocol, * so the returned time should not be used for security purposes. * The device may resynchronize with the same or different network source * at any time. Due to network delays, variations between servers, or local * (client side) clock drift, the accuracy of the returned times cannot be * guaranteed. In extreme cases, consecutive calls to {@link * #currentNetworkTimeMillis(ITimeDetectorService)} could return times that * are out of order. * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized using a remote * network source outside the device. * * <p>While the time returned by {@link System#currentTimeMillis()} can be adjusted by the user, * the time returned by this method cannot be adjusted by the user. * * <p>This performs no blocking network operations and returns values based on a recent * successful synchronization event; it will either return a valid time or throw. * * <p>Note that synchronization may occur using an insecure network protocol, so the returned * time should not be used for security purposes. The device may resynchronize with the same or * different network source at any time. Due to network delays, variations between servers, or * local (client side) clock drift, the accuracy of the returned times cannot be guaranteed. In * extreme cases, consecutive calls to {@link #currentNetworkTimeMillis()} could return times * that are out of order. * * @throws DateTimeException when no network time can be provided. * @hide */ public static long currentNetworkTimeMillis() { if (com.android.internal.os.Flags.applicationSharedMemoryEnabled() && Flags.networkTimeUsesSharedMemory()) { final long latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = ApplicationSharedMemory.getInstance() .getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(); return latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis + elapsedRealtime(); } else { ITimeDetectorService timeDetectorService = getITimeDetectorService(); if (timeDetectorService == null) { throw new RuntimeException(new DeadSystemException()); Loading @@ -370,27 +375,24 @@ public final class SystemClock { long deltaMs = currentMillis - time.getElapsedRealtimeMillis(); return time.getUnixEpochTimeMillis() + deltaMs; } } /** * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC, * synchronized using a remote network source outside the device. * <p> * While the time returned by {@link System#currentTimeMillis()} can be * adjusted by the user, the time returned by this method cannot be adjusted * by the user. * <p> * This performs no blocking network operations and returns values based on * a recent successful synchronization event; it will either return a valid * time or throw. * <p> * Note that synchronization may occur using an insecure network protocol, * so the returned time should not be used for security purposes. * The device may resynchronize with the same or different network source * at any time. Due to network delays, variations between servers, or local * (client side) clock drift, the accuracy of the returned times cannot be * guaranteed. In extreme cases, consecutive calls to {@link * Clock#millis()} on the returned {@link Clock} could return times that are * out of order. * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC, synchronized using a * remote network source outside the device. * * <p>While the time returned by {@link System#currentTimeMillis()} can be adjusted by the user, * the time returned by this method cannot be adjusted by the user. * * <p>This performs no blocking network operations and returns values based on a recent * successful synchronization event; it will either return a valid time or throw. * * <p>Note that synchronization may occur using an insecure network protocol, so the returned * time should not be used for security purposes. The device may resynchronize with the same or * different network source at any time. Due to network delays, variations between servers, or * local (client side) clock drift, the accuracy of the returned times cannot be guaranteed. In * extreme cases, consecutive calls to {@link Clock#millis()} on the returned {@link Clock} * could return times that are out of order. * * @throws DateTimeException when no network time can be provided. */ Loading core/java/android/os/flags.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -224,3 +224,11 @@ flag { is_exported: true bug: "366598445" } flag { name: "network_time_uses_shared_memory" namespace: "system_performance" description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory" bug: "361329788" is_exported: true } core/java/com/android/internal/os/ApplicationSharedMemory.java 0 → 100644 +296 −0 Original line number Diff line number Diff line /* * 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 android.annotation.NonNull; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.CriticalNative; import libcore.io.IoUtils; import java.io.FileDescriptor; import java.io.IOException; import java.time.DateTimeException; /** * This class is used to create and access a shared memory region. * * <p>The intended use case is that memory is shared between system processes and application * processes such that it's readable to all apps and writable only to system processes. * * <p>This shared memory region can be used as an alternative to Binder IPC for driving * communication between system processes and application processes at a lower latency and higher * throughput than Binder IPC can provide, under circumstances where the additional features of * Binder IPC are not required. * * <p>Unlike Binder IPC, shared memory doesn't support synchronous transactions and associated * ordering guarantees, client identity (and therefore caller permission checking), and access * auditing. Therefore it's not a suitable alternative to Binder IPC for most use cases. * * <p>Additionally, because the intended use case is to make this shared memory region readable to * all apps, it's not suitable for sharing sensitive data. * * @see {@link ApplicationSharedMemoryTestRule} for unit testing support. * @hide */ public class ApplicationSharedMemory implements AutoCloseable { // LINT.IfChange(invalid_network_time) public static final long INVALID_NETWORK_TIME = -1; // LINT.ThenChange(frameworks/base/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp:invalid_network_time) private static final boolean DEBUG = false; private static final String LOG_TAG = "ApplicationSharedMemory"; @VisibleForTesting public static ApplicationSharedMemory sInstance; /** Get the process-global instance. */ public static ApplicationSharedMemory getInstance() { ApplicationSharedMemory instance = sInstance; if (instance == null) { throw new IllegalStateException("ApplicationSharedMemory not initialized"); } return instance; } /** Set the process-global instance. */ public static void setInstance(ApplicationSharedMemory instance) { if (DEBUG) { Log.d(LOG_TAG, "setInstance: " + instance); } if (sInstance != null) { throw new IllegalStateException("ApplicationSharedMemory already initialized"); } sInstance = instance; } /** Allocate mutable shared memory region. */ public static ApplicationSharedMemory create() { if (DEBUG) { Log.d(LOG_TAG, "create"); } int fd = nativeCreate(); FileDescriptor fileDescriptor = new FileDescriptor(); fileDescriptor.setInt$(fd); final boolean mutable = true; long ptr = nativeMap(fd, mutable); nativeInit(ptr); return new ApplicationSharedMemory(fileDescriptor, mutable, ptr); } /** * Open shared memory region from a given {@link FileDescriptor}. * * @param fileDescriptor Handle to shared memory region. * @param mutable Whether the shared memory region is mutable. If true, will be mapped as * read-write memory. If false, will be mapped as read-only memory. Passing true (mutable) * if |pfd| is a handle to read-only memory will result in undefined behavior. */ public static ApplicationSharedMemory fromFileDescriptor( @NonNull FileDescriptor fileDescriptor, boolean mutable) { if (DEBUG) { Log.d(LOG_TAG, "fromFileDescriptor: " + fileDescriptor + " mutable: " + mutable); } long ptr = nativeMap(fileDescriptor.getInt$(), mutable); return new ApplicationSharedMemory(fileDescriptor, mutable, ptr); } /** * Allocate read-write shared memory region. * * @return File descriptor of the shared memory region. */ private static native int nativeCreate(); /** * Map the shared memory region. * * @param fd File descriptor of the shared memory region. * @param isMutable Whether the shared memory region is mutable. If true, will be mapped as * read-write memory. If false, will be mapped as read-only memory. * @return Pointer to the mapped shared memory region. */ private static native long nativeMap(int fd, boolean isMutable); /** * Initialize read-write shared memory region. * * @param Pointer to the mapped shared memory region. */ private static native void nativeInit(long ptr); /** * Unmap the shared memory region. * * @param ptr Pointer to the mapped shared memory region. */ private static native void nativeUnmap(long ptr); /** * If true, this object owns the read-write instance of the shared memory region. If false, this * object can only read. */ private final boolean mMutable; /** * Handle to the shared memory region. This can be send to other processes over Binder calls or * Intent extras. Recipients can use this handle to obtain read-only access to the shared memory * region. */ private FileDescriptor mFileDescriptor; /** Native pointer to the mapped shared memory region. */ private volatile long mPtr; ApplicationSharedMemory(@NonNull FileDescriptor fileDescriptor, boolean mutable, long ptr) { mFileDescriptor = fileDescriptor; mMutable = mutable; mPtr = ptr; } /** * Returns the file descriptor of the shared memory region. * * <p>This file descriptor retains the mutability properties of this object instance, and can be * sent over Binder IPC or Intent extras to another process to allow the remote process to map * the same shared memory region with the same access rights. * * @throws IllegalStateException if the file descriptor is closed. */ public FileDescriptor getFileDescriptor() { checkFileOpen(); return mFileDescriptor; } /** * Returns a read-only file descriptor of the shared memory region. This object can be sent over * Binder IPC or Intent extras to another process to allow the remote process to map the same * shared memory region with read-only access. * * @return a read-only handle to the shared memory region. * @throws IllegalStateException if the file descriptor is closed. */ public FileDescriptor getReadOnlyFileDescriptor() throws IOException { checkFileOpen(); FileDescriptor readOnlyFileDescriptor = new FileDescriptor(); int readOnlyFd = nativeDupAsReadOnly(mFileDescriptor.getInt$()); readOnlyFileDescriptor.setInt$(readOnlyFd); return readOnlyFileDescriptor; } /** Return a read-only duplicate of the file descriptor. */ private static native int nativeDupAsReadOnly(int fd); /** Set the latest network Unix Epoch minus realtime millis. */ public void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(long offset) { checkMutable(); nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(mPtr, offset); } /** Clear the latest network Unix Epoch minus realtime millis. */ public void clearLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() { checkMutable(); nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis( mPtr, INVALID_NETWORK_TIME); } @CriticalNative private static native void nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis( long ptr, long offset); /** * Get the latest network Unix Epoch minus realtime millis. * * @throws DateTimeException when no network time can be provided. */ public long getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() throws DateTimeException { checkMapped(); long offset = nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(mPtr); if (offset == INVALID_NETWORK_TIME) { throw new DateTimeException("No network time available"); } return offset; } @CriticalNative public static native long nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis( long ptr); /** * Close the associated file descriptor. * * <p>This method is safe to call if you never intend to pass the file descriptor to another * process, whether via {@link #getFileDescriptor()} or {@link #getReadOnlyFileDescriptor()}. * After calling this method, subsequent calls to {@link #getFileDescriptor()} or {@link * #getReadOnlyFileDescriptor()} will throw an {@link IllegalStateException}. */ public void closeFileDescriptor() { if (mFileDescriptor != null) { IoUtils.closeQuietly(mFileDescriptor); mFileDescriptor = null; } } public void close() { if (mPtr != 0) { nativeUnmap(mPtr); mPtr = 0; } if (mFileDescriptor != null) { IoUtils.closeQuietly(mFileDescriptor); mFileDescriptor = null; } } private void checkFileOpen() { if (mFileDescriptor == null) { throw new IllegalStateException("File descriptor is closed"); } } /** * Check that the shared memory region is mapped. * * @throws IllegalStateException if the shared memory region is not mapped. */ private void checkMapped() { if (mPtr == 0) { throw new IllegalStateException("Instance is closed"); } } /** * Check that the shared memory region is mapped and mutable. * * @throws IllegalStateException if the shared memory region is not mapped or not mutable. */ private void checkMutable() { checkMapped(); if (!mMutable) { throw new IllegalStateException("Not mutable"); } } } Loading
core/java/android/app/ActivityThread.java +12 −0 Original line number Diff line number Diff line Loading @@ -230,6 +230,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; import com.android.internal.os.ApplicationSharedMemory; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.DebugStore; Loading Loading @@ -1301,6 +1302,7 @@ public final class ActivityThread extends ClientTransactionHandler long[] disabledCompatChanges, long[] loggableCompatChanges, SharedMemory serializedSystemFontMap, FileDescriptor applicationSharedMemoryFd, long startRequestedElapsedTime, long startRequestedUptime) { if (services != null) { Loading Loading @@ -1329,6 +1331,16 @@ public final class ActivityThread extends ClientTransactionHandler ServiceManager.initServiceCache(services); } // This must be initialized as early as possible to ensure availability for any // downstream callers. if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()) { ApplicationSharedMemory instance = ApplicationSharedMemory.fromFileDescriptor( applicationSharedMemoryFd, /* mutable= */ false); instance.closeFileDescriptor(); ApplicationSharedMemory.setInstance(instance); } setCoreSettings(coreSettings); AppBindData data = new AppBindData(); Loading
core/java/android/app/IApplicationThread.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -92,6 +92,7 @@ oneway interface IApplicationThread { in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions, in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges, in long[] loggableCompatChanges, in SharedMemory serializedSystemFontMap, in FileDescriptor applicationSharedMemoryFd, long startRequestedElapsedTime, long startRequestedUptime); void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs); void scheduleExit(); Loading
core/java/android/os/SystemClock.java +61 −59 Original line number Diff line number Diff line Loading @@ -28,6 +28,8 @@ import android.location.LocationTime; import android.text.format.DateUtils; import android.util.Slog; import com.android.internal.os.ApplicationSharedMemory; import dalvik.annotation.optimization.CriticalNative; import java.time.Clock; Loading Loading @@ -323,30 +325,33 @@ public final class SystemClock { } /** * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized * using a remote network source outside the device. * <p> * While the time returned by {@link System#currentTimeMillis()} can be * adjusted by the user, the time returned by this method cannot be adjusted * by the user. * <p> * This performs no blocking network operations and returns values based on * a recent successful synchronization event; it will either return a valid * time or throw. * <p> * Note that synchronization may occur using an insecure network protocol, * so the returned time should not be used for security purposes. * The device may resynchronize with the same or different network source * at any time. Due to network delays, variations between servers, or local * (client side) clock drift, the accuracy of the returned times cannot be * guaranteed. In extreme cases, consecutive calls to {@link * #currentNetworkTimeMillis(ITimeDetectorService)} could return times that * are out of order. * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized using a remote * network source outside the device. * * <p>While the time returned by {@link System#currentTimeMillis()} can be adjusted by the user, * the time returned by this method cannot be adjusted by the user. * * <p>This performs no blocking network operations and returns values based on a recent * successful synchronization event; it will either return a valid time or throw. * * <p>Note that synchronization may occur using an insecure network protocol, so the returned * time should not be used for security purposes. The device may resynchronize with the same or * different network source at any time. Due to network delays, variations between servers, or * local (client side) clock drift, the accuracy of the returned times cannot be guaranteed. In * extreme cases, consecutive calls to {@link #currentNetworkTimeMillis()} could return times * that are out of order. * * @throws DateTimeException when no network time can be provided. * @hide */ public static long currentNetworkTimeMillis() { if (com.android.internal.os.Flags.applicationSharedMemoryEnabled() && Flags.networkTimeUsesSharedMemory()) { final long latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = ApplicationSharedMemory.getInstance() .getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(); return latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis + elapsedRealtime(); } else { ITimeDetectorService timeDetectorService = getITimeDetectorService(); if (timeDetectorService == null) { throw new RuntimeException(new DeadSystemException()); Loading @@ -370,27 +375,24 @@ public final class SystemClock { long deltaMs = currentMillis - time.getElapsedRealtimeMillis(); return time.getUnixEpochTimeMillis() + deltaMs; } } /** * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC, * synchronized using a remote network source outside the device. * <p> * While the time returned by {@link System#currentTimeMillis()} can be * adjusted by the user, the time returned by this method cannot be adjusted * by the user. * <p> * This performs no blocking network operations and returns values based on * a recent successful synchronization event; it will either return a valid * time or throw. * <p> * Note that synchronization may occur using an insecure network protocol, * so the returned time should not be used for security purposes. * The device may resynchronize with the same or different network source * at any time. Due to network delays, variations between servers, or local * (client side) clock drift, the accuracy of the returned times cannot be * guaranteed. In extreme cases, consecutive calls to {@link * Clock#millis()} on the returned {@link Clock} could return times that are * out of order. * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC, synchronized using a * remote network source outside the device. * * <p>While the time returned by {@link System#currentTimeMillis()} can be adjusted by the user, * the time returned by this method cannot be adjusted by the user. * * <p>This performs no blocking network operations and returns values based on a recent * successful synchronization event; it will either return a valid time or throw. * * <p>Note that synchronization may occur using an insecure network protocol, so the returned * time should not be used for security purposes. The device may resynchronize with the same or * different network source at any time. Due to network delays, variations between servers, or * local (client side) clock drift, the accuracy of the returned times cannot be guaranteed. In * extreme cases, consecutive calls to {@link Clock#millis()} on the returned {@link Clock} * could return times that are out of order. * * @throws DateTimeException when no network time can be provided. */ Loading
core/java/android/os/flags.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -224,3 +224,11 @@ flag { is_exported: true bug: "366598445" } flag { name: "network_time_uses_shared_memory" namespace: "system_performance" description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory" bug: "361329788" is_exported: true }
core/java/com/android/internal/os/ApplicationSharedMemory.java 0 → 100644 +296 −0 Original line number Diff line number Diff line /* * 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 android.annotation.NonNull; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.CriticalNative; import libcore.io.IoUtils; import java.io.FileDescriptor; import java.io.IOException; import java.time.DateTimeException; /** * This class is used to create and access a shared memory region. * * <p>The intended use case is that memory is shared between system processes and application * processes such that it's readable to all apps and writable only to system processes. * * <p>This shared memory region can be used as an alternative to Binder IPC for driving * communication between system processes and application processes at a lower latency and higher * throughput than Binder IPC can provide, under circumstances where the additional features of * Binder IPC are not required. * * <p>Unlike Binder IPC, shared memory doesn't support synchronous transactions and associated * ordering guarantees, client identity (and therefore caller permission checking), and access * auditing. Therefore it's not a suitable alternative to Binder IPC for most use cases. * * <p>Additionally, because the intended use case is to make this shared memory region readable to * all apps, it's not suitable for sharing sensitive data. * * @see {@link ApplicationSharedMemoryTestRule} for unit testing support. * @hide */ public class ApplicationSharedMemory implements AutoCloseable { // LINT.IfChange(invalid_network_time) public static final long INVALID_NETWORK_TIME = -1; // LINT.ThenChange(frameworks/base/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp:invalid_network_time) private static final boolean DEBUG = false; private static final String LOG_TAG = "ApplicationSharedMemory"; @VisibleForTesting public static ApplicationSharedMemory sInstance; /** Get the process-global instance. */ public static ApplicationSharedMemory getInstance() { ApplicationSharedMemory instance = sInstance; if (instance == null) { throw new IllegalStateException("ApplicationSharedMemory not initialized"); } return instance; } /** Set the process-global instance. */ public static void setInstance(ApplicationSharedMemory instance) { if (DEBUG) { Log.d(LOG_TAG, "setInstance: " + instance); } if (sInstance != null) { throw new IllegalStateException("ApplicationSharedMemory already initialized"); } sInstance = instance; } /** Allocate mutable shared memory region. */ public static ApplicationSharedMemory create() { if (DEBUG) { Log.d(LOG_TAG, "create"); } int fd = nativeCreate(); FileDescriptor fileDescriptor = new FileDescriptor(); fileDescriptor.setInt$(fd); final boolean mutable = true; long ptr = nativeMap(fd, mutable); nativeInit(ptr); return new ApplicationSharedMemory(fileDescriptor, mutable, ptr); } /** * Open shared memory region from a given {@link FileDescriptor}. * * @param fileDescriptor Handle to shared memory region. * @param mutable Whether the shared memory region is mutable. If true, will be mapped as * read-write memory. If false, will be mapped as read-only memory. Passing true (mutable) * if |pfd| is a handle to read-only memory will result in undefined behavior. */ public static ApplicationSharedMemory fromFileDescriptor( @NonNull FileDescriptor fileDescriptor, boolean mutable) { if (DEBUG) { Log.d(LOG_TAG, "fromFileDescriptor: " + fileDescriptor + " mutable: " + mutable); } long ptr = nativeMap(fileDescriptor.getInt$(), mutable); return new ApplicationSharedMemory(fileDescriptor, mutable, ptr); } /** * Allocate read-write shared memory region. * * @return File descriptor of the shared memory region. */ private static native int nativeCreate(); /** * Map the shared memory region. * * @param fd File descriptor of the shared memory region. * @param isMutable Whether the shared memory region is mutable. If true, will be mapped as * read-write memory. If false, will be mapped as read-only memory. * @return Pointer to the mapped shared memory region. */ private static native long nativeMap(int fd, boolean isMutable); /** * Initialize read-write shared memory region. * * @param Pointer to the mapped shared memory region. */ private static native void nativeInit(long ptr); /** * Unmap the shared memory region. * * @param ptr Pointer to the mapped shared memory region. */ private static native void nativeUnmap(long ptr); /** * If true, this object owns the read-write instance of the shared memory region. If false, this * object can only read. */ private final boolean mMutable; /** * Handle to the shared memory region. This can be send to other processes over Binder calls or * Intent extras. Recipients can use this handle to obtain read-only access to the shared memory * region. */ private FileDescriptor mFileDescriptor; /** Native pointer to the mapped shared memory region. */ private volatile long mPtr; ApplicationSharedMemory(@NonNull FileDescriptor fileDescriptor, boolean mutable, long ptr) { mFileDescriptor = fileDescriptor; mMutable = mutable; mPtr = ptr; } /** * Returns the file descriptor of the shared memory region. * * <p>This file descriptor retains the mutability properties of this object instance, and can be * sent over Binder IPC or Intent extras to another process to allow the remote process to map * the same shared memory region with the same access rights. * * @throws IllegalStateException if the file descriptor is closed. */ public FileDescriptor getFileDescriptor() { checkFileOpen(); return mFileDescriptor; } /** * Returns a read-only file descriptor of the shared memory region. This object can be sent over * Binder IPC or Intent extras to another process to allow the remote process to map the same * shared memory region with read-only access. * * @return a read-only handle to the shared memory region. * @throws IllegalStateException if the file descriptor is closed. */ public FileDescriptor getReadOnlyFileDescriptor() throws IOException { checkFileOpen(); FileDescriptor readOnlyFileDescriptor = new FileDescriptor(); int readOnlyFd = nativeDupAsReadOnly(mFileDescriptor.getInt$()); readOnlyFileDescriptor.setInt$(readOnlyFd); return readOnlyFileDescriptor; } /** Return a read-only duplicate of the file descriptor. */ private static native int nativeDupAsReadOnly(int fd); /** Set the latest network Unix Epoch minus realtime millis. */ public void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(long offset) { checkMutable(); nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(mPtr, offset); } /** Clear the latest network Unix Epoch minus realtime millis. */ public void clearLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() { checkMutable(); nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis( mPtr, INVALID_NETWORK_TIME); } @CriticalNative private static native void nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis( long ptr, long offset); /** * Get the latest network Unix Epoch minus realtime millis. * * @throws DateTimeException when no network time can be provided. */ public long getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() throws DateTimeException { checkMapped(); long offset = nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(mPtr); if (offset == INVALID_NETWORK_TIME) { throw new DateTimeException("No network time available"); } return offset; } @CriticalNative public static native long nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis( long ptr); /** * Close the associated file descriptor. * * <p>This method is safe to call if you never intend to pass the file descriptor to another * process, whether via {@link #getFileDescriptor()} or {@link #getReadOnlyFileDescriptor()}. * After calling this method, subsequent calls to {@link #getFileDescriptor()} or {@link * #getReadOnlyFileDescriptor()} will throw an {@link IllegalStateException}. */ public void closeFileDescriptor() { if (mFileDescriptor != null) { IoUtils.closeQuietly(mFileDescriptor); mFileDescriptor = null; } } public void close() { if (mPtr != 0) { nativeUnmap(mPtr); mPtr = 0; } if (mFileDescriptor != null) { IoUtils.closeQuietly(mFileDescriptor); mFileDescriptor = null; } } private void checkFileOpen() { if (mFileDescriptor == null) { throw new IllegalStateException("File descriptor is closed"); } } /** * Check that the shared memory region is mapped. * * @throws IllegalStateException if the shared memory region is not mapped. */ private void checkMapped() { if (mPtr == 0) { throw new IllegalStateException("Instance is closed"); } } /** * Check that the shared memory region is mapped and mutable. * * @throws IllegalStateException if the shared memory region is not mapped or not mutable. */ private void checkMutable() { checkMapped(); if (!mMutable) { throw new IllegalStateException("Not mutable"); } } }