From bfe1b70fcd3f3688bd27be317ee0830e80cc00fc Mon Sep 17 00:00:00 2001 From: Chris Li <lihongyu@google.com> Date: Tue, 17 Mar 2020 10:12:24 -0700 Subject: [PATCH] Report incorrect context usage in ViewConfiguration Before, the documentation said that the passed context is an application context, which is incorrect to get the density, window metrics, and window manager. We should use visual context to get these instead. Bug: 151474461 Test: StrictModeTest#testIncorrectContextUse_GetViewConfiguration Change-Id: Iea28d727cafbb3ec8536742c6a0e594f73fe5a51 --- core/java/android/app/ContextImpl.java | 4 +- core/java/android/content/Context.java | 9 +++++ core/java/android/content/ContextWrapper.java | 8 ++++ core/java/android/view/ViewConfiguration.java | 38 +++++++++++++++---- .../src/android/test/mock/MockContext.java | 6 +++ 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 17fd4efec9cc..d8757c36374a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1920,7 +1920,9 @@ class ContextImpl extends Context { return SystemServiceRegistry.getSystemServiceName(serviceClass); } - private boolean isUiContext() { + /** @hide */ + @Override + public boolean isUiContext() { return mIsSystemOrSystemUiContext || mIsUiContext || isSystemOrSystemUI(); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2fe935e35735..e21a31e5e185 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6103,4 +6103,13 @@ public abstract class Context { + "get a UI context from ActivityThread#getSystemUiContext()"); } } + + /** + * Indicates if this context is a visual context such as {@link android.app.Activity} or + * a context created from {@link #createWindowContext(int, Bundle)}. + * @hide + */ + public boolean isUiContext() { + 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 d389d2a4ae09..5dc41e483640 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1145,4 +1145,12 @@ public class ContextWrapper extends Context { mBase.setContentCaptureOptions(options); } } + + /** + * @hide + */ + @Override + public boolean isUiContext() { + return mBase.isUiContext(); + } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 4bea623716dc..69d37ab91a1c 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -16,8 +16,11 @@ package android.view; +import static android.os.StrictMode.vmIncorrectContextUseEnabled; + import android.annotation.FloatRange; import android.annotation.TestApi; +import android.app.Activity; import android.app.AppGlobals; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -25,9 +28,12 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.os.Build; +import android.os.Bundle; 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; @@ -35,6 +41,8 @@ import android.util.TypedValue; * Contains methods to standard constants used in the UI for timeouts, sizes, and distances. */ public class ViewConfiguration { + private static final String TAG = "ViewConfiguration"; + /** * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in * dips @@ -372,11 +380,13 @@ public class ViewConfiguration { } /** - * Creates a new configuration for the specified context. The configuration depends on - * various parameters of the context, like the dimension of the display or the density - * of the display. + * Creates a new configuration for the specified visual {@link Context}. The configuration + * depends on various parameters of the {@link Context}, like the dimension of the display or + * the density of the display. * - * @param context The application context used to initialize this view configuration. + * @param context A visual {@link Context} used to initialize the view configuration. It must + * be {@link Activity} or other {@link Context} created with + * {@link Context#createWindowContext(int, Bundle)}. * * @see #get(android.content.Context) * @see android.util.DisplayMetrics @@ -480,13 +490,27 @@ public class ViewConfiguration { } /** - * Returns a configuration for the specified context. The configuration depends on - * various parameters of the context, like the dimension of the display or the + * Returns a configuration for the specified visual {@link Context}. The configuration depends + * on various parameters of the {@link Context}, like the dimension of the display or the * density of the display. * - * @param context The application context used to initialize the view configuration. + * @param context A visual {@link Context} used to initialize the view configuration. It must + * be {@link Activity} or other {@link Context} created with + * {@link Context#createWindowContext(int, Bundle)}. */ public static ViewConfiguration get(Context context) { + if (!context.isUiContext() && vmIncorrectContextUseEnabled()) { + final String errorMessage = "Tried to access UI constants from a non-visual 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); + } + 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 784ee85fea34..cf3b03cae72e 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -928,4 +928,10 @@ public class MockContext extends Context { public Handler getMainThreadHandler() { throw new UnsupportedOperationException(); } + + /** {@hide} */ + @Override + public boolean isUiContext() { + throw new UnsupportedOperationException(); + } } -- GitLab