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

Commit fb1bf69d authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Set permissions for launching on private displays

- System UIDs must be allowed to launch anything and everywhere.
- Display owner must be allowed to launch activities on it.
- Apps that are already on target display must be allowed to launch
  there.
- All other apps mustn't be allowed to launch on private displays.

Bug: 34230873
Test: android.server.cts.ActivityManagerDisplayTests
Test: #testPermissionLaunchFromSystem
Test: #testPermissionLaunchFromAppOnSecondary
Test: #testPermissionLaunchFromOwner
Test: #testPermissionLaunchFromDifferentApp
Change-Id: Ic98005649a6368370c512e822cba4e9decc18ae9
parent b4fb0ca6
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package android.hardware.display;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.PowerManager;
import android.util.IntArray;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;

@@ -146,6 +148,21 @@ public abstract class DisplayManagerInternal {
     */
    public abstract void setDisplayOffsets(int displayId, int x, int y);

    /**
     * Provide a list of UIDs that are present on the display and are allowed to access it.
     *
     * @param displayAccessUIDs Mapping displayId -> int array of UIDs.
     */
    public abstract void setDisplayAccessUIDs(SparseArray<IntArray> displayAccessUIDs);

    /**
     * Check if specified UID's content is present on display and should be granted access to it.
     *
     * @param uid UID to be checked.
     * @param displayId id of the display where presence of the content is checked.
     * */
    public abstract boolean isUidPresentOnDisplay(int uid, int displayId);

    /**
     * Describes the requested power state of the display.
     *
+2 −1
Original line number Diff line number Diff line
@@ -750,13 +750,14 @@ public interface WindowManagerPolicy {
     * @param windowFlags Window layout flags.
     * @param overrideConfig override configuration to consider when generating
     *        context to for resources.
     * @param displayId Id of the display to show the splash screen at.
     *
     * @return The starting surface.
     *
     */
    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
            int logo, int windowFlags, Configuration overrideConfig);
            int logo, int windowFlags, Configuration overrideConfig, int displayId);

    /**
     * Prepare for a window being added to the window manager.  You can throw an
+8 −4
Original line number Diff line number Diff line
@@ -149,6 +149,7 @@ final class ActivityRecord implements AppWindowContainerListener {
    AppWindowContainerController mWindowContainerController;
    final ActivityInfo info; // all about me
    final ApplicationInfo appInfo; // information about activity's app
    final int launchedFromPid; // always the pid who started the activity.
    final int launchedFromUid; // always the uid who started the activity.
    final String launchedFromPackage; // always the package who started the activity.
    final int userId;          // Which user is this running for?
@@ -595,7 +596,7 @@ final class ActivityRecord implements AppWindowContainerListener {
        return ResolverActivity.class.getName().equals(realActivity.getClassName());
    }

    ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
    ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromPid,
            int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
            ActivityInfo aInfo, Configuration _configuration,
            ActivityRecord _resultTo, String _resultWho, int _reqCode,
@@ -605,6 +606,7 @@ final class ActivityRecord implements AppWindowContainerListener {
        service = _service;
        appToken = new Token(this);
        info = aInfo;
        launchedFromPid = _launchedFromPid;
        launchedFromUid = _launchedFromUid;
        launchedFromPackage = _launchedFromPackage;
        userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
@@ -2206,9 +2208,11 @@ final class ActivityRecord implements AppWindowContainerListener {
            throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent +
                    " resolvedType=" + resolvedType);
        }
        final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid,
                launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(),
                null, null, 0, componentSpecified, false, stackSupervisor, null, null, null);
        final ActivityRecord r = new ActivityRecord(service, null /* caller */,
                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
                aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
                0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
                stackSupervisor, null /* container */, null /* options */, null /* sourceRecord */);

        r.persistentState = persistentState;
        r.taskDescription = taskDescription;
