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

Commit 1c3a1ec8 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Start using experimental API

To enable experimental API, use
```
export RAVENWOOD_ENABLE_EXP_API=1 # enable experimental api for all tests.
```
or
```
export RAVENWOOD_ENABLE_EXP_API_TestModuleName=1
```

Also rename the env flag to enable sysprop reads to
`RAVENWOOD_ALLOW_ANY_SYSPROP_READ` with support of
per-module flag, like `RAVENWOOD_ALLOW_ANY_SYSPROP_READ_TestModuleName=1`.

Bug: 292141694
Flag: TEST_ONLY
Test: $ANDROID_BUILD_TOP/frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh -s
Change-Id: I7ac83640d4cfce78ace9fc407eb57a50e370d777
parent 0ba3137a
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;
@@ -238,6 +240,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/"
@@ -285,8 +292,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<>();
@@ -158,7 +157,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