diff options
-rw-r--r-- | core/java/android/app/ContextImpl.java | 28 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 21 | ||||
-rw-r--r-- | core/java/android/content/ContextWrapper.java | 11 | ||||
-rw-r--r-- | core/java/android/os/StrictMode.java | 39 | ||||
-rw-r--r-- | core/java/android/view/GestureDetector.java | 16 | ||||
-rw-r--r-- | core/java/android/view/LayoutInflater.java | 4 | ||||
-rw-r--r-- | core/java/android/view/ViewConfiguration.java | 18 | ||||
-rw-r--r-- | test-mock/src/android/test/mock/MockContext.java | 6 |
8 files changed, 105 insertions, 38 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index eb31b5294a26..388c7b9f6cdc 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -242,6 +242,9 @@ class ContextImpl extends Context { */ private boolean mForceDisplayOverrideInResources; + /** @see Context#isConfigurationContext() */ + private boolean mIsConfigurationBasedContext; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final int mFlags; @@ -2001,13 +2004,12 @@ class ContextImpl extends Context { public Object getSystemService(String name) { if (vmIncorrectContextUseEnabled()) { // Check incorrect Context usage. - if (isUiComponent(name) && !isUiContext()) { + if (WINDOW_SERVICE.equals(name) && !isUiContext()) { final String errorMessage = "Tried to access visual service " + SystemServiceRegistry.getSystemServiceClassName(name) + " from a non-visual Context:" + getOuterContext(); - final String message = "Visual services, such as WindowManager " - + "or LayoutInflater should be accessed from Activity or another visual " - + "Context. Use an Activity or a Context created with " + final String message = "WindowManager should be accessed from Activity or other " + + "visual Context. Use an Activity or a Context created with " + "Context#createWindowContext(int, Bundle), which are adjusted to " + "the configuration and visual bounds of an area on screen."; final Exception exception = new IllegalAccessException(errorMessage); @@ -2040,6 +2042,12 @@ class ContextImpl extends Context { } } + /** @hide */ + @Override + public boolean isConfigurationContext() { + return isUiContext() || mIsConfigurationBasedContext; + } + /** * Temporary workaround to permit incorrect usages of Context by SystemUI. * TODO(b/147647877): Fix usages and remove. @@ -2052,10 +2060,6 @@ class ContextImpl extends Context { Binder.getCallingUid()) == PERMISSION_GRANTED; } - private static boolean isUiComponent(String name) { - return WINDOW_SERVICE.equals(name) || LAYOUT_INFLATER_SERVICE.equals(name); - } - @Override public int checkPermission(String permission, int pid, int uid) { if (permission == null) { @@ -2537,6 +2541,7 @@ class ContextImpl extends Context { mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), mSplitName, mToken, mUser, mFlags, mClassLoader, null); + context.mIsConfigurationBasedContext = true; final int displayId = getDisplayId(); final Integer overrideDisplayId = mForceDisplayOverrideInResources @@ -2574,6 +2579,10 @@ class ContextImpl extends Context { // the display that would otherwise be inherited from mToken (or the global configuration if // mToken is null). context.mForceDisplayOverrideInResources = true; + // The configuration is overridden by display adjustments' configuration and won't receive + // configuration changes. This context won't be regarded as having the proper configuration + // anymore. + context.mIsConfigurationBasedContext = false; return context; } @@ -2987,6 +2996,7 @@ class ContextImpl extends Context { ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY, null, null, activityInfo.splitName, activityToken, null, 0, classLoader, null); context.mContextType = CONTEXT_TYPE_ACTIVITY; + context.mIsConfigurationBasedContext = true; // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; @@ -3057,6 +3067,7 @@ class ContextImpl extends Context { setResources(container.mResources); mDisplay = container.mDisplay; mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources; + mIsConfigurationBasedContext = container.mIsConfigurationBasedContext; mContextType = container.mContextType; } else { mBasePackageName = packageInfo.mPackageName; @@ -3132,6 +3143,7 @@ class ContextImpl extends Context { // WindowContext. if (mOuterContext.isUiContext() && mContextType <= CONTEXT_TYPE_DISPLAY_CONTEXT) { mContextType = CONTEXT_TYPE_WINDOW_CONTEXT; + mIsConfigurationBasedContext = true; } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 232daa8f8b47..6d08c34f9176 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6816,4 +6816,25 @@ public abstract class Context { * @hide */ public void destroy() { } + + /** + * Indicates this {@link Context} has the proper {@link Configuration} to obtain + * {@link android.view.LayoutInflater}, {@link android.view.ViewConfiguration} and + * {@link android.view.GestureDetector}. Generally, all UI contexts, such as + * {@link android.app.Activity} or {@link android.app.WindowContext}, are initialized with base + * configuration. + * <p> + * Note that the context created via {@link Context#createConfigurationContext(Configuration)} + * is also regarded as a context that is based on a configuration because the + * configuration is explicitly provided via the API. + * </p> + * + * @see #isUiContext() + * @see #createConfigurationContext(Configuration) + * + * @hide + */ + public boolean isConfigurationContext() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index de0d65fec1fb..8936d0c47a58 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1250,4 +1250,15 @@ public class ContextWrapper extends Context { } return mBase.isUiContext(); } + + /** + * @hide + */ + @Override + public boolean isConfigurationContext() { + if (mBase == null) { + return false; + } + return mBase.isConfigurationContext(); + } } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index df24baaf8ad9..44c3d61b8760 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -30,6 +30,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.net.TrafficStats; import android.net.Uri; import android.os.storage.IStorageManager; @@ -2261,6 +2262,44 @@ public final class StrictMode { } /** + * A helper method to verify if the {@code context} has a proper {@link Configuration} to obtain + * {@link android.view.LayoutInflater}, {@link android.view.ViewConfiguration} or + * {@link android.view.GestureDetector}. Throw {@link IncorrectContextUseViolation} if the + * {@code context} doesn't have a proper configuration. + * <p> + * Note that the context created via {@link Context#createConfigurationContext(Configuration)} + * is also regarded as a context with a proper configuration because the {@link Configuration} + * is handled by developers. + * </p> + * @param context The context to verify if it is a display associative context + * @param methodName The asserted method name + * + * @see Context#isConfigurationContext() + * @see Context#createConfigurationContext(Configuration) + * @see Context#getSystemService(String) + * @see Context#LAYOUT_INFLATER_SERVICE + * @see android.view.ViewConfiguration#get(Context) + * @see android.view.LayoutInflater#from(Context) + * @see IncorrectContextUseViolation + * + * @hide + */ + public static void assertConfigurationContext(@NonNull Context context, + @NonNull String methodName) { + if (vmIncorrectContextUseEnabled() && !context.isConfigurationContext()) { + final String errorMessage = "Tried to access the API:" + methodName + " which needs to" + + " have proper configuration from a non-UI Context:" + context; + final String message = "The API:" + methodName + " needs a proper configuration." + + " Use UI contexts such as an activity or a context created" + + " via createWindowContext(Display, int, Bundle) or " + + " createConfigurationContext(Configuration) with a proper configuration."; + final Exception exception = new IllegalAccessException(errorMessage); + StrictMode.onIncorrectContextUsed(message, exception); + Log.e(TAG, errorMessage + " " + message, exception); + } + } + + /** * A helper method to verify if the {@code context} is a UI context and throw * {@link IncorrectContextUseViolation} if the {@code context} is not a UI context. * diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 8a722184eb77..63a8300ce6aa 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -16,8 +16,6 @@ package android.view; -import static android.os.StrictMode.vmIncorrectContextUseEnabled; - import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS; @@ -34,7 +32,6 @@ import android.os.Handler; import android.os.Message; import android.os.StrictMode; import android.os.SystemClock; -import android.util.Log; import com.android.internal.util.FrameworkStatsLog; @@ -394,6 +391,7 @@ public class GestureDetector { * * @throws NullPointerException if {@code listener} is null. */ + // TODO(b/182007470): Use @ConfigurationContext instead public GestureDetector(@UiContext Context context, OnGestureListener listener) { this(context, listener, null); } @@ -467,17 +465,7 @@ public class GestureDetector { mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); mAmbiguousGestureMultiplier = ViewConfiguration.getAmbiguousGestureMultiplier(); } else { - if (!context.isUiContext() && vmIncorrectContextUseEnabled()) { - final String errorMessage = - "Tried to access UI constants from a non-visual Context."; - final String message = "GestureDetector must be accessed from Activity or other " - + "visual Context. Use an Activity or a Context created with " - + "Context#createWindowContext(int, Bundle), which are adjusted to the " - + "configuration and visual bounds of an area on screen."; - final Exception exception = new IllegalArgumentException(errorMessage); - StrictMode.onIncorrectContextUsed(message, exception); - Log.e(TAG, errorMessage + message, exception); - } + StrictMode.assertConfigurationContext(context, "GestureDetector#init"); final ViewConfiguration configuration = ViewConfiguration.get(context); touchSlop = configuration.getScaledTouchSlop(); doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop(); diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 66c514824e35..df78827534a6 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -32,6 +32,7 @@ import android.graphics.Canvas; import android.os.Build; import android.os.Handler; import android.os.Message; +import android.os.StrictMode; import android.os.Trace; import android.util.AttributeSet; import android.util.Log; @@ -94,6 +95,7 @@ public abstract class LayoutInflater { * This field should be made private, so it is hidden from the SDK. * {@hide} */ + // TODO(b/182007470): Use @ConfigurationContext instead @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @UiContext protected final Context mContext; @@ -255,6 +257,7 @@ public abstract class LayoutInflater { * values for their attributes are retrieved. */ protected LayoutInflater(Context context) { + StrictMode.assertConfigurationContext(context, "LayoutInflater"); mContext = context; initPrecompiledViews(); } @@ -268,6 +271,7 @@ public abstract class LayoutInflater { * @param newContext The new Context to use. */ protected LayoutInflater(LayoutInflater original, Context newContext) { + StrictMode.assertConfigurationContext(newContext, "LayoutInflater"); mContext = newContext; mFactory = original.mFactory; mFactory2 = original.mFactory2; diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index ec23a29b2070..0a3d0da6da1e 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -16,8 +16,6 @@ package android.view; -import static android.os.StrictMode.vmIncorrectContextUseEnabled; - import android.annotation.FloatRange; import android.annotation.TestApi; import android.annotation.UiContext; @@ -34,7 +32,6 @@ import android.os.RemoteException; import android.os.StrictMode; import android.provider.Settings; import android.util.DisplayMetrics; -import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; @@ -520,20 +517,9 @@ public class ViewConfiguration { * be {@link Activity} or other {@link Context} created with * {@link Context#createWindowContext(int, Bundle)}. */ - + // TODO(b/182007470): Use @ConfigurationContext instead public static ViewConfiguration get(@UiContext Context context) { - if (!context.isUiContext() && vmIncorrectContextUseEnabled()) { - final String errorMessage = "Tried to access UI constants from a non-visual Context:" - + context; - final String message = "UI constants, such as display metrics or window metrics, " - + "must be accessed from Activity or other visual Context. " - + "Use an Activity or a Context created with " - + "Context#createWindowContext(int, Bundle), which are adjusted to the " - + "configuration and visual bounds of an area on screen"; - final Exception exception = new IllegalArgumentException(errorMessage); - StrictMode.onIncorrectContextUsed(message, exception); - Log.e(TAG, errorMessage + message, exception); - } + StrictMode.assertConfigurationContext(context, "ViewConfiguration"); final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); final int density = (int) (100.0f * metrics.density); diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index 5391bd8642d5..65aa9aed8f99 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -950,4 +950,10 @@ public class MockContext extends Context { public boolean isUiContext() { throw new UnsupportedOperationException(); } + + /** {@hide} */ + @Override + public boolean isConfigurationContext() { + throw new UnsupportedOperationException(); + } } |