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

Commit bffe9b69 authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "Start using experimental API" into main

parents 2d06f060 1c3a1ec8
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
import android.ravenwood.annotation.RavenwoodReplace;
import android.ravenwood.annotation.RavenwoodThrow;

/**
 * Class to interact with the Ravenwood environment.
@@ -122,4 +123,15 @@ public final class RavenwoodHelperBridge {
        @ChangeId
        public static final long TEST_COMPAT_ID_5 = 387558811L;
    }

    /**
     * We use it for testing the experimental API mechanism.
     *
     * This method has an "exp" in the policy file. For that to take effect, the method must be
     * originally unsupported, so we explicitly set "@RavenwoodThrow".
     */
    @RavenwoodThrow
    public static int forExperimentalApiTest() {
        return 1;
    }
}
+10 −2
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ import android.graphics.Typeface;
import android.icu.util.ULocale;
import android.os.Binder;
import android.os.Build;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process_ravenwood;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -200,6 +202,11 @@ public class RavenwoodDriver {
        // Do the basic set up for the android sysprops.
        RavenwoodSystemProperties.initialize();

        var mainThread = new HandlerThread(RavenwoodEnvironment.MAIN_THREAD_NAME);

        // Initialize the "environment".
        RavenwoodEnvironment.init(pid, mainThread);

        // Set ICU data file
        String icuData = getRavenwoodRuntimePath()
                + "ravenwood-data/"
@@ -247,8 +254,9 @@ public class RavenwoodDriver {
        LocalServices.removeAllServicesForTest();
        ActivityManager.init$ravenwood(SYSTEM.getIdentifier());

        // Initialize the "environment".
        RavenwoodEnvironment.init(pid);
        // Start the main thread.
        mainThread.start();
        Looper.setMainLooperForTest(mainThread.getLooper());

        // Start app lifecycle.
        RavenwoodAppDriver.init();
+20 −7
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.app.ResourcesManager;
import android.content.res.Resources;
import android.os.Build;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;
import android.view.DisplayAdjustments;

@@ -80,7 +79,7 @@ public final class RavenwoodEnvironment {
            new File(RavenwoodInternalUtils.getRavenwoodRuntimePath(),
                    "/ravenwood-data/ravenwood-empty-res.apk");

    private static final String MAIN_THREAD_NAME = "Ravenwood:Main";
    public static final String MAIN_THREAD_NAME = "Ravenwood:Main";
    private static final String TEST_THREAD_NAME = "Ravenwood:Test";

    private static final String RESOURCE_APK_DIR = "ravenwood-res-apks";
@@ -161,9 +160,6 @@ public final class RavenwoodEnvironment {
        mRootDir = Files.createTempDirectory("ravenwood-root-dir-").toFile();
        mRootDir.mkdirs();

        mainThread.start();
        Looper.setMainLooperForTest(mainThread.getLooper());

        Log.i(TAG, "TargetPackageName=" + mTargetPackageName);
        Log.i(TAG, "TestPackageName=" + mInstPackageName);
        Log.i(TAG, "TargetSdkLevel=" + mTargetSdkLevel);
@@ -172,7 +168,7 @@ public final class RavenwoodEnvironment {
    /**
     * Create and initialize the singleton instance. Also initializes {@link RavenwoodVmState}.
     */
    public static void init(int pid) throws IOException {
    public static void init(int pid, HandlerThread mainThread) throws IOException {
        final var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");

        // TODO(b/377765941) Read them from the manifest too?
@@ -198,7 +194,7 @@ public final class RavenwoodEnvironment {
                resourceApk,
                targetResourceApk,
                Thread.currentThread(), // Test thread,
                new HandlerThread(MAIN_THREAD_NAME));
                mainThread);
        if (!sInstance.compareAndSet(null, instance)) {
            throw new RuntimeException("RavenwoodEnvironment already initialized!");
        }
@@ -355,4 +351,21 @@ public final class RavenwoodEnvironment {
        }
        return RAVENWOOD_EMPTY_RESOURCES_APK;
    }

    /** Reads a per-module environmental variable. */
    public String getEnvVar(String keyName, String defValue) {
        var value = System.getenv(keyName + "_" + getTestModuleName());
        if (value == null) {
            value = System.getenv(keyName);
        }
        if (value == null) {
            value = defValue;
        }
        return value;
    }

    /** Reads a per-module environmental boolean variable. */
    public boolean getBoolEnvVar(String keyName) {
        return "1".equals(getEnvVar(keyName, ""));
    }
}
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 android.platform.test.ravenwood;

import com.android.internal.annotations.GuardedBy;

public class RavenwoodExperimentalApiChecker {
    private RavenwoodExperimentalApiChecker() {
    }

    private static final Object sLock = new Object();

    @GuardedBy("sLock")
    private static boolean sInitialized;

    @GuardedBy("sLock")
    private static boolean sExperimentalApiEnabled;

    public static boolean isExperimentalApiEnabled() {
        synchronized (sLock) {
            if (!sInitialized) {
                sExperimentalApiEnabled = RavenwoodEnvironment.getInstance()
                        .getBoolEnvVar("RAVENWOOD_ENABLE_EXP_API");
                sInitialized = true;
            }
            return sExperimentalApiEnabled;
        }
    }

    /**
     * Check if experimental APIs are enabled, and if not, throws
     * {@link RavenwoodUnsupportedApiException}.
     */
    public static void onExperimentalApiCalled(Class<?> clazz, String method, String desc) {
        onExperimentalApiCalled(2);
    }

    /**
     * Check if experimental APIs are enabled, and if not, throws
     * {@link RavenwoodUnsupportedApiException}.
     *
     * @param skipStackTraces the thrown {@link RavenwoodUnsupportedApiException} will skip
     * this many stack frames to make it look like it's thrown from the "real" missing API.
     */
    public static void onExperimentalApiCalled(int skipStackTraces) {
        if (isExperimentalApiEnabled()) {
            return;
        }
        throw new RavenwoodUnsupportedApiException().skipStackTraces(skipStackTraces);
    }


    private static void ensureCoreTest() {
        if (RavenwoodEnvironment.getInstance().getTestModuleName().equals("RavenwoodCoreTest")) {
            // Okay
            return;
        }
        throw new IllegalStateException("This method is only for internal testing");
    }

    /**
     * Override {@link #sExperimentalApiEnabled}. Only use it in internal testing.
     */
    public static void setExperimentalApiEnabledOnlyForTesting(boolean experimentalApiEnabled) {
        ensureCoreTest();
        sExperimentalApiEnabled = experimentalApiEnabled;
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -47,8 +47,7 @@ public final class RavenwoodRunnerState {
    private static final String RAVENWOOD_RULE_ERROR =
            "RavenwoodRule(s) are not executed in the correct order";

    private static final boolean ALLOW_ALL_SYSPROP_READS = "1".equals(
            System.getenv("RAVENWOOD_ALLOW_ALL_SYSPROP_READS"));
    private static final String ALLOW_ALL_SYSPROP_READ_ENV = "RAVENWOOD_ALLOW_ANY_SYSPROP_READ";

    private static final List<Pair<RavenwoodRule, RavenwoodPropertyState>> sActiveProperties =
            new ArrayList<>();
@@ -155,7 +154,8 @@ public final class RavenwoodRunnerState {
                || sActiveProperties.stream().anyMatch(p -> p.second.isKeyAccessible(key, write));

        if (!result) {
            if (ALLOW_ALL_SYSPROP_READS && !write) {
            if (RavenwoodEnvironment.getInstance().getBoolEnvVar(ALLOW_ALL_SYSPROP_READ_ENV)
                    && !write) {
                Log.w(TAG, "Unallow-listed property read detected: key=" + key);
                return;
            }
Loading