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

Commit 5877c7d6 authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Exempt-From-Owner-Approval: Report non-visual Context misuse

Make obtaining a visual service from non-visual Context instance
report a strict mode violation and print the stacktrace.

Make calling getDisplay() throw an exception if called on an instance
that is not associated with a display. For existing usages introduce
a new internal method that does not perform the verification until
the usages are properly fixed.

Bug: 128338354
Test: StrictModeTest#testIncorrectContextUse_GetSystemService
Test: StrictModeTest#testIncorrectContextUse_GetDisplay
Change-Id: Id25d590eca6e10066e55d7ed6436d3bc9e433beb
parent 5263c11a
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -2529,6 +2529,11 @@ package android.os {
    method public void log(android.os.StrictMode.ViolationInfo);
  }

  public static final class StrictMode.VmPolicy.Builder {
    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse();
  }

  public class SystemConfigManager {
    method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
    method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+54 −12
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.app;

import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.StrictMode.vmIncorrectContextUseEnabled;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -64,6 +67,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -247,6 +251,7 @@ class ContextImpl extends Context {

    private boolean mIsSystemOrSystemUiContext;
    private boolean mIsUiContext;
    private boolean mIsAssociatedWithDisplay;

    @GuardedBy("mSync")
    private File mDatabasesDir;
@@ -1891,9 +1896,20 @@ class ContextImpl extends Context {

    @Override
    public Object getSystemService(String name) {
        if (isUiComponent(name) && !isUiContext()) {
            Log.w(TAG, name + " should be accessed from Activity or other visual Context");
        // Check incorrect Context usage.
        if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
            final String errorMessage = "Tried to access visual service " + name
                    + " from a non-visual Context.";
            final String message = "Visual services, such as WindowManager, WallpaperService or "
                    + "LayoutInflater 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);
            StrictMode.onIncorrectContextUsed(message, exception);
            Log.e(TAG, errorMessage + message, exception);
        }

        return SystemServiceRegistry.getSystemService(this, name);
    }

@@ -1902,8 +1918,17 @@ class ContextImpl extends Context {
        return SystemServiceRegistry.getSystemServiceName(serviceClass);
    }

    boolean isUiContext() {
        return mIsSystemOrSystemUiContext || mIsUiContext;
    private boolean isUiContext() {
        return mIsSystemOrSystemUiContext || mIsUiContext || isSystemOrSystemUI();
    }

    /**
     * Temporary workaround to permit incorrect usages of Context by SystemUI.
     * TODO(b/149790106): Fix usages and remove.
     */
    private boolean isSystemOrSystemUI() {
        return ActivityThread.isSystem() || checkPermission("android.permission.STATUS_BAR_SERVICE",
                Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED;
    }

    private static boolean isUiComponent(String name) {
@@ -1925,7 +1950,7 @@ class ContextImpl extends Context {
            final int appId = UserHandle.getAppId(uid);
            if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
                Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
                return PackageManager.PERMISSION_GRANTED;
                return PERMISSION_GRANTED;
            }
            Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
                    + permission);
@@ -1989,7 +2014,7 @@ class ContextImpl extends Context {
    private void enforce(
            String permission, int resultOfCheck,
            boolean selfToo, int uid, String message) {
        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
        if (resultOfCheck != PERMISSION_GRANTED) {
            throw new SecurityException(
                    (message != null ? (message + ": ") : "") +
                    (selfToo
@@ -2116,15 +2141,15 @@ class ContextImpl extends Context {
        if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
            if (readPermission == null
                    || checkPermission(readPermission, pid, uid)
                    == PackageManager.PERMISSION_GRANTED) {
                return PackageManager.PERMISSION_GRANTED;
                    == PERMISSION_GRANTED) {
                return PERMISSION_GRANTED;
            }
        }
        if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
            if (writePermission == null
                    || checkPermission(writePermission, pid, uid)
                    == PackageManager.PERMISSION_GRANTED) {
                return PackageManager.PERMISSION_GRANTED;
                    == PERMISSION_GRANTED) {
                return PERMISSION_GRANTED;
            }
        }
        return uri != null ? checkUriPermission(uri, pid, uid, modeFlags)
@@ -2157,7 +2182,7 @@ class ContextImpl extends Context {
    private void enforceForUri(
            int modeFlags, int resultOfCheck, boolean selfToo,
            int uid, Uri uri, String message) {
        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
        if (resultOfCheck != PERMISSION_GRANTED) {
            throw new SecurityException(
                    (message != null ? (message + ": ") : "") +
                    (selfToo
@@ -2373,6 +2398,7 @@ class ContextImpl extends Context {
                null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
                mResources.getLoaders()));
        context.mDisplay = display;
        context.mIsAssociatedWithDisplay = true;
        return context;
    }

@@ -2390,6 +2416,7 @@ class ContextImpl extends Context {
        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
                mSplitName, token, mUser, mFlags, mClassLoader, null);
        context.mIsUiContext = true;
        context.mIsAssociatedWithDisplay = true;
        return context;
    }

@@ -2440,6 +2467,19 @@ class ContextImpl extends Context {

    @Override
    public Display getDisplay() {
        if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay && !isSystemOrSystemUI()) {
            throw new UnsupportedOperationException("Tried to obtain display from a Context not "
                    + "associated with  one. Only visual Contexts (such as Activity or one created "
                    + "with Context#createWindowContext) or ones created with "
                    + "Context#createDisplayContext are associated with displays. Other types of "
                    + "Contexts are typically related to background entities and may return an "
                    + "arbitrary display.");
        }
        return getDisplayNoVerify();
    }

    @Override
    public Display getDisplayNoVerify() {
        if (mDisplay == null) {
            return mResourcesManager.getAdjustedDisplay(Display.DEFAULT_DISPLAY,
                    mResources);
@@ -2450,13 +2490,14 @@ class ContextImpl extends Context {

    @Override
    public int getDisplayId() {
        final Display display = getDisplay();
        final Display display = getDisplayNoVerify();
        return display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY;
    }

    @Override
    public void updateDisplay(int displayId) {
        mDisplay = mResourcesManager.getAdjustedDisplay(displayId, mResources);
        mIsAssociatedWithDisplay = true;
    }

    @Override
@@ -2630,6 +2671,7 @@ class ContextImpl extends Context {
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
                activityInfo.splitName, activityToken, null, 0, classLoader, null);
        context.mIsUiContext = true;
        context.mIsAssociatedWithDisplay = true;

        // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
        displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
+1 −1
Original line number Diff line number Diff line
@@ -597,7 +597,7 @@ public class TaskEmbedder {
        if (mTmpDisplayMetrics == null) {
            mTmpDisplayMetrics = new DisplayMetrics();
        }
        mContext.getDisplay().getMetrics(mTmpDisplayMetrics);
        mContext.getDisplayNoVerify().getRealMetrics(mTmpDisplayMetrics);
        return mTmpDisplayMetrics.densityDpi;
    }

+1 −1
Original line number Diff line number Diff line
@@ -2097,7 +2097,7 @@ public class WallpaperManager {

        public ColorManagementProxy(@NonNull Context context) {
            // Get a list of supported wide gamut color spaces.
            Display display = context.getDisplay();
            Display display = context.getDisplayNoVerify();
            if (display != null) {
                mSupportedColorSpaces.addAll(Arrays.asList(display.getSupportedWideColorGamut()));
            }
+25 −2
Original line number Diff line number Diff line
@@ -3495,13 +3495,23 @@ public abstract class Context {
     * <dl>
     *  <dt> {@link #WINDOW_SERVICE} ("window")
     *  <dd> The top-level window manager in which you can place custom
     *  windows.  The returned object is a {@link android.view.WindowManager}.
     *  windows.  The returned object is a {@link android.view.WindowManager}. Must only be obtained
     *  from a visual context such as Activity or a Context created with
     *  {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and
     *  visual bounds of an area on screen.
     *  <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater")
     *  <dd> A {@link android.view.LayoutInflater} for inflating layout resources
     *  in this context.
     *  in this context. Must only be obtained from a visual context such as Activity or a Context
     *  created with {@link #createWindowContext(int, Bundle)}, which are adjusted to the
     *  configuration and visual bounds of an area on screen.
     *  <dt> {@link #ACTIVITY_SERVICE} ("activity")
     *  <dd> A {@link android.app.ActivityManager} for interacting with the
     *  global activity state of the system.
     *  <dt> {@link #WALLPAPER_SERVICE} ("wallpaper")
     *  <dd> A {@link android.service.wallpaper.WallpaperService} for accessing wallpapers in this
     *  context. Must only be obtained from a visual context such as Activity or a Context created
     *  with {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and
     *  visual bounds of an area on screen.
     *  <dt> {@link #POWER_SERVICE} ("power")
     *  <dd> A {@link android.os.PowerManager} for controlling power
     *  management.
@@ -5901,12 +5911,25 @@ public abstract class Context {
     * {@link #createDisplayContext(Display)} to get a display object associated with a Context, or
     * {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id.
     * @return Returns the {@link Display} object this context is associated with.
     * @throws UnsupportedOperationException if the method is called on an instance that is not
     *         associated with any display.
     */
    @Nullable
    public Display getDisplay() {
        throw new RuntimeException("Not implemented. Must override in a subclass.");
    }

    /**
     * A version of {@link #getDisplay()} that does not perform a Context misuse check to be used by
     * legacy APIs.
     * TODO(b/149790106): Fix usages and remove.
     * @hide
     */
    @Nullable
    public Display getDisplayNoVerify() {
        throw new RuntimeException("Not implemented. Must override in a subclass.");
    }

    /**
     * Gets the ID of the display this context is associated with.
     *
Loading