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

Commit 8a922eca authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Support service dependencies on Ravenwood.

Ravenwood test authors should only need to call setServicesRequired()
for the services they directly interact with, but often those
services will have indirect dependencies on other OS internals, and
those dependencies will evolve over time.

To handle this, we add the concept of dependencies to SystemService,
where a service declares the other services required to run.  (They
can choose to omit services from their dependencies where they
gracefully handle a missing service at runtime.)

Bug: 325506297
Test: atest RavenwoodServicesTest
Change-Id: I42187b3494fef499d619ad882891e832b0fd6bca
parent 16f7cdf2
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -149,6 +149,7 @@ java_library {
    installable: false,
    srcs: [":services.fakes-sources"],
    libs: [
        "ravenwood-framework",
        "services.core.ravenwood",
    ],
    jarjar_rules: ":ravenwood-services-jarjar-rules",
@@ -204,6 +205,7 @@ android_ravenwood_libgroup {
        // Provide runtime versions of utils linked in below
        "junit",
        "truth",
        "ravenwood-framework",
        "ravenwood-junit-impl",
        "mockito-ravenwood-prebuilt",
        "inline-mockito-ravenwood-prebuilt",
@@ -218,6 +220,7 @@ android_ravenwood_libgroup {
    libs: [
        "junit",
        "truth",
        "ravenwood-framework",
        "ravenwood-junit",
        "mockito-ravenwood-prebuilt",
        "inline-mockito-ravenwood-prebuilt",
+16 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ java_library {
    libs: [
        "android.test.mock",
        "framework-minus-apex.ravenwood",
        "ravenwood-framework",
        "services.core.ravenwood",
        "junit",
    ],
@@ -102,6 +103,21 @@ java_library {
    visibility: ["//visibility:public"],
}

// Library used to publish a handful of `android.ravenwood` APIs into
// the Ravenwood BCP; we don't want to publish these APIs into the BCP
// on physical devices, which is why this is a separate library
java_library {
    name: "ravenwood-framework",
    srcs: [
        "framework-src/**/*.java",
    ],
    libs: [
        "framework-minus-apex.ravenwood",
    ],
    sdk_version: "core_current",
    visibility: ["//visibility:public"],
}

java_host_for_device {
    name: "androidx.test.monitor-for-device",
    libs: [
+34 −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 android.ravenwood.example;

import android.annotation.SystemService;
import android.os.RemoteException;
import android.os.ServiceManager;

@SystemService(BlueManager.SERVICE_NAME)
public class BlueManager {
    public static final String SERVICE_NAME = "example_blue";

    public String getInterfaceDescriptor() {
        try {
            return ServiceManager.getService(SERVICE_NAME).getInterfaceDescriptor();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+34 −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 android.ravenwood.example;

import android.annotation.SystemService;
import android.os.RemoteException;
import android.os.ServiceManager;

@SystemService(RedManager.SERVICE_NAME)
public class RedManager {
    public static final String SERVICE_NAME = "example_red";

    public String getInterfaceDescriptor() {
        try {
            return ServiceManager.getService(SERVICE_NAME).getInterfaceDescriptor();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+16 −4
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.ravenwood.example.BlueManager;
import android.ravenwood.example.RedManager;
import android.test.mock.MockContext;
import android.util.ArrayMap;
import android.util.Singleton;
@@ -53,16 +55,23 @@ public class RavenwoodContext extends MockContext {
        mPackageName = packageName;
        mMainThread = mainThread;

        // Services provided by a typical shipping device
        registerService(ClipboardManager.class,
                Context.CLIPBOARD_SERVICE, asSingleton(() ->
                Context.CLIPBOARD_SERVICE, memoize(() ->
                        new ClipboardManager(this, getMainThreadHandler())));
        registerService(PermissionEnforcer.class,
                Context.PERMISSION_ENFORCER_SERVICE, () -> mEnforcer);
        registerService(SerialManager.class,
                Context.SERIAL_SERVICE, asSingleton(() ->
                Context.SERIAL_SERVICE, memoize(() ->
                        new SerialManager(this, ISerialManager.Stub.asInterface(
                                ServiceManager.getService(Context.SERIAL_SERVICE)))
                ));

        // Additional services we provide for testing purposes
        registerService(BlueManager.class,
                BlueManager.SERVICE_NAME, memoize(() -> new BlueManager()));
        registerService(RedManager.class,
                RedManager.SERVICE_NAME, memoize(() -> new RedManager()));
    }

    @Override
@@ -143,9 +152,12 @@ public class RavenwoodContext extends MockContext {
    }

    /**
     * Wrap the given {@link Supplier} to become a memoized singleton.
     * Wrap the given {@link Supplier} to become memoized.
     *
     * The underlying {@link Supplier} will only be invoked once, and that result will be cached
     * and returned for any future requests.
     */
    private static <T> Supplier<T> asSingleton(ThrowingSupplier<T> supplier) {
    private static <T> Supplier<T> memoize(ThrowingSupplier<T> supplier) {
        final Singleton<T> singleton = new Singleton<>() {
            @Override
            protected T create() {
Loading