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

Commit a3716118 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Let sender permission allow background launch when starting recents

Currently the recents activity is started by a pending intent created
by launcher. And the sender is system server.
(HIERARCHY_OP_TYPE_PENDING_INTENT in WindowOrganizerController)

If the intent creator doesn't have visible windows, e.g. launcher is
occluded by its another embedded of another package, then the background
launch policy will check whether the intent sender is allowed. But
system server also doesn't have visible windows, which causes
BackgroundActivityStartController#
checkBackgroundActivityStartAllowedBySender to return BalVerdict.BLOCK.
Which will set MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS to disallow
moving the target task to front.
See I72a6c22a5fb27aeac52a4e5d46c6a16e28ee6757 for the block policy.

Although currently the recents activity can still move to front because
some places miss to check blocking the launch. Then it is like just
using a security hole.

By adding the background launch permission hint to ActivityOptions,
BackgroundActivityStartController#hasBalPermission will check if the
real caller has permission START_ACTIVITIES_FROM_BACKGROUND. Then
it will pass because the intent sender is system server.

Bug: 341618283
Flag: EXEMPT bugfix
Test: atest NexusLauncherTests: \
            com.android.quickstep.TaskAnimationManagerTest
Test: Swipe to minus one screen. Click a news item to Launch chrome.
      Swipe from bottom to return to home. There should not have an
      error log:
      "Without Android 15 BAL hardening this activity would be moved
       to the foreground ... only the creator of the PendingIntent
       allows BAL. realCallingPackage: android.uid.system:1000 ..."
       (from ActivityStarter#logPIOnlyCreatorAllowsBAL)
Change-Id: I19153f6553c09421bca248d4ff9110d168b34f98
parent 80dbe9ae
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -100,6 +100,11 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
    TaskAnimationManager(Context ctx) {
        mCtx = ctx;
    }

    SystemUiProxy getSystemUiProxy() {
        return SystemUiProxy.INSTANCE.get(mCtx);
    }

    /**
     * Preloads the recents animation.
     */
@@ -153,7 +158,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
        final BaseContainerInterface containerInterface = gestureState.getContainerInterface();
        mLastGestureState = gestureState;
        RecentsAnimationCallbacks newCallbacks = new RecentsAnimationCallbacks(
                SystemUiProxy.INSTANCE.get(mCtx), containerInterface.allowMinimizeSplitScreen());
                getSystemUiProxy(), containerInterface.allowMinimizeSplitScreen());
        mCallbacks = newCallbacks;
        mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
            @Override
@@ -260,7 +265,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
                }

                RemoteAnimationTarget[] nonAppTargets = ENABLE_SHELL_TRANSITIONS
                        ? null : SystemUiProxy.INSTANCE.get(mCtx).onStartingSplitLegacy(
                        ? null : getSystemUiProxy().onStartingSplitLegacy(
                                appearedTaskTargets);
                if (nonAppTargets == null) {
                    nonAppTargets = new RemoteAnimationTarget[0];
@@ -327,12 +332,13 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn

        if (ENABLE_SHELL_TRANSITIONS) {
            final ActivityOptions options = ActivityOptions.makeBasic();
            options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
            // Use regular (non-transient) launch for all apps page to control IME.
            if (!containerInterface.allowAllAppsFromOverview()) {
                options.setTransientLaunch();
            }
            options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
            mRecentsAnimationStartPending = SystemUiProxy.INSTANCE.get(mCtx)
            mRecentsAnimationStartPending = getSystemUiProxy()
                    .startRecentsActivity(intent, options, mCallbacks);
            if (enableHandleDelayedGestureCallbacks()) {
                ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.quickstep;

import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;

import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@SmallTest
public class TaskAnimationManagerTest {

    @Mock
    private Context mContext;

    @Mock
    private SystemUiProxy mSystemUiProxy;

    private TaskAnimationManager mTaskAnimationManager;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mTaskAnimationManager = new TaskAnimationManager(mContext) {
            @Override
            SystemUiProxy getSystemUiProxy() {
                return mSystemUiProxy;
            }
        };
    }

    @Test
    public void startRecentsActivity_allowBackgroundLaunch() {
        assumeTrue(TaskAnimationManager.ENABLE_SHELL_TRANSITIONS);

        final LauncherActivityInterface activityInterface = mock(LauncherActivityInterface.class);
        final GestureState gestureState = mock(GestureState.class);
        final RecentsAnimationCallbacks.RecentsAnimationListener listener =
                mock(RecentsAnimationCallbacks.RecentsAnimationListener.class);
        doReturn(activityInterface).when(gestureState).getContainerInterface();
        mTaskAnimationManager.startRecentsAnimation(gestureState, new Intent(), listener);

        final ArgumentCaptor<ActivityOptions> optionsCaptor =
                ArgumentCaptor.forClass(ActivityOptions.class);
        verify(mSystemUiProxy).startRecentsActivity(any(), optionsCaptor.capture(), any());
        assertTrue(optionsCaptor.getValue()
                .isPendingIntentBackgroundActivityLaunchAllowedByPermission());
    }
}