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

Commit d98e448e authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "[Ravenwood] Decouple environment setup from RavenwoodConfig" into main

parents b1f8019b b870b936
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -133,9 +133,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase

        Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());

        // This is needed to make AndroidJUnit4ClassRunner happy.
        InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);

        // Hook point to allow more customization.
        runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);

+0 −85
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 android.platform.test.ravenwood;

import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import android.annotation.Nullable;
import android.app.ResourcesManager;
import android.content.res.Resources;
import android.view.DisplayAdjustments;

import java.io.File;
import java.util.HashMap;

/**
 * Used to store various states associated with {@link RavenwoodConfig} that's inly needed
 * in junit-impl.
 *
 * We don't want to put it in junit-src to avoid having to recompile all the downstream
 * dependencies after changing this class.
 *
 * All members must be called from the runner's main thread.
 */
public class RavenwoodConfigState {
    private static final String TAG = "RavenwoodConfigState";

    private final RavenwoodConfig mConfig;

    // TODO: Move the other contexts from RavenwoodConfig to here too? They're used by
    // RavenwoodRule too, but RavenwoodRule can probably use InstrumentationRegistry?
    RavenwoodContext mSystemServerContext;

    public RavenwoodConfigState(RavenwoodConfig config) {
        mConfig = config;
    }

    /** Map from path -> resources. */
    private final HashMap<File, Resources> mCachedResources = new HashMap<>();

    /**
     * Load {@link Resources} from an APK, with cache.
     */
    public Resources loadResources(@Nullable File apkPath) {
        var cached = mCachedResources.get(apkPath);
        if (cached != null) {
            return cached;
        }

        var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK);

        assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile());

        final String path = fileToLoad.getAbsolutePath();
        final var emptyPaths = new String[0];

        ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths);

        final var ret = ResourcesManager.getInstance().getResources(null, path,
                emptyPaths, emptyPaths, emptyPaths,
                emptyPaths, null, null,
                new DisplayAdjustments().getCompatibilityInfo(),
                RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null);

        assertNotNull(ret);

        mCachedResources.put(apkPath, ret);
        return ret;
    }
}
+4 −28
Original line number Diff line number Diff line
@@ -52,11 +52,6 @@ public final class RavenwoodRunnerState {
        mRunner = runner;
    }

    /**
     * The RavenwoodConfig used to configure the current Ravenwood environment.
     * This can either come from mConfig or mRule.
     */
    private RavenwoodConfig mCurrentConfig;
    /**
     * The RavenwoodConfig declared in the test class
     */
