Loading core/java/android/content/Context.java +67 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ import android.provider.E2eeContactKeysManager; import android.provider.MediaStore; import android.ravenwood.annotation.RavenwoodKeep; import android.ravenwood.annotation.RavenwoodKeepPartialClass; import android.ravenwood.annotation.RavenwoodSupported.SupportType; import android.telephony.TelephonyRegistryManager; import android.util.AttributeSet; import android.view.Display; Loading Loading @@ -856,6 +857,8 @@ public abstract class Context { * @return an AssetManager instance for the application's package * @see #getResources() */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract AssetManager getAssets(); /** Loading @@ -869,9 +872,14 @@ public abstract class Context { * @return a Resources instance for the application's package * @see #getAssets() */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Resources getResources(); /** Return PackageManager instance to find global package information. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext", comment = "Almost no APIS on PackageManager are supported yet") public abstract PackageManager getPackageManager(); /** Return a ContentResolver instance for your application's package. */ Loading @@ -888,6 +896,8 @@ public abstract class Context { * * @return The main looper. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Looper getMainLooper(); /** Loading @@ -895,6 +905,8 @@ public abstract class Context { * thread associated with this context. This is the thread used to dispatch * calls to application components (activities, services, etc). */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public Executor getMainExecutor() { // This is pretty inefficient, which is why ContextImpl overrides it return new HandlerExecutor(new Handler(getMainLooper())); Loading Loading @@ -925,6 +937,8 @@ public abstract class Context { * if you forget to unregister, unbind, etc. * </ul> */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Context getApplicationContext(); /** Non-activity related autofill ids are unique in the app */ Loading Loading @@ -1092,6 +1106,8 @@ public abstract class Context { * * @param resid The style resource describing the theme. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract void setTheme(@StyleRes int resid); /** @hide Needed for some internal implementation... not public because Loading @@ -1105,6 +1121,8 @@ public abstract class Context { * Return the Theme object associated with this Context. */ @ViewDebug.ExportedProperty(deepExport = true) @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Resources.Theme getTheme(); /** Loading Loading @@ -1167,9 +1185,13 @@ public abstract class Context { /** * Return a class loader you can use to retrieve classes in this package. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract ClassLoader getClassLoader(); /** Return the name of this application's package. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract String getPackageName(); /** Loading @@ -1190,6 +1212,8 @@ public abstract class Context { * This is not generally intended for third party application developers. */ @NonNull @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public String getOpPackageName() { throw new RuntimeException("Not implemented. Must override in a subclass."); } Loading @@ -1201,6 +1225,9 @@ public abstract class Context { * * @return the attribution tag this context is for or {@code null} if this is the default. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext", comment = "Always returns null (for now)") public @Nullable String getAttributionTag() { return null; } Loading Loading @@ -1244,6 +1271,8 @@ public abstract class Context { * * @return String Path to the resources. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract String getPackageResourcePath(); /** Loading Loading @@ -1361,6 +1390,8 @@ public abstract class Context { * @see #deleteFile * @see java.io.FileInputStream#FileInputStream(String) */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract FileInputStream openFileInput(String name) throws FileNotFoundException; Loading @@ -1382,6 +1413,8 @@ public abstract class Context { * @see #deleteFile * @see java.io.FileOutputStream#FileOutputStream(String) */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract FileOutputStream openFileOutput(String name, @FileMode int mode) throws FileNotFoundException; Loading @@ -1400,6 +1433,8 @@ public abstract class Context { * @see #fileList * @see java.io.File#delete() */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract boolean deleteFile(String name); /** Loading @@ -1418,6 +1453,8 @@ public abstract class Context { * @see #getFilesDir * @see #getDir */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getFileStreamPath(String name); /** Loading @@ -1434,6 +1471,8 @@ public abstract class Context { * @removed */ @SuppressWarnings("HiddenAbstractMethod") @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getSharedPreferencesPath(String name); /** Loading @@ -1451,6 +1490,8 @@ public abstract class Context { * * @see ApplicationInfo#dataDir */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getDataDir(); /** Loading @@ -1468,6 +1509,8 @@ public abstract class Context { * @see #getFileStreamPath * @see #getDir */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getFilesDir(); /** Loading Loading @@ -1515,6 +1558,8 @@ public abstract class Context { * @see #getDir * @see android.app.backup.BackupAgent */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getNoBackupFilesDir(); /** Loading Loading @@ -1819,6 +1864,8 @@ public abstract class Context { * @see #getDir * @see #getExternalCacheDir */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getCacheDir(); /** Loading @@ -1840,6 +1887,8 @@ public abstract class Context { * * @return The path of the directory holding application code cache files. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getCodeCacheDir(); /** Loading Loading @@ -2054,6 +2103,8 @@ public abstract class Context { * * @see #openFileOutput(String, int) */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getDir(String name, @FileMode int mode); /** Loading Loading @@ -4601,6 +4652,8 @@ public abstract class Context { * @see #AUTHENTICATION_POLICY_SERVICE * @see android.security.authenticationpolicy.AuthenticationPolicyManager */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Object getSystemService(@ServiceName @NonNull String name); /** Loading Loading @@ -4661,6 +4714,8 @@ public abstract class Context { * @param serviceClass The class of the desired service. * @return The service name or null if the class is not a supported system service. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract @Nullable String getSystemServiceName(@NonNull Class<?> serviceClass); /** Loading Loading @@ -7695,6 +7750,8 @@ public abstract class Context { @NonNull @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @TestApi @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public @CanBeALL @CanBeCURRENT UserHandle getUser() { return android.os.Process.myUserHandle(); } Loading @@ -7705,6 +7762,8 @@ public abstract class Context { */ @UnsupportedAppUsage @TestApi @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public @CanBeALL @CanBeCURRENT @UserIdInt int getUserId() { return android.os.UserHandle.myUserId(); } Loading Loading @@ -8162,6 +8221,8 @@ public abstract class Context { * @see #registerDeviceIdChangeListener(Executor, IntConsumer) * @see #isUiContext() */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public int getDeviceId() { throw new RuntimeException("Not implemented. Must override in a subclass."); } Loading Loading @@ -8209,6 +8270,8 @@ public abstract class Context { * * @see #CONTEXT_RESTRICTED */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public boolean isRestricted() { return false; } Loading Loading @@ -8237,6 +8300,8 @@ public abstract class Context { * @hide */ @SuppressWarnings("HiddenAbstractMethod") @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract boolean canLoadUnsafeResources(); /** Loading Loading @@ -8306,6 +8371,8 @@ public abstract class Context { /** * @hide */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public Handler getMainThreadHandler() { throw new RuntimeException("Not implemented. Must override in a subclass."); } Loading core/java/android/content/pm/PackageManager.java +3 −0 Original line number Diff line number Diff line Loading @@ -89,6 +89,7 @@ import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.permission.PermissionManager; import android.ravenwood.annotation.RavenwoodSupported.SupportType; import android.telephony.TelephonyManager; import android.telephony.UiccCardInfo; import android.telephony.gba.GbaService; Loading Loading @@ -8493,6 +8494,8 @@ public abstract class PackageManager { * found on the system. */ @NonNull @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodPackageManager") public abstract InstrumentationInfo getInstrumentationInfo(@NonNull ComponentName className, @InstrumentationInfoFlags int flags) throws NameNotFoundException; Loading ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java +2 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.os.Looper; import android.os.PermissionEnforcer; import android.os.ServiceManager; import android.os.UserHandle; import android.ravenwood.annotation.RavenwoodSupported.RavenwoodProvidingImplementation; import android.ravenwood.example.BlueManager; import android.ravenwood.example.RedManager; import android.util.ArrayMap; Loading @@ -48,6 +49,7 @@ import java.nio.file.Files; import java.util.concurrent.Executor; import java.util.function.Supplier; @RavenwoodProvidingImplementation(target = Context.class) public class RavenwoodContext extends RavenwoodBaseContext { private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG; Loading ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodPackageManager.java +3 −0 Original line number Diff line number Diff line Loading @@ -17,7 +17,10 @@ package android.platform.test.ravenwood; import android.content.ComponentName; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageManager; import android.ravenwood.annotation.RavenwoodSupported.RavenwoodProvidingImplementation; @RavenwoodProvidingImplementation(target = PackageManager.class) public class RavenwoodPackageManager extends RavenwoodBasePackageManager { private final RavenwoodContext mContext; Loading ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodSupportedAnnotationTest.java 0 → 100644 +188 −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 com.android.ravenwoodtest.coretest; import static com.google.common.truth.Truth.assertThat; import android.ravenwood.annotation.RavenwoodSupported.RavenwoodProvidingImplementation; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; /** * Test to verify {@link android.ravenwood.annotation.RavenwoodSupported} * and {@link RavenwoodProvidingImplementation} are used correctly. */ public class RavenwoodSupportedAnnotationTest { private static final Class<? extends Annotation> RAVENWOOD_SUPPORTED_ANNOT = getThrowButSupportedAnnotation(); @Test public void testContext() throws Exception { check("android.content.Context", "android.platform.test.ravenwood.RavenwoodContext"); } @Test public void testPackageManager() throws Exception { check("android.content.pm.PackageManager", "android.platform.test.ravenwood.RavenwoodPackageManager"); } @SuppressWarnings("unchecked") private static Class<? extends Annotation> getThrowButSupportedAnnotation() { try { return (Class<? extends Annotation>) Class.forName( "com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrowButSupported"); } catch (Exception e) { throw new RuntimeException(e); } } private static void check(String frameworkClass, String subclass) throws Exception { check(Class.forName(frameworkClass), Class.forName(subclass)); } /** Class to hold a Method with its signature. */ private static class MethodRef { public final Method method; private final String mSignature; MethodRef(Method method) { this.method = method; this.mSignature = getMethodSignature(method); } String getSignature() { return mSignature; } } /** Build a simple method signature. (method name + arg types only) */ private static String getMethodSignature(Method method) { var sb = new StringBuilder(); sb.append(method.getName()); sb.append("("); for (var p : method.getParameterTypes()) { sb.append(p.getName()); sb.append(","); } sb.append(")"); return sb.toString(); } /** Return all methods (including super and interfaces' ones) from a given type. */ private static List<MethodRef> findAllMethods(Class<?> cls) throws Exception { var ret = new ArrayList<MethodRef>(); collectAllMethods(cls, ret); ret.sort(Comparator.comparing(MethodRef::getSignature)); return ret; } /** Collect methods from a given type. */ private static void collectAllMethods(Class<?> cls, List<MethodRef> methods) throws Exception { if (cls == null || cls == Object.class) { return; } for (var m : cls.getDeclaredMethods()) { methods.add(new MethodRef(m)); } // Collect super methods. collectAllMethods(cls.getSuperclass(), methods); // Collect interface methods. for (var i : cls.getInterfaces()) { collectAllMethods(i, methods); } } /** * Find methods declared in {@code subclass} that's in {@code superMethods}, * meaning methods overriding the super methods. */ private static List<MethodRef> findOverridingMethods(Class<?> subclass, List<MethodRef> superMethods) { // Create a set of super method signatures. var superMethodSet = new HashSet<String>(); superMethods.forEach(superMethod -> superMethodSet.add(superMethod.getSignature())); var ret = new ArrayList<MethodRef>(); for (var m : subclass.getDeclaredMethods()) { var sig = getMethodSignature(m); if (superMethodSet.contains(sig)) { ret.add(new MethodRef(m)); } } return ret; } /** Convert a method list to a single string for diffing. */ private static String toMethodListString(List<MethodRef> methods) { var sb = new StringBuilder(); for (var m : methods) { sb.append(m.getSignature()); sb.append("\n"); } // If no methods are found, there's something wrong. assertThat(sb.length()).isGreaterThan(0); return sb.toString(); } /** * Actual test method * @param frameworkClass target (base) class * @param subclass subclass that provides the implementation. */ private static void check(Class<?> frameworkClass, Class<?> subclass) throws Exception { // First, check the class's annotation too. var anot = subclass.getAnnotation(RavenwoodProvidingImplementation.class); assertThat(anot).isNotNull(); assertThat(anot.target()).isEqualTo(frameworkClass); // List all the methods from the target class. var frameworkMethods = findAllMethods(frameworkClass); // Extract only methods with @RavenwoodSupported. var supportedFrameworkMethods = frameworkMethods.stream() .filter(m -> m.method.isAnnotationPresent(RAVENWOOD_SUPPORTED_ANNOT)) .collect(Collectors.toList()); // Methods in the subclass that var overridingMethods = findOverridingMethods(subclass, frameworkMethods); supportedFrameworkMethods.sort(Comparator.comparing(MethodRef::getSignature)); overridingMethods.sort(Comparator.comparing(MethodRef::getSignature)); // Then compare them. They should match. assertThat(toMethodListString(supportedFrameworkMethods)) .isEqualTo(toMethodListString(overridingMethods)); } } Loading
core/java/android/content/Context.java +67 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ import android.provider.E2eeContactKeysManager; import android.provider.MediaStore; import android.ravenwood.annotation.RavenwoodKeep; import android.ravenwood.annotation.RavenwoodKeepPartialClass; import android.ravenwood.annotation.RavenwoodSupported.SupportType; import android.telephony.TelephonyRegistryManager; import android.util.AttributeSet; import android.view.Display; Loading Loading @@ -856,6 +857,8 @@ public abstract class Context { * @return an AssetManager instance for the application's package * @see #getResources() */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract AssetManager getAssets(); /** Loading @@ -869,9 +872,14 @@ public abstract class Context { * @return a Resources instance for the application's package * @see #getAssets() */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Resources getResources(); /** Return PackageManager instance to find global package information. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext", comment = "Almost no APIS on PackageManager are supported yet") public abstract PackageManager getPackageManager(); /** Return a ContentResolver instance for your application's package. */ Loading @@ -888,6 +896,8 @@ public abstract class Context { * * @return The main looper. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Looper getMainLooper(); /** Loading @@ -895,6 +905,8 @@ public abstract class Context { * thread associated with this context. This is the thread used to dispatch * calls to application components (activities, services, etc). */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public Executor getMainExecutor() { // This is pretty inefficient, which is why ContextImpl overrides it return new HandlerExecutor(new Handler(getMainLooper())); Loading Loading @@ -925,6 +937,8 @@ public abstract class Context { * if you forget to unregister, unbind, etc. * </ul> */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Context getApplicationContext(); /** Non-activity related autofill ids are unique in the app */ Loading Loading @@ -1092,6 +1106,8 @@ public abstract class Context { * * @param resid The style resource describing the theme. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract void setTheme(@StyleRes int resid); /** @hide Needed for some internal implementation... not public because Loading @@ -1105,6 +1121,8 @@ public abstract class Context { * Return the Theme object associated with this Context. */ @ViewDebug.ExportedProperty(deepExport = true) @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Resources.Theme getTheme(); /** Loading Loading @@ -1167,9 +1185,13 @@ public abstract class Context { /** * Return a class loader you can use to retrieve classes in this package. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract ClassLoader getClassLoader(); /** Return the name of this application's package. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract String getPackageName(); /** Loading @@ -1190,6 +1212,8 @@ public abstract class Context { * This is not generally intended for third party application developers. */ @NonNull @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public String getOpPackageName() { throw new RuntimeException("Not implemented. Must override in a subclass."); } Loading @@ -1201,6 +1225,9 @@ public abstract class Context { * * @return the attribution tag this context is for or {@code null} if this is the default. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext", comment = "Always returns null (for now)") public @Nullable String getAttributionTag() { return null; } Loading Loading @@ -1244,6 +1271,8 @@ public abstract class Context { * * @return String Path to the resources. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract String getPackageResourcePath(); /** Loading Loading @@ -1361,6 +1390,8 @@ public abstract class Context { * @see #deleteFile * @see java.io.FileInputStream#FileInputStream(String) */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract FileInputStream openFileInput(String name) throws FileNotFoundException; Loading @@ -1382,6 +1413,8 @@ public abstract class Context { * @see #deleteFile * @see java.io.FileOutputStream#FileOutputStream(String) */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract FileOutputStream openFileOutput(String name, @FileMode int mode) throws FileNotFoundException; Loading @@ -1400,6 +1433,8 @@ public abstract class Context { * @see #fileList * @see java.io.File#delete() */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract boolean deleteFile(String name); /** Loading @@ -1418,6 +1453,8 @@ public abstract class Context { * @see #getFilesDir * @see #getDir */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getFileStreamPath(String name); /** Loading @@ -1434,6 +1471,8 @@ public abstract class Context { * @removed */ @SuppressWarnings("HiddenAbstractMethod") @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getSharedPreferencesPath(String name); /** Loading @@ -1451,6 +1490,8 @@ public abstract class Context { * * @see ApplicationInfo#dataDir */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getDataDir(); /** Loading @@ -1468,6 +1509,8 @@ public abstract class Context { * @see #getFileStreamPath * @see #getDir */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getFilesDir(); /** Loading Loading @@ -1515,6 +1558,8 @@ public abstract class Context { * @see #getDir * @see android.app.backup.BackupAgent */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getNoBackupFilesDir(); /** Loading Loading @@ -1819,6 +1864,8 @@ public abstract class Context { * @see #getDir * @see #getExternalCacheDir */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getCacheDir(); /** Loading @@ -1840,6 +1887,8 @@ public abstract class Context { * * @return The path of the directory holding application code cache files. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getCodeCacheDir(); /** Loading Loading @@ -2054,6 +2103,8 @@ public abstract class Context { * * @see #openFileOutput(String, int) */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract File getDir(String name, @FileMode int mode); /** Loading Loading @@ -4601,6 +4652,8 @@ public abstract class Context { * @see #AUTHENTICATION_POLICY_SERVICE * @see android.security.authenticationpolicy.AuthenticationPolicyManager */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract Object getSystemService(@ServiceName @NonNull String name); /** Loading Loading @@ -4661,6 +4714,8 @@ public abstract class Context { * @param serviceClass The class of the desired service. * @return The service name or null if the class is not a supported system service. */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract @Nullable String getSystemServiceName(@NonNull Class<?> serviceClass); /** Loading Loading @@ -7695,6 +7750,8 @@ public abstract class Context { @NonNull @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @TestApi @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public @CanBeALL @CanBeCURRENT UserHandle getUser() { return android.os.Process.myUserHandle(); } Loading @@ -7705,6 +7762,8 @@ public abstract class Context { */ @UnsupportedAppUsage @TestApi @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public @CanBeALL @CanBeCURRENT @UserIdInt int getUserId() { return android.os.UserHandle.myUserId(); } Loading Loading @@ -8162,6 +8221,8 @@ public abstract class Context { * @see #registerDeviceIdChangeListener(Executor, IntConsumer) * @see #isUiContext() */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public int getDeviceId() { throw new RuntimeException("Not implemented. Must override in a subclass."); } Loading Loading @@ -8209,6 +8270,8 @@ public abstract class Context { * * @see #CONTEXT_RESTRICTED */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public boolean isRestricted() { return false; } Loading Loading @@ -8237,6 +8300,8 @@ public abstract class Context { * @hide */ @SuppressWarnings("HiddenAbstractMethod") @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public abstract boolean canLoadUnsafeResources(); /** Loading Loading @@ -8306,6 +8371,8 @@ public abstract class Context { /** * @hide */ @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodContext") public Handler getMainThreadHandler() { throw new RuntimeException("Not implemented. Must override in a subclass."); } Loading
core/java/android/content/pm/PackageManager.java +3 −0 Original line number Diff line number Diff line Loading @@ -89,6 +89,7 @@ import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.permission.PermissionManager; import android.ravenwood.annotation.RavenwoodSupported.SupportType; import android.telephony.TelephonyManager; import android.telephony.UiccCardInfo; import android.telephony.gba.GbaService; Loading Loading @@ -8493,6 +8494,8 @@ public abstract class PackageManager { * found on the system. */ @NonNull @android.ravenwood.annotation.RavenwoodSupported( type = SupportType.SUBCLASS, subclass = "RavenwoodPackageManager") public abstract InstrumentationInfo getInstrumentationInfo(@NonNull ComponentName className, @InstrumentationInfoFlags int flags) throws NameNotFoundException; Loading
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java +2 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.os.Looper; import android.os.PermissionEnforcer; import android.os.ServiceManager; import android.os.UserHandle; import android.ravenwood.annotation.RavenwoodSupported.RavenwoodProvidingImplementation; import android.ravenwood.example.BlueManager; import android.ravenwood.example.RedManager; import android.util.ArrayMap; Loading @@ -48,6 +49,7 @@ import java.nio.file.Files; import java.util.concurrent.Executor; import java.util.function.Supplier; @RavenwoodProvidingImplementation(target = Context.class) public class RavenwoodContext extends RavenwoodBaseContext { private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG; Loading
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodPackageManager.java +3 −0 Original line number Diff line number Diff line Loading @@ -17,7 +17,10 @@ package android.platform.test.ravenwood; import android.content.ComponentName; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageManager; import android.ravenwood.annotation.RavenwoodSupported.RavenwoodProvidingImplementation; @RavenwoodProvidingImplementation(target = PackageManager.class) public class RavenwoodPackageManager extends RavenwoodBasePackageManager { private final RavenwoodContext mContext; Loading
ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodSupportedAnnotationTest.java 0 → 100644 +188 −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 com.android.ravenwoodtest.coretest; import static com.google.common.truth.Truth.assertThat; import android.ravenwood.annotation.RavenwoodSupported.RavenwoodProvidingImplementation; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; /** * Test to verify {@link android.ravenwood.annotation.RavenwoodSupported} * and {@link RavenwoodProvidingImplementation} are used correctly. */ public class RavenwoodSupportedAnnotationTest { private static final Class<? extends Annotation> RAVENWOOD_SUPPORTED_ANNOT = getThrowButSupportedAnnotation(); @Test public void testContext() throws Exception { check("android.content.Context", "android.platform.test.ravenwood.RavenwoodContext"); } @Test public void testPackageManager() throws Exception { check("android.content.pm.PackageManager", "android.platform.test.ravenwood.RavenwoodPackageManager"); } @SuppressWarnings("unchecked") private static Class<? extends Annotation> getThrowButSupportedAnnotation() { try { return (Class<? extends Annotation>) Class.forName( "com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrowButSupported"); } catch (Exception e) { throw new RuntimeException(e); } } private static void check(String frameworkClass, String subclass) throws Exception { check(Class.forName(frameworkClass), Class.forName(subclass)); } /** Class to hold a Method with its signature. */ private static class MethodRef { public final Method method; private final String mSignature; MethodRef(Method method) { this.method = method; this.mSignature = getMethodSignature(method); } String getSignature() { return mSignature; } } /** Build a simple method signature. (method name + arg types only) */ private static String getMethodSignature(Method method) { var sb = new StringBuilder(); sb.append(method.getName()); sb.append("("); for (var p : method.getParameterTypes()) { sb.append(p.getName()); sb.append(","); } sb.append(")"); return sb.toString(); } /** Return all methods (including super and interfaces' ones) from a given type. */ private static List<MethodRef> findAllMethods(Class<?> cls) throws Exception { var ret = new ArrayList<MethodRef>(); collectAllMethods(cls, ret); ret.sort(Comparator.comparing(MethodRef::getSignature)); return ret; } /** Collect methods from a given type. */ private static void collectAllMethods(Class<?> cls, List<MethodRef> methods) throws Exception { if (cls == null || cls == Object.class) { return; } for (var m : cls.getDeclaredMethods()) { methods.add(new MethodRef(m)); } // Collect super methods. collectAllMethods(cls.getSuperclass(), methods); // Collect interface methods. for (var i : cls.getInterfaces()) { collectAllMethods(i, methods); } } /** * Find methods declared in {@code subclass} that's in {@code superMethods}, * meaning methods overriding the super methods. */ private static List<MethodRef> findOverridingMethods(Class<?> subclass, List<MethodRef> superMethods) { // Create a set of super method signatures. var superMethodSet = new HashSet<String>(); superMethods.forEach(superMethod -> superMethodSet.add(superMethod.getSignature())); var ret = new ArrayList<MethodRef>(); for (var m : subclass.getDeclaredMethods()) { var sig = getMethodSignature(m); if (superMethodSet.contains(sig)) { ret.add(new MethodRef(m)); } } return ret; } /** Convert a method list to a single string for diffing. */ private static String toMethodListString(List<MethodRef> methods) { var sb = new StringBuilder(); for (var m : methods) { sb.append(m.getSignature()); sb.append("\n"); } // If no methods are found, there's something wrong. assertThat(sb.length()).isGreaterThan(0); return sb.toString(); } /** * Actual test method * @param frameworkClass target (base) class * @param subclass subclass that provides the implementation. */ private static void check(Class<?> frameworkClass, Class<?> subclass) throws Exception { // First, check the class's annotation too. var anot = subclass.getAnnotation(RavenwoodProvidingImplementation.class); assertThat(anot).isNotNull(); assertThat(anot.target()).isEqualTo(frameworkClass); // List all the methods from the target class. var frameworkMethods = findAllMethods(frameworkClass); // Extract only methods with @RavenwoodSupported. var supportedFrameworkMethods = frameworkMethods.stream() .filter(m -> m.method.isAnnotationPresent(RAVENWOOD_SUPPORTED_ANNOT)) .collect(Collectors.toList()); // Methods in the subclass that var overridingMethods = findOverridingMethods(subclass, frameworkMethods); supportedFrameworkMethods.sort(Comparator.comparing(MethodRef::getSignature)); overridingMethods.sort(Comparator.comparing(MethodRef::getSignature)); // Then compare them. They should match. assertThat(toMethodListString(supportedFrameworkMethods)) .isEqualTo(toMethodListString(overridingMethods)); } }