diff options
-rw-r--r-- | core/api/current.txt | 2 | ||||
-rw-r--r-- | core/java/android/app/ContextImpl.java | 32 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 39 | ||||
-rw-r--r-- | core/java/android/content/ContextWrapper.java | 10 | ||||
-rw-r--r-- | core/tests/coretests/src/android/content/ContextTest.java | 25 | ||||
-rw-r--r-- | test-mock/src/android/test/mock/MockContext.java | 10 |
6 files changed, 118 insertions, 0 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 8138f6abea87..1d679ddafdf2 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9684,6 +9684,7 @@ package android.content { method public abstract android.content.Context createConfigurationContext(@NonNull android.content.res.Configuration); method @NonNull public android.content.Context createContext(@NonNull android.content.ContextParams); method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public android.content.Context createDeviceContext(int); method public abstract android.content.Context createDeviceProtectedStorageContext(); method @DisplayContext public abstract android.content.Context createDisplayContext(@NonNull android.view.Display); method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException; @@ -9714,6 +9715,7 @@ package android.content { method public abstract android.content.ContentResolver getContentResolver(); method public abstract java.io.File getDataDir(); method public abstract java.io.File getDatabasePath(String); + method public int getDeviceId(); method public abstract java.io.File getDir(String, int); method @Nullable public android.view.Display getDisplay(); method @Nullable public final android.graphics.drawable.Drawable getDrawable(@DrawableRes int); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 0e1b47f65561..10cdf5315b55 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -25,6 +25,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiContext; +import android.companion.virtual.VirtualDevice; +import android.companion.virtual.VirtualDeviceManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.AutofillOptions; @@ -241,6 +243,7 @@ class ContextImpl extends Context { @UnsupportedAppUsage private @NonNull Resources mResources; private @Nullable Display mDisplay; // may be null if invalid display or not initialized yet. + private int mDeviceId = VirtualDeviceManager.DEFAULT_DEVICE_ID; /** * If set to {@code true} the resources for this context will be configured for mDisplay which @@ -2700,6 +2703,30 @@ class ContextImpl extends Context { return context; } + @Override + public @NonNull Context createDeviceContext(int deviceId) { + boolean validDeviceId = deviceId == VirtualDeviceManager.DEFAULT_DEVICE_ID; + if (deviceId > VirtualDeviceManager.DEFAULT_DEVICE_ID) { + VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class); + if (vdm != null) { + List<VirtualDevice> virtualDevices = vdm.getVirtualDevices(); + validDeviceId = virtualDevices.stream().anyMatch(d -> d.getDeviceId() == deviceId); + } + } + if (!validDeviceId) { + throw new IllegalArgumentException( + "Not a valid ID of the default device or any virtual device: " + deviceId); + } + + ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams, + mAttributionSource.getAttributionTag(), + mAttributionSource.getNext(), + mSplitName, mToken, mUser, mFlags, mClassLoader, null); + + context.mDeviceId = deviceId; + return context; + } + @NonNull @Override public WindowContext createWindowContext(@WindowType int type, @@ -2947,6 +2974,11 @@ class ContextImpl extends Context { } @Override + public int getDeviceId() { + return mDeviceId; + } + + @Override public DisplayAdjustments getDisplayAdjustments(int displayId) { return mResources.getDisplayAdjustments(); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index e28df09e61d7..02cf3e68b20b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -46,6 +46,7 @@ import android.app.VrManager; import android.app.ambientcontext.AmbientContextManager; import android.app.people.PeopleManager; import android.app.time.TimeManager; +import android.companion.virtual.VirtualDeviceManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; @@ -6829,6 +6830,30 @@ public abstract class Context { public abstract Context createDisplayContext(@NonNull Display display); /** + * Returns a new {@code Context} object from the current context but with device association + * given by the {@code deviceId}. Each call to this method returns a new instance of a context + * object. Context objects are not shared; however, common state (such as the + * {@link ClassLoader} and other resources for the same configuration) can be shared, so the + * {@code Context} itself is lightweight. + * <p> + * Applications that run on virtual devices may use this method to access the default device + * capabilities and functionality (by passing + * {@link android.companion.virtual.VirtualDeviceManager#DEFAULT_DEVICE_ID}. Similarly, + * applications running on the default device may access the functionality of virtual devices. + * </p> + * @param deviceId The ID of the device to associate with this context. + * @return A context associated with the given device ID. + * + * @see #getDeviceId() + * @see VirtualDeviceManager#getVirtualDevices() + * @throws IllegalArgumentException if the given device ID is not a valid ID of the default + * device or a virtual device. + */ + public @NonNull Context createDeviceContext(int deviceId) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Creates a Context for a non-activity window. * * <p> @@ -7151,6 +7176,20 @@ public abstract class Context { public abstract void updateDisplay(int displayId); /** + * Get the device ID this context is associated with. Applications can use this method to + * determine whether they are running on a virtual device and identify that device. + * + * The device ID of the host device is + * {@link android.companion.virtual.VirtualDeviceManager#DEFAULT_DEVICE_ID} + * + * @return the ID of the device this context is associated with. + * @see #createDeviceContext(int) + */ + public int getDeviceId() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Indicates whether this Context is restricted. * * @return {@code true} if this Context is restricted, {@code false} otherwise. diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index e6549187e5c5..a1646a172521 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1099,6 +1099,11 @@ public class ContextWrapper extends Context { } @Override + public @NonNull Context createDeviceContext(int deviceId) { + return mBase.createDeviceContext(deviceId); + } + + @Override @NonNull public Context createWindowContext(@WindowType int type, @Nullable Bundle options) { return mBase.createWindowContext(type, options); @@ -1167,6 +1172,11 @@ public class ContextWrapper extends Context { } @Override + public int getDeviceId() { + return mBase.getDeviceId(); + } + + @Override public Context createDeviceProtectedStorageContext() { return mBase.createDeviceProtectedStorageContext(); } diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index e4a9ce59911b..bc356f80dca1 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -16,6 +16,7 @@ package android.content; +import static android.companion.virtual.VirtualDeviceManager.DEFAULT_DEVICE_ID; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; import static android.view.Display.DEFAULT_DISPLAY; @@ -210,6 +211,30 @@ public class ContextTest { assertFalse(context.isUiContext()); } + @Test + public void testDeviceIdForSystemContext() { + final Context systemContext = + ActivityThread.currentActivityThread().getSystemContext(); + + assertEquals(systemContext.getDeviceId(), DEFAULT_DEVICE_ID); + } + + @Test + public void testDeviceIdForSystemUiContext() { + final Context systemUiContext = + ActivityThread.currentActivityThread().getSystemUiContext(); + + assertEquals(systemUiContext.getDeviceId(), DEFAULT_DEVICE_ID); + } + + @Test + public void testDeviceIdForTestContext() { + final Context testContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals(testContext.getDeviceId(), DEFAULT_DEVICE_ID); + } + private Context createUiContext() { final Context appContext = ApplicationProvider.getApplicationContext(); final DisplayManager displayManager = appContext.getSystemService(DisplayManager.class); diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index 49daad324bd1..8fc8c7d162f4 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -839,6 +839,11 @@ public class MockContext extends Context { } @Override + public @NonNull Context createDeviceContext(int deviceId) { + throw new UnsupportedOperationException(); + } + + @Override public @NonNull Context createWindowContext(int type, Bundle options) { throw new UnsupportedOperationException(); } @@ -883,6 +888,11 @@ public class MockContext extends Context { } @Override + public int getDeviceId() { + throw new UnsupportedOperationException(); + } + + @Override public File[] getExternalFilesDirs(String type) { throw new UnsupportedOperationException(); } |