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

Commit 2f91fbf9 authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

Window Z rule for closing the notification shade

We found a few use-cases where the app was displaying a window on top of
the notification shade and wanted to start an activity as a result of
user interaction. In order to show the activity to the user, the app
closes the notification shade using Intent.ACSD. For example, the
accessibility menu (b/119653645) and the IME when replying to a
notification inline (b/74180647), which makes it stay above the shade.

Since these apps already have the privilege to show windows on top of
the shade (because they are doing so), they could already prevent the
user from accessing the shade, so we can allow them to close the shade.
So, exempting this use-case from Intent.ACSD restrictions for apps w/
targetSdk < S.

For targetSdk S+, we don't want apps to be using Intent.ACSD and we
judged that the valid use-case to close the shade is tied to the
activity start. So, in S, we'll automatically close the shade for apps
in that situtation on activity start.

Bug: 159105552
Test: CTS coming
Test: 1. As an a11y service, display a11y windows and send Intent.ACSD
      2. Verify it collapses the shade
      3. Deactivate the service and verify Intent.ACSD doesn't close the
      shade
Test: 1. As an a11y service, display a11y windows and start an activity
      2. Verify the shade is closed
      3. Deactivate the service and verify the shade is not closed when
      activity is started
Change-Id: I783610c1b56739e2926fe9db0c941d40f74121ae
parent d09c22c4
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -40,6 +40,9 @@ public interface StatusBarManagerInternal {

    void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);

    /** Collapses the notification shade. */
    void collapsePanels();

    void dismissKeyboardShortcutsMenu();
    void toggleKeyboardShortcutsMenu(int deviceId);

+10 −0
Original line number Diff line number Diff line
@@ -378,6 +378,16 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
            }
        }

        @Override
        public void collapsePanels() {
            if (mBar != null) {
                try {
                    mBar.animateCollapsePanels();
                } catch (RemoteException ex) {
                }
            }
        }

        @Override
        public void dismissKeyboardShortcutsMenu() {
            if (mBar != null) {
+15 −0
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
import com.android.server.power.ShutdownCheckPoints;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch;
@@ -1589,6 +1590,20 @@ class ActivityStarter {
                    newTransition.abort();
                }
            } else {
                if (!mAvoidMoveToFront && mDoResume
                        && mRootWindowContainer.hasVisibleWindowAboveNotificationShade(
                            r.launchedFromUid)) {
                    // If the UID launching the activity has a visible window on top of the
                    // notification shade and it's launching an activity that's going to be at the
                    // front, we should move the shade out of the way so the user can see it.
                    // We want to avoid the case where the activity is launched on top of a
                    // background task which is not moved to the front.
                    StatusBarManagerInternal statusBar = mService.getStatusBarManagerInternal();
                    if (statusBar != null) {
                        // This results in a async call since the interface is one-way
                        statusBar.collapsePanels();
                    }
                }
                if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
                    // The activity is started new rather than just brought forward, so record
                    // it as an existence change.
+27 −4
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.STOP_APP_SWITCHES;
import static android.app.ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS;
import static android.app.ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
@@ -253,6 +255,7 @@ import com.android.server.firewall.IntentFirewall;
import com.android.server.inputmethod.InputMethodSystemProperty;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;

@@ -342,6 +345,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    /** The cached sys ui service component name from package manager. */
    private ComponentName mSysUiServiceComponent;
    private PermissionPolicyInternal mPermissionPolicyInternal;
    private StatusBarManagerInternal mStatusBarManagerInternal;
    @VisibleForTesting
    final ActivityTaskManagerInternal mInternal;
    PowerManagerInternal mPowerManagerInternal;
@@ -2927,14 +2931,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        }
        if (!canCloseSystemDialogs(pid, uid, process)) {
            // The app can't close system dialogs, throw only if it targets S+
            if (CompatChanges.isChangeEnabled(
                    ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
            if (CompatChanges.isChangeEnabled(LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
                throw new SecurityException(
                        "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS
                                + " broadcast from " + caller + " requires "
                                + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + ".");
            } else if (CompatChanges.isChangeEnabled(
                    ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, uid)) {
            } else if (CompatChanges.isChangeEnabled(DROP_CLOSE_SYSTEM_DIALOGS, uid)) {
                Slog.e(TAG,
                        "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS
                                + " broadcast from " + caller + " requires "
@@ -2978,6 +2980,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                return true;
            }
        }
        // This covers the case where the app is displaying some UI on top of the notification shade
        // and wants to start an activity. The app then sends the intent in order to move the
        // notification shade out of the way and show the activity to the user. This is fine since
        // the caller already has privilege to show a visible window on top of the notification
        // shade, so it can already prevent the user from accessing the shade if it wants to.
        // We only allow for targetSdk < S, for S+ we automatically collapse the shade on
        // startActivity() for these apps.
        if (!CompatChanges.isChangeEnabled(LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
            synchronized (mGlobalLock) {
                if (mRootWindowContainer.hasVisibleWindowAboveNotificationShade(uid)) {
                    return true;
                }
            }
        }
        return false;
    }

@@ -4782,6 +4798,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        return mPermissionPolicyInternal;
    }

    StatusBarManagerInternal getStatusBarManagerInternal() {
        if (mStatusBarManagerInternal == null) {
            mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
        }
        return mStatusBarManagerInternal;
    }

    AppWarnings getAppWarningsLocked() {
        return mAppWarnings;
    }
+22 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -3137,6 +3138,27 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
        });
    }

    /**
     * Returns {@code true} if {@code uid} has a visible window that's above a window of type {@link
     * WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}. If there is no window with type {@link
     * WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}, it returns {@code false}.
     */
    boolean hasVisibleWindowAboveNotificationShade(int uid) {
        boolean[] visibleWindowFound = {false};
        // We only return true if we found the notification shade (ie. window of type
        // TYPE_NOTIFICATION_SHADE). Usually, it should always be there, but if for some reason
        // it isn't, we should better be on the safe side and return false for this.
        return forAllWindows(w -> {
            if (w.mOwnerUid == uid && w.isVisible()) {
                visibleWindowFound[0] = true;
            }
            if (w.mAttrs.type == TYPE_NOTIFICATION_SHADE) {
                return visibleWindowFound[0];
            }
            return false;
        }, true /* traverseTopToBottom */);
    }

    private boolean shouldCloseAssistant(ActivityRecord r, String reason) {
        if (!r.isActivityTypeAssistant()) return false;
        if (reason == SYSTEM_DIALOG_REASON_ASSIST) return false;