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

Commit 25328c3d authored by Achim Thesmann's avatar Achim Thesmann Committed by mse1969
Browse files

RESTRICT AUTOMERGE Ignore pinned Windows (Consolidated fix)

ag/32704936
ag/35782210
ag/36195227
ag/36359572

Bug: 439984935
Test: atest BackgroundActivityLaunchTest
Flag: EXEMPT BUGFIX
Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:4e09e34b7351dd1d7ec223ec8560e51145de37c9
Merged-In: I9ce93366a0876c7bc594ef818c8598342d70513d
Change-Id: I9ce93366a0876c7bc594ef818c8598342d70513d
parent 861255ea
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -197,7 +197,9 @@ public class BackgroundActivityStartController {

        // don't abort if the callingUid has a visible window or is a persistent system process
        final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
        final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
        final boolean callingUidHasAnyVisibleWindow =
                mService.mVisibleActivityProcessTracker.hasVisibleNotPinnedActivity(callingUid)
                        || mService.mActiveUids.hasNonAppVisibleWindow(callingUid);
        final boolean isCallingUidForeground =
                callingUidHasAnyVisibleWindow
                        || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
@@ -229,7 +231,9 @@ public class BackgroundActivityStartController {
        final boolean realCallingUidHasAnyVisibleWindow =
                (callingUid == realCallingUid)
                        ? callingUidHasAnyVisibleWindow
                        : mService.hasActiveVisibleWindow(realCallingUid);
                        : (mService.mVisibleActivityProcessTracker.hasVisibleNotPinnedActivity(
                                realCallingUid) || mService.mActiveUids.hasNonAppVisibleWindow(
                                realCallingUid));
        final boolean isRealCallingUidForeground =
                (callingUid == realCallingUid)
                        ? isCallingUidForeground
+4 −2
Original line number Diff line number Diff line
@@ -80,7 +80,8 @@ class BackgroundLaunchProcessController {
    @BackgroundActivityStartController.BalCode
    int areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
            int appSwitchState, boolean isCheckingForFgsStart,
            boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
            boolean hasActivityInVisibleTask, boolean inPinnedWindow,
            boolean hasBackgroundActivityStartPrivileges,
            long lastStopAppSwitchesTime, long lastActivityLaunchTime,
            long lastActivityFinishTime) {
        // If app switching is not allowed, we ignore all the start activity grace period
@@ -116,7 +117,8 @@ class BackgroundLaunchProcessController {
                        + "activity starts privileges");
        }
        // Allow if the caller has an activity in any foreground task.
        if (hasActivityInVisibleTask
        if ((isCheckingForFgsStart || !inPinnedWindow)
                && hasActivityInVisibleTask
                && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {
            return BackgroundActivityStartController.logStartAllowedAndReturnCode(
                    BAL_ALLOW_FOREGROUND, /*background*/ false, uid, uid, /*intent*/ null,
+11 −2
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import java.util.function.Predicate;
 * host process with foreground (resumed) activity.
 */
class VisibleActivityProcessTracker {
    private static final Predicate<WindowProcessController> ALWAYS_TRUE = wpc -> true;
    @GuardedBy("mProcMap")
    private final ArrayMap<WindowProcessController, CpuTimeRecord> mProcMap = new ArrayMap<>();
    final Executor mBgExecutor = BackgroundThread.getExecutor();
@@ -80,14 +81,22 @@ class VisibleActivityProcessTracker {
     * {@link ActivityRecord#mVisibleRequested} or {@link ActivityRecord#isVisible()} is true.
     */
    boolean hasVisibleActivity(int uid) {
        return match(uid, null /* predicate */);
        return match(uid, ALWAYS_TRUE);
    }

    /**
     * Returns {@code true} if the uid has a process that contains an activity with
     * {@link ActivityRecord#mVisibleRequested} or {@link ActivityRecord#isVisible()} is true.
     */
    boolean hasVisibleNotPinnedActivity(int uid) {
        return match(uid, wpc -> wpc.hasVisibleNotPinnedActivity());
    }

    private boolean match(int uid, Predicate<WindowProcessController> predicate) {
        synchronized (mProcMap) {
            for (int i = mProcMap.size() - 1; i >= 0; i--) {
                final WindowProcessController wpc = mProcMap.keyAt(i);
                if (wpc.mUid == uid && (predicate == null || predicate.test(wpc))) {
                if (wpc.mUid == uid && predicate.test(wpc)) {
                    return true;
                }
            }
+16 −0
Original line number Diff line number Diff line
@@ -559,6 +559,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
            boolean isCheckingForFgsStart) {
        return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
                appSwitchState, isCheckingForFgsStart, hasActivityInVisibleTask(),
                inPinnedWindowingMode(),
                mInstrumentingWithBackgroundActivityStartPrivileges,
                mAtm.getLastStopAppSwitchesTime(),
                mLastActivityLaunchTime, mLastActivityFinishTime);
@@ -714,6 +715,21 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
        return mHasActivities || mHasRecentTasks;
    }

    /**
     * Check if any Activity is visible (or visibility requested) and not pinned.
     */
    boolean hasVisibleNotPinnedActivity() {
        if (!hasVisibleActivities()) return false;
        for (int i = mActivities.size() - 1; i >= 0; --i) {
            final ActivityRecord activityRecord = mActivities.get(i);
            if ((activityRecord.isVisible() || activityRecord.isVisibleRequested())
                    && !activityRecord.inPinnedWindowingMode()) {
                return true;
            }
        }
        return false;
    }

    @Nullable
    TaskDisplayArea getTopActivityDisplayArea() {
        if (mActivities.isEmpty()) {
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.server.wm;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.app.IApplicationThread;
import android.app.WindowConfiguration;
import android.content.pm.ApplicationInfo;
import android.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;


import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;


/**
 * Tests for the {@link com.android.server.wm.VisibleActivityProcessTracker} class.
 *
 * Build/Install/Run:
 * atest WmTests:VisibleActivityProcessTrackerTests
 */
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class VisibleActivityProcessTrackerTests extends WindowTestsBase {

    enum Visibility { VISIBLE, INVISIBLE, REQUESTED }
    private static final boolean PINNED = true;
    private static final boolean UNPINNED = false;

    private VisibleActivityProcessTracker mTracker;

    @Before
    public void setup() {
        mTracker = mAtm.mVisibleActivityProcessTracker;
    }

    @Test
    public void testVisibleActivity() {
        WindowProcessController wpc = createWindowProcessController();
        assertThat(mTracker.hasVisibleActivity(wpc.mUid)).isFalse();
        mTracker.onAnyActivityVisible(wpc);
        assertThat(mTracker.hasVisibleActivity(wpc.mUid)).isTrue();
        mTracker.onAllActivitiesInvisible(wpc);
        assertThat(mTracker.hasVisibleActivity(wpc.mUid)).isFalse();
    }

    @Test
    public void hasVisibleNotPinnedActivity_whenProcessHasNoActivities_returnsFalse() {
        WindowProcessController wpc = createWindowProcessController();
        assertThat(mTracker.hasVisibleActivity(wpc.mUid)).isFalse();
        assertThat(mTracker.hasVisibleNotPinnedActivity(wpc.mUid)).isFalse();
    }

    @Test
    public void hasVisibleNotPinnedActivity_whenProcessHasOnlyInvisibleActivities_returnsFalse() {
        WindowProcessController wpc = createWindowProcessController();
        addActivity(wpc, Visibility.INVISIBLE, UNPINNED);
        assertThat(mTracker.hasVisibleActivity(wpc.mUid)).isFalse();
        assertThat(mTracker.hasVisibleNotPinnedActivity(wpc.mUid)).isFalse();
    }

    @Test
    public void hasVisibleNotPinnedActivity_whenProcessHasOnlyPinnedActivities_returnsFalse() {
        WindowProcessController wpc = createWindowProcessController();
        addActivity(wpc, Visibility.VISIBLE, PINNED);
        assertThat(mTracker.hasVisibleActivity(wpc.mUid)).isTrue();
        assertThat(mTracker.hasVisibleNotPinnedActivity(wpc.mUid)).isFalse();
    }

    @Test
    public void hasVisibleNotPinnedActivity_whenProcessHasUnpinnedVisibleActivity_returnsTrue() {
        WindowProcessController wpc = createWindowProcessController();
        addActivity(wpc, Visibility.VISIBLE, UNPINNED);
        assertThat(mTracker.hasVisibleActivity(wpc.mUid)).isTrue();
        assertThat(mTracker.hasVisibleNotPinnedActivity(wpc.mUid)).isTrue();
    }

    @Test
    public void hasVisibleNotPinnedActivity_whenProcessHasUnpinnedVisReqActivity_returnsTrue() {
        WindowProcessController wpc = createWindowProcessController();
        addActivity(wpc, Visibility.REQUESTED, UNPINNED);
        assertThat(mTracker.hasVisibleActivity(wpc.mUid)).isTrue();
        assertThat(mTracker.hasVisibleNotPinnedActivity(wpc.mUid)).isTrue();
    }

    WindowProcessController createWindowProcessController() {
        WindowProcessListener mMockListener = mock(WindowProcessListener.class);
        ApplicationInfo info = mock(ApplicationInfo.class);
        info.packageName = "test.package.name";
        WindowProcessController mWpc = new WindowProcessController(
                mAtm, info, null, 0, -1, null, mMockListener);
        mWpc.setThread(mock(IApplicationThread.class));
        return mWpc;
    }

    private static ActivityRecord addActivity(WindowProcessController wpc, Visibility visible,
            boolean pinned) {
        ActivityRecord ar = mock(ActivityRecord.class);
        Task task = mock(Task.class);
        doReturn(task).when(ar).getTask();
        switch (visible) {
            case VISIBLE:
                doReturn(true).when(ar).isVisible();
                doReturn(true).when(ar).isVisibleRequested();
                break;
            case INVISIBLE:
                doReturn(false).when(ar).isVisible();
                doReturn(false).when(ar).isVisibleRequested();
                break;
            case REQUESTED:
                doReturn(false).when(ar).isVisible();
                doReturn(true).when(ar).isVisibleRequested();
                break;
        }
        doReturn(pinned).when(ar).inPinnedWindowingMode();
        when(ar.toString()).thenReturn("ar visible=" + visible + ", pinned=" + pinned);
        wpc.addActivityIfNeeded(ar);
        wpc.computeProcessActivityState();
        return ar;
    }
}