Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d12be752 authored by Charles Chen's avatar Charles Chen
Browse files

Allow config context to inflate views

Besides UI contexts, the context created via
Context#createConfigurationContext with a proper configuration
should be allowed to inflate views or obtain ViewConfiguration.
An example is that a wear device inflate views into bitmap and pass
the bitmap to the Wear OS Companion app on the phone.

Bug: 177847640
Test: atest StrictModeTest

Change-Id: Iab232a80a973f54bf0484262d45af3e4c2f0e5dc
parent 57f2f51e
Loading
Loading
Loading
Loading
+20 −8
Original line number Diff line number Diff line
@@ -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;
        }
    }

+21 −0
Original line number Diff line number Diff line
@@ -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.");
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -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();
    }
}
+39 −0
Original line number Diff line number Diff line
@@ -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.
+2 −14
Original line number Diff line number Diff line
@@ -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();
Loading