@@ -68,10 +63,6 @@ public final class RavenwoodRunnerState {
    private boolean mHasRavenwoodRule;
    private Description mMethodDescription;

    public RavenwoodConfig getConfig() {
        return mCurrentConfig;
    }

    public void enterTestRunner() {
        Log.i(TAG, "enterTestRunner: " + mRunner);

@@ -83,31 +74,19 @@ public final class RavenwoodRunnerState {
                fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
                        + " Suggest migrating to RavenwoodConfig.");
            }
            mCurrentConfig = mConfig;
        } else if (!mHasRavenwoodRule) {
            // If no RavenwoodConfig and no RavenwoodRule, use a default config
            mCurrentConfig = new RavenwoodConfig.Builder().build();
        }

        if (mCurrentConfig != null) {
            RavenwoodRuntimeEnvironmentController.init(mRunner);
        }
        RavenwoodRuntimeEnvironmentController.initForRunner();
    }

    public void enterTestClass() {
        Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());

        if (mCurrentConfig != null) {
            RavenwoodRuntimeEnvironmentController.init(mRunner);
        }
    }

    public void exitTestClass() {
        Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
        try {
            if (mCurrentConfig != null) {
                RavenwoodRuntimeEnvironmentController.reset();
            }
            RavenwoodRuntimeEnvironmentController.exitTestClass();
        } finally {
            mConfig = null;
            mRule = null;
@@ -116,11 +95,11 @@ public final class RavenwoodRunnerState {

    public void enterTestMethod(Description description) {
        mMethodDescription = description;
        RavenwoodRuntimeEnvironmentController.initForMethod();
    }

    public void exitTestMethod() {
        mMethodDescription = null;
        RavenwoodRuntimeEnvironmentController.reinit();
    }

    public void enterRavenwoodRule(RavenwoodRule rule) {
@@ -133,10 +112,7 @@ public final class RavenwoodRunnerState {
                    + " which is not supported.");
        }
        mRule = rule;
        if (mCurrentConfig == null) {
            mCurrentConfig = rule.getConfiguration();
        }
        RavenwoodRuntimeEnvironmentController.init(mRunner);
        RavenwoodRuntimeEnvironmentController.setSystemProperties(rule.mSystemProperties);
    }

    public void exitRavenwoodRule(RavenwoodRule rule) {
+107 −144
Original line number Diff line number Diff line
@@ -16,8 +16,11 @@

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.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
@@ -25,7 +28,9 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSIO
import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt;
import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -53,6 +58,7 @@ import android.provider.DeviceConfig_host;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import android.view.DisplayAdjustments;

import androidx.test.platform.app.InstrumentationRegistry;

@@ -62,7 +68,6 @@ import com.android.internal.os.RuntimeInit;
import com.android.ravenwood.RavenwoodRuntimeNative;
import com.android.ravenwood.RavenwoodRuntimeState;
import com.android.ravenwood.common.RavenwoodCommonUtils;
import com.android.ravenwood.common.RavenwoodRuntimeException;
import com.android.ravenwood.common.SneakyThrow;
import com.android.server.LocalServices;
import com.android.server.compat.PlatformCompat;
@@ -74,8 +79,10 @@ import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -85,8 +92,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;

/**
 * Responsible for initializing and de-initializing the environment, according to a
 * {@link RavenwoodConfig}.
 * Responsible for initializing and the environment.
 */
public class RavenwoodRuntimeEnvironmentController {
    private static final String TAG = "RavenwoodRuntimeEnvironmentController";
@@ -113,8 +119,6 @@ public class RavenwoodRuntimeEnvironmentController {

    private static ScheduledFuture<?> sPendingTimeout;

    private static long sOriginalIdentityToken = -1;

    /**
     * When enabled, attempt to detect uncaught exceptions from background threads.
     */
@@ -147,6 +151,10 @@ public class RavenwoodRuntimeEnvironmentController {
        return res;
    }

    /** Map from path -> resources. */
    private static final HashMap<File, Resources> sCachedResources = new HashMap<>();
    private static Set<String> sAdoptedPermissions = Collections.emptySet();

    private static final Object sInitializationLock = new Object();

    @GuardedBy("sInitializationLock")
@@ -155,15 +163,18 @@ public class RavenwoodRuntimeEnvironmentController {
    @GuardedBy("sInitializationLock")
    private static Throwable sExceptionFromGlobalInit;

    private static RavenwoodAwareTestRunner sRunner;
    private static RavenwoodSystemProperties sProps;

    private static final int DEFAULT_TARGET_SDK_LEVEL = VERSION_CODES.CUR_DEVELOPMENT;
    private static final String DEFAULT_PACKAGE_NAME = "com.android.ravenwoodtests.defaultname";

    private static final int sMyPid = new Random().nextInt(100, 32768);
    private static int sTargetSdkLevel;
    private static String sTestPackageName;
    private static String sTargetPackageName;
    private static Instrumentation sInstrumentation;
    private static final long sCallingIdentity =
            packBinderIdentityToken(false, FIRST_APPLICATION_UID, sMyPid);

    /**
     * Initialize the global environment.
@@ -182,7 +193,7 @@ public class RavenwoodRuntimeEnvironmentController {
                    Log.e(TAG, "globalInit() failed", th);

                    sExceptionFromGlobalInit = th;
                    throw th;
                    SneakyThrow.sneakyThrow(th);
                }
            } else {
                // Subsequent calls. If the first call threw, just throw the same error, to prevent
@@ -197,10 +208,13 @@ public class RavenwoodRuntimeEnvironmentController {
        }
    }

    private static void globalInitInner() {
    private static void globalInitInner() throws IOException {
        if (RAVENWOOD_VERBOSE_LOGGING) {
            Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH"));
        }
        if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
            Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
        }

        // Some process-wide initialization. (maybe redirect stdout/stderr)
        RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME);
@@ -251,82 +265,32 @@ public class RavenwoodRuntimeEnvironmentController {
        loadRavenwoodProperties();

        assertMockitoVersion();
    }

    private static void loadRavenwoodProperties() {
        var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");

        sTargetSdkLevel = withDefault(
                parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL);
        sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME);
        sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName);

        // TODO(b/377765941) Read them from the manifest too?
    }

    /**
     * Initialize the environment.
     */
    public static void init(RavenwoodAwareTestRunner runner) {
        if (RAVENWOOD_VERBOSE_LOGGING) {
            Log.v(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
        }
        if (sRunner == runner) {
            return;
        }
        if (sRunner != null) {
            reset();
        }
        sRunner = runner;
        try {
            initInner(runner.mState.getConfig());
        } catch (Exception th) {
            Log.e(TAG, "init() failed", th);

            RavenwoodCommonUtils.runIgnoringException(()-> reset());

            SneakyThrow.sneakyThrow(th);
        }
    }

    private static void initInner(RavenwoodConfig config) throws IOException {
        if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
            maybeThrowPendingUncaughtException(false);
            Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
        }

        config.mTargetPackageName = sTargetPackageName;
        config.mTestPackageName = sTestPackageName;
        config.mTargetSdkLevel = sTargetSdkLevel;

        Log.i(TAG, "TargetPackageName=" + sTargetPackageName);
        Log.i(TAG, "TestPackageName=" + sTestPackageName);
        Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel);

        RavenwoodRuntimeState.sUid = config.mUid;
        RavenwoodRuntimeState.sPid = config.mPid;
        RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel;
        sOriginalIdentityToken = Binder.clearCallingIdentity();
        reinit();
        setSystemProperties(config.mSystemProperties);
        RavenwoodRuntimeState.sUid = FIRST_APPLICATION_UID;
        RavenwoodRuntimeState.sPid = sMyPid;
        RavenwoodRuntimeState.sTargetSdkLevel = sTargetSdkLevel;

        ServiceManager.init$ravenwood();
        LocalServices.removeAllServicesForTest();

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

        final var main = new HandlerThread(MAIN_THREAD_NAME);
        main.start();
        Looper.setMainLooperForTest(main.getLooper());

        final boolean isSelfInstrumenting =
                Objects.equals(config.mTestPackageName, config.mTargetPackageName);
                Objects.equals(sTestPackageName, sTargetPackageName);

        // 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.
        final Supplier<Resources> targetResourcesLoader = () -> {
            var file = new File(RAVENWOOD_RESOURCE_APK);
            return config.mState.loadResources(file.exists() ? file : null);
            return loadResources(file.exists() ? file : null);
        };

        // Set up test context's (== instrumentation context's) resources.
@@ -337,18 +301,17 @@ public class RavenwoodRuntimeEnvironmentController {
        } else {
            instResourcesLoader = () -> {
                var file = new File(RAVENWOOD_INST_RESOURCE_APK);
                return config.mState.loadResources(file.exists() ? file : null);
                return loadResources(file.exists() ? file : null);
            };
        }

        var instContext = new RavenwoodContext(
                config.mTestPackageName, main, instResourcesLoader);
                sTestPackageName, main, instResourcesLoader);
        var targetContext = new RavenwoodContext(
                config.mTargetPackageName, main, targetResourcesLoader);
                sTargetPackageName, main, targetResourcesLoader);

        // Set up app context.
        var appContext = new RavenwoodContext(
                config.mTargetPackageName, main, targetResourcesLoader);
        var appContext = new RavenwoodContext(sTargetPackageName, main, targetResourcesLoader);
        appContext.setApplicationContext(appContext);
        if (isSelfInstrumenting) {
            instContext.setApplicationContext(appContext);
@@ -357,42 +320,65 @@ public class RavenwoodRuntimeEnvironmentController {
            // When instrumenting into another APK, the test context doesn't have an app context.
            targetContext.setApplicationContext(appContext);
        }
        config.mInstContext = instContext;
        config.mTargetContext = targetContext;

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

        config.mState.mSystemServerContext =
        var systemServerContext =
                new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);

        // Prepare other fields.
        config.mInstrumentation = new Instrumentation();
        config.mInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());
        InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY);
        sInstrumentation = new Instrumentation();
        sInstrumentation.basicInit(instContext, targetContext, null);
        InstrumentationRegistry.registerInstance(sInstrumentation, Bundle.EMPTY);

        RavenwoodSystemServer.init(config);
        RavenwoodSystemServer.init(systemServerContext);

        initializeCompatIds(config);
        initializeCompatIds();
    }

    private static void loadRavenwoodProperties() {
        var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");

        sTargetSdkLevel = withDefault(
                parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL);
        sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME);
        sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName);

        // TODO(b/377765941) Read them from the manifest too?
    }

    /**
     * Partially reset and initialize before each test class invocation
     */
    public static void initForRunner() {
        var targetContext = sInstrumentation.getTargetContext();
        var instContext = sInstrumentation.getContext();
        // We need to recreate the mock UiAutomation for each test class, because sometimes tests
        // will call Mockito.framework().clearInlineMocks() after execution.
        sInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());

        Process_ravenwood.reset();
        DeviceConfig_host.reset();
        Binder.restoreCallingIdentity(sCallingIdentity);

        if (ENABLE_TIMEOUT_STACKS) {
            sPendingTimeout = sTimeoutExecutor.schedule(
                    RavenwoodRuntimeEnvironmentController::dumpStacks,
                    TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
        }
        if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
            maybeThrowPendingUncaughtException(false);
        }
    }

    /**
     * Partially re-initialize after each test method invocation
     * Partially reset and initialize before each test method invocation
     */
    public static void reinit() {
        // sRunner could be null, if there was a failure in the initialization.
        if (sRunner != null) {
            var config = sRunner.mState.getConfig();
            Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
        }
    public static void initForMethod() {
        // TODO(b/375272444): this is a hacky workaround to ensure binder identity
        Binder.restoreCallingIdentity(sCallingIdentity);
    }

    private static void initializeCompatIds(RavenwoodConfig config) {
    private static void initializeCompatIds() {
        // 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
@@ -400,8 +386,8 @@ public class RavenwoodRuntimeEnvironmentController {

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

        PlatformCompat platformCompat = null;
        try {
@@ -418,65 +404,42 @@ public class RavenwoodRuntimeEnvironmentController {
    }

    /**
     * De-initialize.
     *
     * Note, we call this method when init() fails too, so this method should deal with
     * any partially-initialized states.
     * Load {@link Resources} from an APK, with cache.
     */
    public static void reset() {
        if (RAVENWOOD_VERBOSE_LOGGING) {
            Log.v(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
        }
        if (sRunner == null) {
            throw new RavenwoodRuntimeException("Internal error: reset() already called");
    private static Resources loadResources(@Nullable File apkPath) {
        var cached = sCachedResources.get(apkPath);
        if (cached != null) {
            return cached;
        }
        var config = sRunner.mState.getConfig();
        sRunner = null;

        if (ENABLE_TIMEOUT_STACKS) {
            sPendingTimeout.cancel(false);
        }
        var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK);

        RavenwoodSystemServer.reset(config);
        assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile());

        InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
        config.mInstrumentation = null;
        if (config.mInstContext != null) {
            ((RavenwoodContext) config.mInstContext).cleanUp();
            config.mInstContext = null;
        }
        if (config.mTargetContext != null) {
            ((RavenwoodContext) config.mTargetContext).cleanUp();
            config.mTargetContext = null;
        }
        if (config.mState.mSystemServerContext != null) {
            config.mState.mSystemServerContext.cleanUp();
        }
        final String path = fileToLoad.getAbsolutePath();
        final var emptyPaths = new String[0];

        if (Looper.getMainLooper() != null) {
            Looper.getMainLooper().quit();
        }
        Looper.clearMainLooperForTest();
        ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths);

        ActivityManager.reset$ravenwood();
        final var ret = ResourcesManager.getInstance().getResources(null, path,
                emptyPaths, emptyPaths, emptyPaths,
                emptyPaths, null, null,
                new DisplayAdjustments().getCompatibilityInfo(),
                RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null);

        LocalServices.removeAllServicesForTest();
        ServiceManager.reset$ravenwood();
        assertNotNull(ret);

        setSystemProperties(null);
        if (sOriginalIdentityToken != -1) {
            Binder.restoreCallingIdentity(sOriginalIdentityToken);
        sCachedResources.put(apkPath, ret);
        return ret;
    }
        RavenwoodRuntimeState.reset();
        Process_ravenwood.reset();
        DeviceConfig_host.reset();

        try {
            ResourcesManager.setInstance(null); // Better structure needed.
        } catch (Exception e) {
            // AOSP-CHANGE: AOSP doesn't support resources yet.
    /**
     * A callback when a test class finishes its execution, mostly only for debugging.
     */
    public static void exitTestClass() {
        if (ENABLE_TIMEOUT_STACKS) {
            sPendingTimeout.cancel(false);
        }

        if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
            maybeThrowPendingUncaughtException(true);
        }
@@ -524,7 +487,7 @@ public class RavenwoodRuntimeEnvironmentController {
    /**
     * Set the current configuration to the actual SystemProperties.
     */
    private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
    public static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
        SystemProperties.clearChangeCallbacksForTest();
        RavenwoodRuntimeNative.clearSystemProperties();
        if (systemProperties == null) systemProperties = new RavenwoodSystemProperties();
@@ -558,28 +521,28 @@ public class RavenwoodRuntimeEnvironmentController {

    // TODO: use the real UiAutomation class instead of a mock
    private static UiAutomation createMockUiAutomation() {
        final Set[] adoptedPermission = { Collections.emptySet() };
        sAdoptedPermissions = Collections.emptySet();
        var mock = mock(UiAutomation.class, inv -> {
            HostTestUtils.onThrowMethodCalled();
            return null;
        });
        doAnswer(inv -> {
            adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS;
            sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS;
            return null;
        }).when(mock).adoptShellPermissionIdentity();
        doAnswer(inv -> {
            if (inv.getArgument(0) == null) {
                adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS;
                sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS;
            } else {
                adoptedPermission[0] = Set.of(inv.getArguments());
                sAdoptedPermissions = (Set) Set.of(inv.getArguments());
            }
            return null;
        }).when(mock).adoptShellPermissionIdentity(any());
        doAnswer(inv -> {
            adoptedPermission[0] = Collections.emptySet();
            sAdoptedPermissions = Collections.emptySet();
            return null;
        }).when(mock).dropShellPermissionIdentity();
        doAnswer(inv -> adoptedPermission[0]).when(mock).getAdoptedShellPermissions();
        doAnswer(inv -> sAdoptedPermissions).when(mock).getAdoptedShellPermissions();
        return mock;
    }

+7 −10

File changed.

Preview size limit exceeded, changes collapsed.

Loading