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

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

Merge "Introduce RavenwoodEnvironment" into main

parents 37a4e048 0d483c9e
Loading
Loading
Loading
Loading
+2 −9
Original line number Diff line number Diff line
@@ -15,12 +15,12 @@
 */
package android.os;

import android.platform.test.ravenwood.RavenwoodEnvironment;
import android.util.Log;

import com.android.ravenwood.common.RavenwoodInternalUtils;

import java.io.File;
import java.util.Objects;

/**
 * Ravenwood redirect target class from {@link Environment}.
@@ -33,13 +33,6 @@ public class Environment_ravenwood {
    private Environment_ravenwood() {
    }

    private static volatile File sRootDir;

    /** Called by ravenwood to initialize it. */
    public static void init(File rootDir) {
        sRootDir = Objects.requireNonNull(rootDir);
    }

    /** Redirected from the corresponding {@link Environment} method. */
    static String getEnvPath(String variableName) {
        return System.getProperty("ravenwood.env." + variableName);
@@ -68,7 +61,7 @@ public class Environment_ravenwood {
            throw new RuntimeException(
                    "Path doesn't start with a '/'. Actual=" + path);
        }
        var root = Objects.requireNonNull(sRootDir);
        var root = RavenwoodEnvironment.getInstance().getRootDir();
        if (path.startsWith(root.toString())) {
            if (VERBOSE) {
                Log.v(TAG, "translate: " + path + " is already translated");
+4 −8
Original line number Diff line number Diff line
@@ -172,21 +172,17 @@ public class Log_ravenwood {
    }

    /**
     * PID. We need to use a JNI method to get it, but JNI isn't initially ready.
     * Call {@link #onRavenwoodRuntimeNativeReady} to signal when JNI is ready, at which point
     * we set this field.
     * (We don't want to call native methods that may not be fully initialized even with a
     * try-catch, because partially initialized JNI methods could crash the process.)
     * PID. Set from RavenwoodDriver
     */
    private static volatile int sPid = 0;

    private static ThreadLocal<Integer> sTid = ThreadLocal.withInitial(Process::myTid);

    /**
     * Call it when {@link RavenwoodRuntimeNative} is usable.
     * Call it when PID is available.
     */
    public static void onRavenwoodRuntimeNativeReady() {
        sPid = Process.myPid();
    public static void setPid(int pid) {
        sPid = pid;
    }

    private static int getPid() {
+2 −0
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ public class RavenwoodInternalUtils {
    private RavenwoodInternalUtils() {
    }

    public static final String ANDROID_PACKAGE_NAME = "android";

    /**
     * If set to "1", we enable the verbose logging.
     *
+3 −4
Original line number Diff line number Diff line
@@ -63,7 +63,6 @@ public class RavenwoodContext extends RavenwoodBaseContext {
    private final ArrayMap<Class<?>, String> mClassToName = new ArrayMap<>();
    private final ArrayMap<String, Supplier<?>> mNameToFactory = new ArrayMap<>();

    private final File mDataDir;
    private final Supplier<Resources> mResourcesSupplier;

    private Application mAppContext;
@@ -89,8 +88,6 @@ public class RavenwoodContext extends RavenwoodBaseContext {
        mMainThread = mainThread;
        mResourcesSupplier = resourcesSupplier;

        mDataDir = RavenwoodDriver.sAppDataDir;

        // Services provided by a typical shipping device
        registerService(ClipboardManager.class,
                Context.CLIPBOARD_SERVICE, memoize(() ->
@@ -212,7 +209,9 @@ public class RavenwoodContext extends RavenwoodBaseContext {

    @Override
    public File getDataDir() {
        return mDataDir;
        // Create the dir lazily upon request. Note, "android" package doesn't have a
        // data dir. This would throw.
        return RavenwoodEnvironment.getInstance().getAppDataDir(mPackageName);
    }

    @Override
+46 −75
Original line number Diff line number Diff line
@@ -18,9 +18,9 @@ package android.platform.test.ravenwood;

import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.UserHandle.SYSTEM;
import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACKAGE_NAME;

import static com.android.modules.utils.ravenwood.RavenwoodHelper.RavenwoodInternal.RAVENWOOD_RUNTIME_PATH_JAVA_SYSPROP;
import static com.android.ravenwood.common.RavenwoodInternalUtils.ANDROID_PACKAGE_NAME;
import static com.android.ravenwood.common.RavenwoodInternalUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
import static com.android.ravenwood.common.RavenwoodInternalUtils.RAVENWOOD_INST_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodInternalUtils.RAVENWOOD_RESOURCE_APK;
@@ -52,10 +52,8 @@ import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Environment_ravenwood;
import android.os.HandlerThread;
import android.os.Handler_ravenwood;
import android.os.Looper;
import android.os.Looper_ravenwood;
import android.os.Message;
import android.os.Process_ravenwood;
@@ -74,7 +72,6 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.RuntimeInit;
import com.android.ravenwood.RavenwoodRuntimeNative;
import com.android.ravenwood.RavenwoodRuntimeState;
import com.android.ravenwood.common.RavenwoodInternalUtils;
import com.android.ravenwood.common.SneakyThrow;
import com.android.server.LocalServices;
@@ -85,9 +82,7 @@ import org.junit.internal.management.ManagementFactory;
import org.junit.runner.Description;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
@@ -117,7 +112,7 @@ public class RavenwoodDriver {
    private static final PrintStream sStdErr = System.err;

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

    private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
    private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
@@ -192,7 +187,7 @@ public class RavenwoodDriver {

    // TODO: expose packCallingIdentity function in libbinder and use it directly
    // See: packCallingIdentity in frameworks/native/libs/binder/IPCThreadState.cpp
    private static long packBinderIdentityToken(
    static long packBinderIdentityToken(
            boolean hasExplicitIdentity, int callingUid, int callingPid) {
        long res = ((long) callingUid << 32) | callingPid;
        if (hasExplicitIdentity) {
@@ -221,32 +216,16 @@ public class RavenwoodDriver {
    private static final String DEFAULT_INSTRUMENTATION_CLASS =
            "androidx.test.runner.AndroidJUnitRunner";

    static volatile Thread sTestThread;
    static volatile HandlerThread sMainThread;

    private static final int sMyPid = new Random().nextInt(100, 32768);
    private static int sTargetSdkLevel;

    private static String sTestPackageName;
    private static String sTargetPackageName;
    private static String sInstrumentationClass;

    static volatile RavenwoodContext sInstContext;
    static volatile RavenwoodContext sTargetContext;
    static volatile Application sTargetApplication;
    private static Instrumentation sInstrumentation;
    private static final long sCallingIdentity =
            packBinderIdentityToken(false, FIRST_APPLICATION_UID, sMyPid);

    static volatile File sRootDir;
    static volatile File sAppDataDir;

    /**
     * Initialize the global environment.
     */
    public static void globalInitOnce() {
        sTestThread = Thread.currentThread();
        Thread.currentThread().setName(TESTS_THREAD_NAME);
        Thread.currentThread().setName(TEST_THREAD_NAME);
        synchronized (sInitializationLock) {
            if (!sInitialized) {
                // globalInitOnce() is called from class initializer, which cause
@@ -282,7 +261,7 @@ public class RavenwoodDriver {
        }
    }

    private static void globalInitInner() throws IOException {
    private static void globalInitInner() throws Exception {
        // We haven't initialized liblog yet, so directly write to System.out here.
        RavenwoodInternalUtils.log(TAG, "globalInitInner()");

@@ -313,7 +292,6 @@ public class RavenwoodDriver {
        System.load(RavenwoodInternalUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));

        Log_ravenwood.setLogLevels(getLogTags());
        Log_ravenwood.onRavenwoodRuntimeNativeReady();

        // Do the basic set up for the android sysprops.
        RavenwoodSystemProperties.initialize();
@@ -356,22 +334,12 @@ public class RavenwoodDriver {
        System.setProperty("android.junit.runner",
                "androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner");

        loadRavenwoodProperties();

        assertMockitoVersion();

        sRootDir = Files.createTempDirectory("ravenwood-root-dir-").toFile();
        sAppDataDir = new File(sRootDir, "data/app/appdatadir/");
        sAppDataDir.mkdirs();
        Environment_ravenwood.init(sRootDir);

        Log.i(TAG, "TargetPackageName=" + sTargetPackageName);
        Log.i(TAG, "TestPackageName=" + sTestPackageName);
        Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel);
        Looper_ravenwood.sDispatcher = RavenwoodDriver::dispatchMessage;

        RavenwoodRuntimeState.sUid = FIRST_APPLICATION_UID;
        RavenwoodRuntimeState.sPid = sMyPid;
        RavenwoodRuntimeState.sTargetSdkLevel = sTargetSdkLevel;
        var env = createEnvironment();
        Log_ravenwood.setPid(env.getPid());

        RavenwoodUtils.sPendingExceptionThrower =
                RavenwoodDriver::maybeThrowPendingRecoverableUncaughtExceptionNoClear;
@@ -380,11 +348,6 @@ public class RavenwoodDriver {
            return null;
        };

        final var main = new HandlerThread(MAIN_THREAD_NAME);
        sMainThread = main;
        main.start();
        Looper_ravenwood.sDispatcher = RavenwoodDriver::dispatchMessage;
        Looper.setMainLooperForTest(main.getLooper());


        ServiceManager.init$ravenwood();
@@ -392,8 +355,7 @@ public class RavenwoodDriver {

        ActivityManager.init$ravenwood(SYSTEM.getIdentifier());

        final boolean isSelfInstrumenting =
                Objects.equals(sTestPackageName, sTargetPackageName);
        final boolean isSelfInstrumenting = env.isSelfInstrumenting();

        // This will load the resources from the apk set to `resource_apk` in the build file.
        // This is supposed to be the "target app"'s resources.
@@ -415,9 +377,9 @@ public class RavenwoodDriver {
        }

        sInstContext = new RavenwoodContext(
                sTestPackageName, main, instResourcesLoader);
                env.getInstPackageName(), env.getMainThread(), instResourcesLoader);
        sTargetContext = new RavenwoodContext(
                sTargetPackageName, main, targetResourcesLoader);
                env.getTargetPackageName(), env.getMainThread(), targetResourcesLoader);

        // Set up app context. App context is always created for the target app.
        var application = new Application();
@@ -438,25 +400,18 @@ public class RavenwoodDriver {

        final Supplier<Resources> systemResourcesLoader = () -> loadResources(null);

        var systemServerContext =
                new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
        var systemServerContext = new RavenwoodContext(
                ANDROID_PACKAGE_NAME, env.getMainThread(), systemResourcesLoader);

        var uiAutomation = new UiAutomation(sInstContext, new IUiAutomationConnection.Default());

        var instArgs = Bundle.EMPTY;
        RavenwoodUtils.runOnMainThreadSync(() -> {
            var instClassName = withDefault(sInstrumentationClass, DEFAULT_INSTRUMENTATION_CLASS);
            try {
                var clazz = Class.forName(instClassName);
                var clazz = Class.forName(env.getInstrumentationClass());
                sInstrumentation = (Instrumentation) clazz.getConstructor().newInstance();
            } catch (ReflectiveOperationException e) {
                if (sInstrumentationClass != null) {
                    // If the class is explicitly set, it is an error if the class is not found
                SneakyThrow.sneakyThrow(e);
                } else {
                    // Fallback to the platform instrumentation
                    sInstrumentation = new Instrumentation();
                }
            }

            sInstrumentation.basicInit(sInstContext, sTargetContext, uiAutomation);
@@ -466,7 +421,7 @@ public class RavenwoodDriver {

        RavenwoodSystemServer.init(systemServerContext);

        initializeCompatIds();
        initializeCompatIds(env);
    }

    /**
@@ -481,16 +436,29 @@ public class RavenwoodDriver {
        return logTags;
    }

    private static void loadRavenwoodProperties() {
        var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");
    private static RavenwoodEnvironment createEnvironment() throws Exception {
        final var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");

        sTargetSdkLevel = withDefault(
        // TODO(b/377765941) Read them from the manifest too?
        var targetSdkLevel = withDefault(
                parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL);
        sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME);
        sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName);
        sInstrumentationClass = props.get("instrumentationClass");
        var targetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME);
        var testPackageName = withDefault(props.get("instPackageName"), targetPackageName);
        var instrumentationClass = withDefault(props.get("instrumentationClass"),
                DEFAULT_INSTRUMENTATION_CLASS);

        // TODO(b/377765941) Read them from the manifest too?
        // TODO: Why do we use a random PID? We can get the real PID via JNI. Why not use that?

        return RavenwoodEnvironment.init(
                FIRST_APPLICATION_UID,
                new Random().nextInt(100, 32768),
                targetSdkLevel,
                targetPackageName,
                testPackageName,
                instrumentationClass,
                Thread.currentThread(), // Test thread
                new HandlerThread(MAIN_THREAD_NAME)
        );
    }

    private static void maybeThrowUnrecoverableUncaughtExceptionIfDetected() {
@@ -508,7 +476,8 @@ public class RavenwoodDriver {
        UiAutomation_ravenwood.reset();
        Process_ravenwood.reset();
        DeviceConfig_ravenwood.reset();
        Binder.restoreCallingIdentity(sCallingIdentity);
        Binder.restoreCallingIdentity(
                RavenwoodEnvironment.getInstance().getDefaultCallingIdentity());

        SystemProperties.clearChangeCallbacksForTest();

@@ -527,7 +496,8 @@ public class RavenwoodDriver {
        maybeThrowUnrecoverableUncaughtExceptionIfDetected();

        // TODO(b/375272444): this is a hacky workaround to ensure binder identity
        Binder.restoreCallingIdentity(sCallingIdentity);
        Binder.restoreCallingIdentity(
                RavenwoodEnvironment.getInstance().getDefaultCallingIdentity());

        scheduleTimeout();
    }
@@ -562,7 +532,7 @@ public class RavenwoodDriver {
        }
    }

    private static void initializeCompatIds() {
    private static void initializeCompatIds(RavenwoodEnvironment env) {
        // Set up compat-IDs for the app side.
        // TODO: Inside the system server, all the compat-IDs should be enabled,
        // Due to the `AppCompatCallbacks.install(new long[0], new long[0] ...` call in
@@ -570,8 +540,8 @@ public class RavenwoodDriver {

        // Compat framework only uses the package name and the target SDK level.
        ApplicationInfo appInfo = new ApplicationInfo();
        appInfo.packageName = sTargetPackageName;
        appInfo.targetSdkVersion = sTargetSdkLevel;
        appInfo.packageName = env.getTargetPackageName();
        appInfo.targetSdkVersion = env.getTargetSdkLevel();

        PlatformCompat platformCompat = null;
        try {
@@ -740,8 +710,9 @@ public class RavenwoodDriver {
                    Comparator.comparingLong(Thread::getId)).collect(Collectors.toList());

            // Put the test and the main thread at the top.
            var testThread = sTestThread;
            var mainThread = sMainThread;
            var env = RavenwoodEnvironment.getInstance();
            var testThread = env.getTestThread();
            var mainThread = env.getMainThread();
            if (mainThread != null) {
                threads.remove(mainThread);
                threads.add(0, mainThread);
Loading