summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt2
-rw-r--r--core/java/android/app/ContextImpl.java32
-rw-r--r--core/java/android/content/Context.java39
-rw-r--r--core/java/android/content/ContextWrapper.java10
-rw-r--r--core/tests/coretests/src/android/content/ContextTest.java25
-rw-r--r--test-mock/src/android/test/mock/MockContext.java10
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();
}