Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f931fee authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Create shared memory region for framework APIs" into main

parents 10b67750 369b0578
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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) {
@@ -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();
+1 −0
Original line number Diff line number Diff line
@@ -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();
+61 −59
Original line number Diff line number Diff line
@@ -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;
@@ -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());
@@ -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.
     */
+8 −0
Original line number Diff line number Diff line
@@ -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
}
+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