+30 −2
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -195,6 +196,12 @@ final class ActivityStack extends ConfigurationContainer {
        return mActivityContainer.mActivityDisplay;
    }

    @Override
    void onParentChanged() {
        super.onParentChanged();
        mStackSupervisor.updateUIDsPresentOnDisplay();
    }

    enum ActivityState {
        INITIALIZING,
        RESUMED,
@@ -680,6 +687,27 @@ final class ActivityStack extends ConfigurationContainer {
        return null;
    }

    boolean isInStackLocked(TaskRecord task) {
        return mTaskHistory.contains(task);
    }

    /** Checks if there are tasks with specific UID in the stack. */
    boolean isUidPresent(int uid) {
        for (TaskRecord task : mTaskHistory) {
            if (task.effectiveUid == uid) {
                return true;
            }
        }
        return false;
    }

    /** Get all UIDs that are present in the stack. */
    void getPresentUIDs(IntArray presentUIDs) {
        for (TaskRecord task : mTaskHistory) {
            presentUIDs.add(task.effectiveUid);
        }
    }

    final boolean updateLRUListLocked(ActivityRecord r) {
        final boolean hadit = mLRUActivities.remove(r);
        mLRUActivities.add(r);
@@ -4904,13 +4932,13 @@ final class ActivityStack extends ConfigurationContainer {
        final boolean toTop = position >= mTaskHistory.size();
        final ActivityStack prevStack = preAddTask(task, reason, toTop);

        mTaskHistory.add(position, task);
        task.setStack(this);

        if (toTop) {
            updateTaskReturnToForTopInsertion(task);
        }

        mTaskHistory.add(position, task);
        updateTaskMovement(task, toTop);

        postAddTask(task, prevStack);
@@ -4927,8 +4955,8 @@ final class ActivityStack extends ConfigurationContainer {

        final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
        final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
        task.setStack(this);
        insertTaskAtPosition(task, index);
        task.setStack(this);
        postAddTask(task, null /* prevStack */);

        if (wasResumed) {
+97 −15
Original line number Diff line number Diff line
@@ -125,6 +125,7 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.VirtualDisplay;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
@@ -154,6 +155,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -380,7 +382,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
    /** Mapping from displayId to display current state */
    private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();

    InputManagerInternal mInputManagerInternal;
    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();

    private DisplayManagerInternal mDisplayManagerInternal;
    private InputManagerInternal mInputManagerInternal;

    /** The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
     * may be finished until there is only one entry left. If this is empty the system is not
@@ -580,6 +585,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
            mDisplayManager =
                    (DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
            mDisplayManager.registerDisplayListener(this, null);
            mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);

            Display[] displays = mDisplayManager.getDisplays();
            for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
@@ -1519,11 +1525,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
            // Check if someone tries to launch an activity on a private display with a different
            // owner.
            final int launchDisplayId = options.getLaunchDisplayId();
            if (launchDisplayId != INVALID_DISPLAY) {
                final ActivityDisplay activityDisplay = mActivityDisplays.get(launchDisplayId);
                if (activityDisplay != null
                        && (activityDisplay.mDisplay.getFlags() & FLAG_PRIVATE) != 0) {
                    if (activityDisplay.mDisplay.getOwnerUid() != callingUid) {
            if (launchDisplayId != INVALID_DISPLAY
                    && !isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, launchDisplayId)) {
                final String msg = "Permission Denial: starting " + intent.toString()
                        + " from " + callerApp + " (pid=" + callingPid
                        + ", uid=" + callingUid + ") with launchDisplayId="
@@ -1532,12 +1535,66 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
                throw new SecurityException(msg);
            }
        }

        return true;
    }

    /** Check if caller is allowed to launch activities on specified display. */
    boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId) {
        if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: displayId=" + launchDisplayId
                + " callingPid=" + callingPid + " callingUid=" + callingUid);

        final ActivityDisplay activityDisplay = mActivityDisplays.get(launchDisplayId);
        if (activityDisplay == null) {
            Slog.w(TAG, "Launch on display check: display not found");
            return false;
        }

        if (!activityDisplay.isPrivate()) {
            // Anyone can launch on a public display.
            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                    + " allow launch on public display");
            return true;
        }

        // Check if the caller is the owner of the display.
        if (activityDisplay.mDisplay.getOwnerUid() == callingUid) {
            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                    + " allow launch for owner of the display");
            return true;
        }

        // Check if caller is present on display
        if (activityDisplay.isUidPresent(callingUid)) {
            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                    + " allow launch for caller present on the display");
            return true;
        }

        // Check if the caller can launch anything.
        final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
                callingUid);
        if (startAnyPerm == PERMISSION_GRANTED) {
            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                    + " allow launch any on display");
            return true;
        }

        Slog.w(TAG, "Launch on display check: denied");
        return false;
    }

    /** Update lists of UIDs that are present on displays and have access to them. */
    void updateUIDsPresentOnDisplay() {
        mDisplayAccessUIDs.clear();
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
            mDisplayAccessUIDs.append(activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
        }
        // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
        mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
    }

    UserInfo getUserInfo(int userId) {
        final long identity = Binder.clearCallingIdentity();
        try {
@@ -4568,6 +4625,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D

        ActivityRecord mVisibleBehindActivity;

        /** Array of all UIDs that are present on the display. */
        private IntArray mDisplayAccessUIDs = new IntArray();

        ActivityDisplay() {
        }

@@ -4630,6 +4690,28 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        protected ConfigurationContainer getParent() {
            return ActivityStackSupervisor.this;
        }

        boolean isPrivate() {
            return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
        }

        boolean isUidPresent(int uid) {
            for (ActivityStack stack : mStacks) {
                if (stack.isUidPresent(uid)) {
                    return true;
                }
            }
            return false;
        }

        /** Update and get all UIDs that are present on the display and have access to it. */
        private IntArray getPresentUIDs() {
            mDisplayAccessUIDs.clear();
            for (ActivityStack stack : mStacks) {
                stack.getPresentUIDs(mDisplayAccessUIDs);
            }
            return mDisplayAccessUIDs;
        }
    }

    class VirtualActivityDisplay extends ActivityDisplay {
Loading