Add deviceId to Context and allow for creating Contexts with different deviceId.

Applications may create contexts associated with the default host device or with any virtual device. The only way to create a non-default device context for now is via an explcit createDeviceContext call.

Bug: 239152561
Test: atest FrameworksCoreTests:ContextTest
Change-Id: If18dc7661fb232b0bc40121c723673ba880be9d5
This commit is contained in:
Vladimir Komsiyski
2022-10-05 14:09:28 +02:00
parent 0050a51961
commit 7c1be3298d
6 changed files with 118 additions and 0 deletions

View File

@@ -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);

View File

@@ -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,
@@ -2946,6 +2973,11 @@ class ContextImpl extends Context {
}
}
@Override
public int getDeviceId() {
return mDeviceId;
}
@Override
public DisplayAdjustments getDisplayAdjustments(int displayId) {
return mResources.getDisplayAdjustments();

View File

@@ -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;
@@ -6828,6 +6829,30 @@ public abstract class Context {
@DisplayContext
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.
*
@@ -7150,6 +7175,20 @@ public abstract class Context {
@SuppressWarnings("HiddenAbstractMethod")
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.
*

View File

@@ -1098,6 +1098,11 @@ public class ContextWrapper extends Context {
return mBase.createDisplayContext(display);
}
@Override
public @NonNull Context createDeviceContext(int deviceId) {
return mBase.createDeviceContext(deviceId);
}
@Override
@NonNull
public Context createWindowContext(@WindowType int type, @Nullable Bundle options) {
@@ -1166,6 +1171,11 @@ public class ContextWrapper extends Context {
mBase.updateDisplay(displayId);
}
@Override
public int getDeviceId() {
return mBase.getDeviceId();
}
@Override
public Context createDeviceProtectedStorageContext() {
return mBase.createDeviceProtectedStorageContext();

View File

@@ -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);

View File

@@ -838,6 +838,11 @@ public class MockContext extends Context {
throw new UnsupportedOperationException();
}
@Override
public @NonNull Context createDeviceContext(int deviceId) {
throw new UnsupportedOperationException();
}
@Override
public @NonNull Context createWindowContext(int type, Bundle options) {
throw new UnsupportedOperationException();
@@ -882,6 +887,11 @@ public class MockContext extends Context {
throw new UnsupportedOperationException();
}
@Override
public int getDeviceId() {
throw new UnsupportedOperationException();
}
@Override
public File[] getExternalFilesDirs(String type) {
throw new UnsupportedOperationException();