Merge "Allow config context to inflate views" into sc-dev

This commit is contained in:
Charles Chen
2021-04-09 06:56:16 +00:00
committed by Android (Google) Code Review
8 changed files with 105 additions and 38 deletions

View File

@@ -243,6 +243,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;
@@ -2002,13 +2005,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);
@@ -2041,6 +2043,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.
@@ -2053,10 +2061,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) {
@@ -2538,6 +2542,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
@@ -2575,6 +2580,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;
}
@@ -2988,6 +2997,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;
@@ -3058,6 +3068,7 @@ class ContextImpl extends Context {
setResources(container.mResources);
mDisplay = container.mDisplay;
mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources;
mIsConfigurationBasedContext = container.mIsConfigurationBasedContext;
mContextType = container.mContextType;
} else {
mBasePackageName = packageInfo.mPackageName;
@@ -3135,6 +3146,7 @@ class ContextImpl extends Context {
// WindowContext.
if (mOuterContext.isUiContext() && mContextType <= CONTEXT_TYPE_DISPLAY_CONTEXT) {
mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
mIsConfigurationBasedContext = true;
}
}

View File

@@ -6817,4 +6817,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.");
}
}

View File

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

View File

@@ -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;
@@ -2260,6 +2261,44 @@ public final class StrictMode {
onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack));
}
/**
* 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.

View File

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

View File

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

View File

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

View File

@@ -950,4 +950,10 @@ public class MockContext extends Context {
public boolean isUiContext() {
throw new UnsupportedOperationException();
}
/** {@hide} */
@Override
public boolean isConfigurationContext() {
throw new UnsupportedOperationException();
}
}