Loading core/api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -9879,6 +9879,7 @@ package android.content { method @Deprecated public abstract int getWallpaperDesiredMinimumHeight(); method @Deprecated public abstract int getWallpaperDesiredMinimumWidth(); method public abstract void grantUriPermission(String, android.net.Uri, int); method public boolean isDeviceContext(); method public abstract boolean isDeviceProtectedStorage(); method public boolean isRestricted(); method public boolean isUiContext(); Loading @@ -9894,6 +9895,7 @@ package android.content { method public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, @Nullable android.database.DatabaseErrorHandler); method @Deprecated public abstract android.graphics.drawable.Drawable peekWallpaper(); method public void registerComponentCallbacks(android.content.ComponentCallbacks); method public void registerDeviceIdChangeListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer); method @Nullable public abstract android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter); method @Nullable public abstract android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, int); method @Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler); Loading Loading @@ -9933,6 +9935,7 @@ package android.content { method public abstract boolean stopService(android.content.Intent); method public abstract void unbindService(@NonNull android.content.ServiceConnection); method public void unregisterComponentCallbacks(android.content.ComponentCallbacks); method public void unregisterDeviceIdChangeListener(@NonNull java.util.function.IntConsumer); method public abstract void unregisterReceiver(android.content.BroadcastReceiver); method public void updateServiceGroup(@NonNull android.content.ServiceConnection, int, int); field public static final String ACCESSIBILITY_SERVICE = "accessibility"; core/java/android/app/ContextImpl.java +135 −9 Original line number Diff line number Diff line Loading @@ -21,12 +21,14 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.StrictMode.vmIncorrectContextUseEnabled; import static android.view.WindowManager.LayoutParams.WindowType; import android.annotation.CallbackExecutor; 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.companion.virtual.VirtualDeviceParams; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.AutofillOptions; Loading Loading @@ -121,6 +123,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.IntConsumer; class ReceiverRestrictedContext extends ContextWrapper { @UnsupportedAppUsage Loading Loading @@ -257,6 +260,13 @@ class ContextImpl extends Context { /** @see Context#isConfigurationContext() */ private boolean mIsConfigurationBasedContext; /** * Indicates that this context was created with an explicit device ID association via * Context#createDeviceContext and under no circumstances will it ever change, even if * this context is not associated with a display id, or if the associated display id changes. */ private boolean mIsExplicitDeviceId = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final int mFlags; Loading Loading @@ -372,6 +382,24 @@ class ContextImpl extends Context { @ServiceInitializationState final int[] mServiceInitializationStateArray = new int[mServiceCache.length]; private final Object mDeviceIdListenerLock = new Object(); /** * List of listeners for deviceId changes and their associated Executor. * List is lazy-initialized on first registration */ @GuardedBy("mDeviceIdListenerLock") @Nullable private ArrayList<DeviceIdChangeListenerDelegate> mDeviceIdChangeListeners; private static class DeviceIdChangeListenerDelegate { final @NonNull IntConsumer mListener; final @NonNull Executor mExecutor; DeviceIdChangeListenerDelegate(IntConsumer listener, Executor executor) { mListener = listener; mExecutor = executor; } } @UnsupportedAppUsage static ContextImpl getImpl(Context context) { Context nextContext; Loading Loading @@ -2699,15 +2727,7 @@ class ContextImpl extends Context { @Override public @NonNull Context createDeviceContext(int deviceId) { boolean validDeviceId = deviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT; if (deviceId > VirtualDeviceManager.DEVICE_ID_DEFAULT) { VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class); if (vdm != null) { List<VirtualDevice> virtualDevices = vdm.getVirtualDevices(); validDeviceId = virtualDevices.stream().anyMatch(d -> d.getDeviceId() == deviceId); } } if (!validDeviceId) { if (!isValidDeviceId(deviceId)) { throw new IllegalArgumentException( "Not a valid ID of the default device or any virtual device: " + deviceId); } Loading @@ -2718,9 +2738,35 @@ class ContextImpl extends Context { mSplitName, mToken, mUser, mFlags, mClassLoader, null); context.mDeviceId = deviceId; context.mIsExplicitDeviceId = true; return context; } /** * Checks whether the passed {@code deviceId} is valid or not. * {@link VirtualDeviceManager#DEVICE_ID_DEFAULT} is valid as it is the ID of the default * device when no additional virtual devices exist. If {@code deviceId} is the id of * a virtual device, it should correspond to a virtual device created by * {@link VirtualDeviceManager#createVirtualDevice(int, VirtualDeviceParams)}. */ private boolean isValidDeviceId(int deviceId) { if (deviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT) { return true; } if (deviceId > VirtualDeviceManager.DEVICE_ID_DEFAULT) { VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class); if (vdm != null) { List<VirtualDevice> virtualDevices = vdm.getVirtualDevices(); for (int i = 0; i < virtualDevices.size(); i++) { if (virtualDevices.get(i).getDeviceId() == deviceId) { return true; } } } } return false; } @NonNull @Override public WindowContext createWindowContext(@WindowType int type, Loading Loading @@ -2965,6 +3011,21 @@ class ContextImpl extends Context { if (mContextType == CONTEXT_TYPE_NON_UI) { mContextType = CONTEXT_TYPE_DISPLAY_CONTEXT; } // TODO(b/253201821): Update deviceId when display is updated. } @Override public void updateDeviceId(int updatedDeviceId) { if (!isValidDeviceId(updatedDeviceId)) { throw new IllegalArgumentException( "Not a valid ID of the default device or any virtual device: " + mDeviceId); } if (mIsExplicitDeviceId) { throw new UnsupportedOperationException( "Cannot update device ID on a Context created with createDeviceContext()"); } mDeviceId = updatedDeviceId; notifyOnDeviceChangedListeners(updatedDeviceId); } @Override Loading @@ -2972,6 +3033,69 @@ class ContextImpl extends Context { return mDeviceId; } @Override public boolean isDeviceContext() { return mIsExplicitDeviceId || isAssociatedWithDisplay(); } @Override public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor, @NonNull IntConsumer listener) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(listener, "listener cannot be null"); synchronized (mDeviceIdListenerLock) { if (getDeviceIdListener(listener) != null) { throw new IllegalArgumentException( "attempt to call registerDeviceIdChangeListener() " + "on a previously registered listener"); } // lazy initialization if (mDeviceIdChangeListeners == null) { mDeviceIdChangeListeners = new ArrayList<>(); } mDeviceIdChangeListeners.add(new DeviceIdChangeListenerDelegate(listener, executor)); } } @Override public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) { Objects.requireNonNull(listener, "listener cannot be null"); synchronized (mDeviceIdListenerLock) { DeviceIdChangeListenerDelegate listenerToRemove = getDeviceIdListener(listener); if (listenerToRemove != null) { mDeviceIdChangeListeners.remove(listenerToRemove); } } } @GuardedBy("mDeviceIdListenerLock") @Nullable private DeviceIdChangeListenerDelegate getDeviceIdListener( @Nullable IntConsumer listener) { if (mDeviceIdChangeListeners == null) { return null; } for (int i = 0; i < mDeviceIdChangeListeners.size(); i++) { DeviceIdChangeListenerDelegate delegate = mDeviceIdChangeListeners.get(i); if (delegate.mListener == listener) { return delegate; } } return null; } private void notifyOnDeviceChangedListeners(int deviceId) { synchronized (mDeviceIdListenerLock) { if (mDeviceIdChangeListeners != null) { for (DeviceIdChangeListenerDelegate delegate : mDeviceIdChangeListeners) { delegate.mExecutor.execute(() -> delegate.mListener.accept(deviceId)); } } } } @Override public DisplayAdjustments getDisplayAdjustments(int displayId) { return mResources.getDisplayAdjustments(); Loading Loading @@ -3227,6 +3351,8 @@ class ContextImpl extends Context { opPackageName = container.mOpPackageName; setResources(container.mResources); mDisplay = container.mDisplay; mDeviceId = container.mDeviceId; mIsExplicitDeviceId = container.mIsExplicitDeviceId; mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources; mIsConfigurationBasedContext = container.mIsConfigurationBasedContext; mContextType = container.mContextType; Loading core/java/android/content/Context.java +102 −1 Original line number Diff line number Diff line Loading @@ -109,6 +109,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * Interface to global information about an application environment. This is Loading Loading @@ -6921,6 +6922,10 @@ public abstract class Context { * {@link android.companion.virtual.VirtualDeviceManager#DEVICE_ID_DEFAULT}. Similarly, * applications running on the default device may access the functionality of virtual devices. * </p> * <p> * Note that the newly created instance will be associated with the same display as the parent * Context, regardless of the device ID passed here. * </p> * @param deviceId The ID of the device to associate with this context. * @return A context associated with the given device ID. * Loading Loading @@ -7256,19 +7261,115 @@ 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 * Updates the device ID association of this Context. Since a Context created with * {@link #createDeviceContext} cannot change its device association, this method must * not be called for instances created with {@link #createDeviceContext}. * * @param deviceId The new device ID to assign to this Context. * @throws UnsupportedOperationException if the method is called on an instance that was * created with {@link Context#createDeviceContext(int)} * @throws IllegalArgumentException if the given device ID is not a valid ID of the default * device or a virtual device. * * @see #isDeviceContext() * @see #createDeviceContext(int) * @hide */ public void updateDeviceId(int deviceId) { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Gets 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#DEVICE_ID_DEFAULT} * * <p> * If the underlying device ID is changed by the system, for example, when an * {@link Activity} is moved to a different virtual device, applications can register to listen * to changes by calling * {@link Context#registerDeviceIdChangeListener(Executor, IntConsumer)}. * </p> * * <p> * This method will only return a reliable value for this instance if * {@link Context#isDeviceContext()} is {@code true}. The system can assign an arbitrary device * id value for Contexts not logically associated with a device. * </p> * * @return the ID of the device this context is associated with. * @see #isDeviceContext() * @see #createDeviceContext(int) * @see #registerDeviceIdChangeListener(Executor, IntConsumer) */ public int getDeviceId() { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Indicates whether the value of {@link Context#getDeviceId()} can be relied upon for * this instance. It will return {@code true} for Contexts created by * {@link Context#createDeviceContext(int)}, as well as for UI and Display Contexts. * <p> * Contexts created with {@link Context#createDeviceContext(int)} will have an explicit * device association, which will never change. UI Contexts and Display Contexts are * already associated with a display, so if the device association is not explicitly * given, {@link Context#getDeviceId()} will return the ID of the device associated with * the associated display. The system can assign an arbitrary device id value for Contexts not * logically associated with a device. * </p> * * @return {@code true} if {@link Context#getDeviceId()} is reliable, {@code false} otherwise. * * @see #createDeviceContext(int) * @see #getDeviceId()} * @see #createDisplayContext(Display) * @see #isUiContext() */ public boolean isDeviceContext() { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Adds a new device ID changed listener to the {@code Context}, which will be called when * the device association is changed by the system. * <p> * The callback can be called when an app is moved to a different device and the {@code Context} * is not explicily associated with a specific device. * </p> * <p> When an application receives a device id update callback, this Context is guaranteed to * also have an updated display ID(if any) and {@link Configuration}. * <p/> * @param executor The Executor on whose thread to execute the callbacks of the {@code listener} * object. * @param listener The listener {@code IntConsumer} to call which will receive the updated * device ID. * * @see Context#isDeviceContext() * @see Context#getDeviceId() * @see Context#createDeviceContext(int) */ public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor, @NonNull IntConsumer listener) { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Removes a device ID changed listener from the Context. It's a no-op if * the listener is not already registered. * * @param listener The {@code Consumer} to remove. * * @see #getDeviceId() * @see #registerDeviceIdChangeListener(Executor, IntConsumer) */ public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Indicates whether this Context is restricted. * Loading core/java/android/content/ContextWrapper.java +26 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.content; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -61,6 +62,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; import java.util.function.IntConsumer; /** * Proxying implementation of Context that simply delegates all of its calls to Loading Loading @@ -1171,11 +1173,35 @@ public class ContextWrapper extends Context { mBase.updateDisplay(displayId); } /** * @hide */ @Override public void updateDeviceId(int deviceId) { mBase.updateDeviceId(deviceId); } @Override public int getDeviceId() { return mBase.getDeviceId(); } @Override public boolean isDeviceContext() { return mBase.isDeviceContext(); } @Override public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor, @NonNull IntConsumer listener) { mBase.registerDeviceIdChangeListener(executor, listener); } @Override public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) { mBase.unregisterDeviceIdChangeListener(listener); } @Override public Context createDeviceProtectedStorageContext() { return mBase.createDeviceProtectedStorageContext(); Loading core/java/com/android/internal/policy/DecorContext.java +9 −0 Original line number Diff line number Diff line Loading @@ -138,6 +138,15 @@ public class DecorContext extends ContextThemeWrapper { return false; } @Override public boolean isDeviceContext() { Context context = mContext.get(); if (context != null) { return context.isDeviceContext(); } return false; } @Override public boolean isConfigurationContext() { Context context = mContext.get(); Loading Loading
core/api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -9879,6 +9879,7 @@ package android.content { method @Deprecated public abstract int getWallpaperDesiredMinimumHeight(); method @Deprecated public abstract int getWallpaperDesiredMinimumWidth(); method public abstract void grantUriPermission(String, android.net.Uri, int); method public boolean isDeviceContext(); method public abstract boolean isDeviceProtectedStorage(); method public boolean isRestricted(); method public boolean isUiContext(); Loading @@ -9894,6 +9895,7 @@ package android.content { method public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, @Nullable android.database.DatabaseErrorHandler); method @Deprecated public abstract android.graphics.drawable.Drawable peekWallpaper(); method public void registerComponentCallbacks(android.content.ComponentCallbacks); method public void registerDeviceIdChangeListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer); method @Nullable public abstract android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter); method @Nullable public abstract android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, int); method @Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler); Loading Loading @@ -9933,6 +9935,7 @@ package android.content { method public abstract boolean stopService(android.content.Intent); method public abstract void unbindService(@NonNull android.content.ServiceConnection); method public void unregisterComponentCallbacks(android.content.ComponentCallbacks); method public void unregisterDeviceIdChangeListener(@NonNull java.util.function.IntConsumer); method public abstract void unregisterReceiver(android.content.BroadcastReceiver); method public void updateServiceGroup(@NonNull android.content.ServiceConnection, int, int); field public static final String ACCESSIBILITY_SERVICE = "accessibility";
core/java/android/app/ContextImpl.java +135 −9 Original line number Diff line number Diff line Loading @@ -21,12 +21,14 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.StrictMode.vmIncorrectContextUseEnabled; import static android.view.WindowManager.LayoutParams.WindowType; import android.annotation.CallbackExecutor; 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.companion.virtual.VirtualDeviceParams; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.AutofillOptions; Loading Loading @@ -121,6 +123,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.IntConsumer; class ReceiverRestrictedContext extends ContextWrapper { @UnsupportedAppUsage Loading Loading @@ -257,6 +260,13 @@ class ContextImpl extends Context { /** @see Context#isConfigurationContext() */ private boolean mIsConfigurationBasedContext; /** * Indicates that this context was created with an explicit device ID association via * Context#createDeviceContext and under no circumstances will it ever change, even if * this context is not associated with a display id, or if the associated display id changes. */ private boolean mIsExplicitDeviceId = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final int mFlags; Loading Loading @@ -372,6 +382,24 @@ class ContextImpl extends Context { @ServiceInitializationState final int[] mServiceInitializationStateArray = new int[mServiceCache.length]; private final Object mDeviceIdListenerLock = new Object(); /** * List of listeners for deviceId changes and their associated Executor. * List is lazy-initialized on first registration */ @GuardedBy("mDeviceIdListenerLock") @Nullable private ArrayList<DeviceIdChangeListenerDelegate> mDeviceIdChangeListeners; private static class DeviceIdChangeListenerDelegate { final @NonNull IntConsumer mListener; final @NonNull Executor mExecutor; DeviceIdChangeListenerDelegate(IntConsumer listener, Executor executor) { mListener = listener; mExecutor = executor; } } @UnsupportedAppUsage static ContextImpl getImpl(Context context) { Context nextContext; Loading Loading @@ -2699,15 +2727,7 @@ class ContextImpl extends Context { @Override public @NonNull Context createDeviceContext(int deviceId) { boolean validDeviceId = deviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT; if (deviceId > VirtualDeviceManager.DEVICE_ID_DEFAULT) { VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class); if (vdm != null) { List<VirtualDevice> virtualDevices = vdm.getVirtualDevices(); validDeviceId = virtualDevices.stream().anyMatch(d -> d.getDeviceId() == deviceId); } } if (!validDeviceId) { if (!isValidDeviceId(deviceId)) { throw new IllegalArgumentException( "Not a valid ID of the default device or any virtual device: " + deviceId); } Loading @@ -2718,9 +2738,35 @@ class ContextImpl extends Context { mSplitName, mToken, mUser, mFlags, mClassLoader, null); context.mDeviceId = deviceId; context.mIsExplicitDeviceId = true; return context; } /** * Checks whether the passed {@code deviceId} is valid or not. * {@link VirtualDeviceManager#DEVICE_ID_DEFAULT} is valid as it is the ID of the default * device when no additional virtual devices exist. If {@code deviceId} is the id of * a virtual device, it should correspond to a virtual device created by * {@link VirtualDeviceManager#createVirtualDevice(int, VirtualDeviceParams)}. */ private boolean isValidDeviceId(int deviceId) { if (deviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT) { return true; } if (deviceId > VirtualDeviceManager.DEVICE_ID_DEFAULT) { VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class); if (vdm != null) { List<VirtualDevice> virtualDevices = vdm.getVirtualDevices(); for (int i = 0; i < virtualDevices.size(); i++) { if (virtualDevices.get(i).getDeviceId() == deviceId) { return true; } } } } return false; } @NonNull @Override public WindowContext createWindowContext(@WindowType int type, Loading Loading @@ -2965,6 +3011,21 @@ class ContextImpl extends Context { if (mContextType == CONTEXT_TYPE_NON_UI) { mContextType = CONTEXT_TYPE_DISPLAY_CONTEXT; } // TODO(b/253201821): Update deviceId when display is updated. } @Override public void updateDeviceId(int updatedDeviceId) { if (!isValidDeviceId(updatedDeviceId)) { throw new IllegalArgumentException( "Not a valid ID of the default device or any virtual device: " + mDeviceId); } if (mIsExplicitDeviceId) { throw new UnsupportedOperationException( "Cannot update device ID on a Context created with createDeviceContext()"); } mDeviceId = updatedDeviceId; notifyOnDeviceChangedListeners(updatedDeviceId); } @Override Loading @@ -2972,6 +3033,69 @@ class ContextImpl extends Context { return mDeviceId; } @Override public boolean isDeviceContext() { return mIsExplicitDeviceId || isAssociatedWithDisplay(); } @Override public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor, @NonNull IntConsumer listener) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(listener, "listener cannot be null"); synchronized (mDeviceIdListenerLock) { if (getDeviceIdListener(listener) != null) { throw new IllegalArgumentException( "attempt to call registerDeviceIdChangeListener() " + "on a previously registered listener"); } // lazy initialization if (mDeviceIdChangeListeners == null) { mDeviceIdChangeListeners = new ArrayList<>(); } mDeviceIdChangeListeners.add(new DeviceIdChangeListenerDelegate(listener, executor)); } } @Override public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) { Objects.requireNonNull(listener, "listener cannot be null"); synchronized (mDeviceIdListenerLock) { DeviceIdChangeListenerDelegate listenerToRemove = getDeviceIdListener(listener); if (listenerToRemove != null) { mDeviceIdChangeListeners.remove(listenerToRemove); } } } @GuardedBy("mDeviceIdListenerLock") @Nullable private DeviceIdChangeListenerDelegate getDeviceIdListener( @Nullable IntConsumer listener) { if (mDeviceIdChangeListeners == null) { return null; } for (int i = 0; i < mDeviceIdChangeListeners.size(); i++) { DeviceIdChangeListenerDelegate delegate = mDeviceIdChangeListeners.get(i); if (delegate.mListener == listener) { return delegate; } } return null; } private void notifyOnDeviceChangedListeners(int deviceId) { synchronized (mDeviceIdListenerLock) { if (mDeviceIdChangeListeners != null) { for (DeviceIdChangeListenerDelegate delegate : mDeviceIdChangeListeners) { delegate.mExecutor.execute(() -> delegate.mListener.accept(deviceId)); } } } } @Override public DisplayAdjustments getDisplayAdjustments(int displayId) { return mResources.getDisplayAdjustments(); Loading Loading @@ -3227,6 +3351,8 @@ class ContextImpl extends Context { opPackageName = container.mOpPackageName; setResources(container.mResources); mDisplay = container.mDisplay; mDeviceId = container.mDeviceId; mIsExplicitDeviceId = container.mIsExplicitDeviceId; mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources; mIsConfigurationBasedContext = container.mIsConfigurationBasedContext; mContextType = container.mContextType; Loading
core/java/android/content/Context.java +102 −1 Original line number Diff line number Diff line Loading @@ -109,6 +109,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * Interface to global information about an application environment. This is Loading Loading @@ -6921,6 +6922,10 @@ public abstract class Context { * {@link android.companion.virtual.VirtualDeviceManager#DEVICE_ID_DEFAULT}. Similarly, * applications running on the default device may access the functionality of virtual devices. * </p> * <p> * Note that the newly created instance will be associated with the same display as the parent * Context, regardless of the device ID passed here. * </p> * @param deviceId The ID of the device to associate with this context. * @return A context associated with the given device ID. * Loading Loading @@ -7256,19 +7261,115 @@ 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 * Updates the device ID association of this Context. Since a Context created with * {@link #createDeviceContext} cannot change its device association, this method must * not be called for instances created with {@link #createDeviceContext}. * * @param deviceId The new device ID to assign to this Context. * @throws UnsupportedOperationException if the method is called on an instance that was * created with {@link Context#createDeviceContext(int)} * @throws IllegalArgumentException if the given device ID is not a valid ID of the default * device or a virtual device. * * @see #isDeviceContext() * @see #createDeviceContext(int) * @hide */ public void updateDeviceId(int deviceId) { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Gets 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#DEVICE_ID_DEFAULT} * * <p> * If the underlying device ID is changed by the system, for example, when an * {@link Activity} is moved to a different virtual device, applications can register to listen * to changes by calling * {@link Context#registerDeviceIdChangeListener(Executor, IntConsumer)}. * </p> * * <p> * This method will only return a reliable value for this instance if * {@link Context#isDeviceContext()} is {@code true}. The system can assign an arbitrary device * id value for Contexts not logically associated with a device. * </p> * * @return the ID of the device this context is associated with. * @see #isDeviceContext() * @see #createDeviceContext(int) * @see #registerDeviceIdChangeListener(Executor, IntConsumer) */ public int getDeviceId() { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Indicates whether the value of {@link Context#getDeviceId()} can be relied upon for * this instance. It will return {@code true} for Contexts created by * {@link Context#createDeviceContext(int)}, as well as for UI and Display Contexts. * <p> * Contexts created with {@link Context#createDeviceContext(int)} will have an explicit * device association, which will never change. UI Contexts and Display Contexts are * already associated with a display, so if the device association is not explicitly * given, {@link Context#getDeviceId()} will return the ID of the device associated with * the associated display. The system can assign an arbitrary device id value for Contexts not * logically associated with a device. * </p> * * @return {@code true} if {@link Context#getDeviceId()} is reliable, {@code false} otherwise. * * @see #createDeviceContext(int) * @see #getDeviceId()} * @see #createDisplayContext(Display) * @see #isUiContext() */ public boolean isDeviceContext() { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Adds a new device ID changed listener to the {@code Context}, which will be called when * the device association is changed by the system. * <p> * The callback can be called when an app is moved to a different device and the {@code Context} * is not explicily associated with a specific device. * </p> * <p> When an application receives a device id update callback, this Context is guaranteed to * also have an updated display ID(if any) and {@link Configuration}. * <p/> * @param executor The Executor on whose thread to execute the callbacks of the {@code listener} * object. * @param listener The listener {@code IntConsumer} to call which will receive the updated * device ID. * * @see Context#isDeviceContext() * @see Context#getDeviceId() * @see Context#createDeviceContext(int) */ public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor, @NonNull IntConsumer listener) { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Removes a device ID changed listener from the Context. It's a no-op if * the listener is not already registered. * * @param listener The {@code Consumer} to remove. * * @see #getDeviceId() * @see #registerDeviceIdChangeListener(Executor, IntConsumer) */ public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) { throw new RuntimeException("Not implemented. Must override in a subclass."); } /** * Indicates whether this Context is restricted. * Loading
core/java/android/content/ContextWrapper.java +26 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.content; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -61,6 +62,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; import java.util.function.IntConsumer; /** * Proxying implementation of Context that simply delegates all of its calls to Loading Loading @@ -1171,11 +1173,35 @@ public class ContextWrapper extends Context { mBase.updateDisplay(displayId); } /** * @hide */ @Override public void updateDeviceId(int deviceId) { mBase.updateDeviceId(deviceId); } @Override public int getDeviceId() { return mBase.getDeviceId(); } @Override public boolean isDeviceContext() { return mBase.isDeviceContext(); } @Override public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor, @NonNull IntConsumer listener) { mBase.registerDeviceIdChangeListener(executor, listener); } @Override public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) { mBase.unregisterDeviceIdChangeListener(listener); } @Override public Context createDeviceProtectedStorageContext() { return mBase.createDeviceProtectedStorageContext(); Loading
core/java/com/android/internal/policy/DecorContext.java +9 −0 Original line number Diff line number Diff line Loading @@ -138,6 +138,15 @@ public class DecorContext extends ContextThemeWrapper { return false; } @Override public boolean isDeviceContext() { Context context = mContext.get(); if (context != null) { return context.isDeviceContext(); } return false; } @Override public boolean isConfigurationContext() { Context context = mContext.get(); Loading