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

Commit 2eb84b28 authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Add API to check if activity can be started on a display

Some activity launch restrictions apply to virtual displays, which
are not communicated to apps. Currently there is no way to check
this in advance before starting an activity. This means that an app
get an unexpected SecurityException after calling startActivity and
therefore cannot know when to show in their UI a possible option to
launch on a secondary display.

This CL gives adds a public API to check the possibility of launch
on a specific display.

Test: ActivityManagerMultiDisplayTests
Bug: 119575501
Change-Id: Ieb70f0bb79b1a88b7284a19af2efeeb1fcb90f75
parent 009bd1bf
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3934,6 +3934,7 @@ package android.app {
    method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
    method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
    method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
    method public boolean isActivityStartAllowedOnDisplay(android.content.Context, int, android.content.Intent);
    method public boolean isBackgroundRestricted();
    method public deprecated boolean isInLockTaskMode();
    method public boolean isLowRamDevice();
+26 −0
Original line number Diff line number Diff line
@@ -1975,6 +1975,32 @@ public class ActivityManager {
        }
    }

    /**
     * Check if the context is allowed to start an activity on specified display. Some launch
     * restrictions may apply to secondary displays that are private, virtual, or owned by the
     * system, in which case an activity start may throw a {@link SecurityException}. Call this
     * method prior to starting an activity on a secondary display to check if the current context
     * has access to it.
     *
     * @see ActivityOptions#setLaunchDisplayId(int)
     * @see android.view.Display.FLAG_PRIVATE
     * @see android.view.Display.TYPE_VIRTUAL
     *
     * @param context Source context, from which an activity will be started.
     * @param displayId Target display id.
     * @param intent Intent used to launch an activity.
     * @return {@code true} if a call to start an activity on the target display is allowed for the
     * provided context and no {@link SecurityException} will be thrown, {@code false} otherwise.
     */
    public boolean isActivityStartAllowedOnDisplay(Context context, int displayId, Intent intent) {
        try {
            return getTaskService().isActivityStartAllowedOnDisplay(displayId, intent,
                    intent.resolveTypeIfNeeded(context.getContentResolver()), context.getUserId());
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
    }

    /**
     * Information you can retrieve about a particular Service that is
     * currently running in the system.
+2 −0
Original line number Diff line number Diff line
@@ -119,6 +119,8 @@ interface IActivityTaskManager {
            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
            int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
            IBinder permissionToken, boolean ignoreTargetSecurity, int userId);
    boolean isActivityStartAllowedOnDisplay(int displayId, in Intent intent, in String resolvedType,
            int userId);

    void unhandledBack();
    boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
+0 −1
Original line number Diff line number Diff line
@@ -355,7 +355,6 @@ public class ActivityStartController {
                    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
                            null, userId, ActivityStarter.computeResolveFilterUid(
                                    callingUid, realCallingUid, UserHandle.USER_NULL));
                    // TODO: New, check if this is correct
                    aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);

                    if (aInfo != null &&
+36 −0
Original line number Diff line number Diff line
@@ -1425,6 +1425,42 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        }
    }

    /**
     * Public API to check if the client is allowed to start an activity on specified display.
     *
     * If the target display is private or virtual, some restrictions will apply.
     *
     * @param displayId Target display id.
     * @param intent Intent used to launch the activity.
     * @param resolvedType The MIME type of the intent.
     * @param userId The id of the user for whom the call is made.
     * @return {@code true} if a call to start an activity on the target display should succeed and
     *         no {@link SecurityException} will be thrown, {@code false} otherwise.
     */
    @Override
    public final boolean isActivityStartAllowedOnDisplay(int displayId, Intent intent,
            String resolvedType, int userId) {
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();

        try {
            // Collect information about the target of the Intent.
            ActivityInfo aInfo = mStackSupervisor.resolveActivity(intent, resolvedType,
                    0 /* startFlags */, null /* profilerInfo */, userId,
                    ActivityStarter.computeResolveFilterUid(callingUid, callingUid,
                            UserHandle.USER_NULL));
            aInfo = mAmInternal.getActivityInfoForUser(aInfo, userId);

            synchronized (mGlobalLock) {
                return mStackSupervisor.canPlaceEntityOnDisplay(displayId, callingPid, callingUid,
                        aInfo);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    /**
     * This is the internal entry point for handling Activity.finish().
     *