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

Commit 4ec36a5c authored by Riddle Hsu's avatar Riddle Hsu Committed by Automerger Merge Worker
Browse files

Merge "Remove WM lock from the path of service operations" into sc-dev am:...

Merge "Remove WM lock from the path of service operations" into sc-dev am: 725dae5c am: 07891fff

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13415131

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I3bae149f7cf36d93c6aefeea2f938165f59e5241
parents 87d977b5 07891fff
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -486,13 +486,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
     * Whether normal application switches are allowed; a call to {@link #stopAppSwitches()
     * disables this.
     */
    private boolean mAppSwitchesAllowed = true;
    private volatile boolean mAppSwitchesAllowed = true;

    /**
     * Last stop app switches time, apps finished before this time cannot start background activity
     * even if they are in grace period.
     */
    private long mLastStopAppSwitchesTime;
    private volatile long mLastStopAppSwitchesTime;

    IActivityController mController = null;
    boolean mControllerIsAMonkey = false;
@@ -698,6 +698,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        int OOM_ADJUSTMENT = 1;
        int LRU_UPDATE = 2;
        int PROCESS_CHANGE = 3;
        int START_SERVICE = 4;

        int caller() default NONE;
    }
@@ -4744,6 +4745,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    }

    /** A uid is considered to be foreground if it has a visible non-toast window. */
    @HotPath(caller = HotPath.START_SERVICE)
    boolean hasActiveVisibleWindow(int uid) {
        if (mVisibleActivityProcessTracker.hasVisibleActivity(uid)) {
            return true;
+4 −2
Original line number Diff line number Diff line
@@ -31,7 +31,8 @@ public interface BackgroundActivityStartCallback {
     * Note that if the start was allowed due to a mechanism other than tokens (eg. permission),
     * this won't be called.
     *
     * This will be called holding the WM lock, don't do anything costly here.
     * This will be called holding the WM and local lock, don't do anything costly or invoke AM/WM
     * methods here directly.
     */
    boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, String packageName);

@@ -40,7 +41,8 @@ public interface BackgroundActivityStartCallback {
     * #ACTION_CLOSE_SYSTEM_DIALOGS}, presumably to start activities, based on the originating
     * tokens {@code tokens} currently associated with potential activity starts.
     *
     * This will be called holding the AM and WM lock, don't do anything costly here.
     * This will be called holding the AM and local lock, don't do anything costly or invoke AM/WM
     * methods here directly.
     */
    boolean canCloseSystemDialogs(Collection<IBinder> tokens, int uid);
}
+264 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.function.IntPredicate;

/**
 * A per-process controller to decide whether the process can start activity or foreground service
 * (especially from background). All methods of this class must be thread safe. The caller does not
 * need to hold WM lock, e.g. lock contention of WM lock shouldn't happen when starting service.
 */
class BackgroundLaunchProcessController {
    private static final String TAG =
            TAG_WITH_CLASS_NAME ? "BackgroundLaunchProcessController" : TAG_ATM;

    /** It is {@link ActivityTaskManagerService#hasActiveVisibleWindow(int)}. */
    private final IntPredicate mUidHasActiveVisibleWindowPredicate;

    private final @Nullable BackgroundActivityStartCallback mBackgroundActivityStartCallback;

    /**
     * A set of tokens that currently contribute to this process being temporarily allowed
     * to start activities even if it's not in the foreground. The values of this map are optional
     * (can be null) and are used to trace back the grant to the notification token mechanism.
     */
    @GuardedBy("this")
    private @Nullable ArrayMap<Binder, IBinder> mBackgroundActivityStartTokens;

    /** Set of UIDs of clients currently bound to this process. */
    @GuardedBy("this")
    private @Nullable IntArray mBoundClientUids;

    BackgroundLaunchProcessController(@NonNull IntPredicate uidHasActiveVisibleWindowPredicate,
            @Nullable BackgroundActivityStartCallback callback) {
        mUidHasActiveVisibleWindowPredicate = uidHasActiveVisibleWindowPredicate;
        mBackgroundActivityStartCallback = callback;
    }

    boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
            boolean appSwitchAllowed, boolean isCheckingForFgsStart, boolean hasVisibleActivities,
            boolean hasBackgroundActivityStartPrivileges, long lastStopAppSwitchesTime,
            long lastActivityLaunchTime, long lastActivityFinishTime) {
        // If app switching is not allowed, we ignore all the start activity grace period
        // exception so apps cannot start itself in onPause() after pressing home button.
        if (appSwitchAllowed) {
            // Allow if any activity in the caller has either started or finished very recently, and
            // it must be started or finished after last stop app switches time.
            final long now = SystemClock.uptimeMillis();
            if (now - lastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
                    || now - lastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
                // If activity is started and finished before stop app switch time, we should not
                // let app to be able to start background activity even it's in grace period.
                if (lastActivityLaunchTime > lastStopAppSwitchesTime
                        || lastActivityFinishTime > lastStopAppSwitchesTime) {
                    if (DEBUG_ACTIVITY_STARTS) {
                        Slog.d(TAG, "[Process(" + pid
                                + ")] Activity start allowed: within "
                                + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
                    }
                    return true;
                }
                if (DEBUG_ACTIVITY_STARTS) {
                    Slog.d(TAG, "[Process(" + pid + ")] Activity start within "
                            + ACTIVITY_BG_START_GRACE_PERIOD_MS
                            + "ms grace period but also within stop app switch window");
                }

            }
        }
        // Allow if the proc is instrumenting with background activity starts privs.
        if (hasBackgroundActivityStartPrivileges) {
            if (DEBUG_ACTIVITY_STARTS) {
                Slog.d(TAG, "[Process(" + pid
                        + ")] Activity start allowed: process instrumenting with background "
                        + "activity starts privileges");
            }
            return true;
        }
        // Allow if the caller has an activity in any foreground task.
        if (appSwitchAllowed && hasVisibleActivities) {
            if (DEBUG_ACTIVITY_STARTS) {
                Slog.d(TAG, "[Process(" + pid
                        + ")] Activity start allowed: process has activity in foreground task");
            }
            return true;
        }
        // Allow if the caller is bound by a UID that's currently foreground.
        if (isBoundByForegroundUid()) {
            if (DEBUG_ACTIVITY_STARTS) {
                Slog.d(TAG, "[Process(" + pid
                        + ")] Activity start allowed: process bound by foreground uid");
            }
            return true;
        }
        // Allow if the flag was explicitly set.
        if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
            if (DEBUG_ACTIVITY_STARTS) {
                Slog.d(TAG, "[Process(" + pid
                        + ")] Activity start allowed: process allowed by token");
            }
            return true;
        }
        return false;
    }

    /**
     * If there are no tokens, we don't allow *by token*. If there are tokens and
     * isCheckingForFgsStart is false, we ask the callback if the start is allowed for these tokens,
     * otherwise if there is no callback we allow.
     */
    private boolean isBackgroundStartAllowedByToken(int uid, String packageName,
            boolean isCheckingForFgsStart) {
        synchronized (this) {
            if (mBackgroundActivityStartTokens == null
                    || mBackgroundActivityStartTokens.isEmpty()) {
                return false;
            }
            if (isCheckingForFgsStart) {
                // BG-FGS-start only checks if there is a token.
                return true;
            }

            if (mBackgroundActivityStartCallback == null) {
                // We have tokens but no callback to decide => allow.
                return true;
            }
            // The callback will decide.
            return mBackgroundActivityStartCallback.isActivityStartAllowed(
                    mBackgroundActivityStartTokens.values(), uid, packageName);
        }
    }

    private boolean isBoundByForegroundUid() {
        synchronized (this) {
            if (mBoundClientUids != null) {
                for (int i = mBoundClientUids.size() - 1; i >= 0; i--) {
                    if (mUidHasActiveVisibleWindowPredicate.test(mBoundClientUids.get(i))) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    void setBoundClientUids(ArraySet<Integer> boundClientUids) {
        synchronized (this) {
            if (boundClientUids == null || boundClientUids.isEmpty()) {
                mBoundClientUids = null;
                return;
            }
            if (mBoundClientUids == null) {
                mBoundClientUids = new IntArray();
            } else {
                mBoundClientUids.clear();
            }
            for (int i = boundClientUids.size() - 1; i >= 0; i--) {
                mBoundClientUids.add(boundClientUids.valueAt(i));
            }
        }
    }

    /**
     * Allows background activity starts using token {@code entity}. Optionally, you can provide
     * {@code originatingToken} if you have one such originating token, this is useful for tracing
     * back the grant in the case of the notification token.
     *
     * If {@code entity} is already added, this method will update its {@code originatingToken}.
     */
    void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
            @Nullable IBinder originatingToken) {
        synchronized (this) {
            if (mBackgroundActivityStartTokens == null) {
                mBackgroundActivityStartTokens = new ArrayMap<>();
            }
            mBackgroundActivityStartTokens.put(entity, originatingToken);
        }
    }

    /**
     * Removes token {@code entity} that allowed background activity starts added via {@link
     * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
     */
    void removeAllowBackgroundActivityStartsToken(Binder entity) {
        synchronized (this) {
            if (mBackgroundActivityStartTokens != null) {
                mBackgroundActivityStartTokens.remove(entity);
            }
        }
    }

    /**
     * Returns whether this process is allowed to close system dialogs via a background activity
     * start token that allows the close system dialogs operation (eg. notification).
     */
    boolean canCloseSystemDialogsByToken(int uid) {
        if (mBackgroundActivityStartCallback == null) {
            return false;
        }
        synchronized (this) {
            if (mBackgroundActivityStartTokens == null
                    || mBackgroundActivityStartTokens.isEmpty()) {
                return false;
            }
            return mBackgroundActivityStartCallback.canCloseSystemDialogs(
                    mBackgroundActivityStartTokens.values(), uid);
        }
    }

    void dump(PrintWriter pw, String prefix) {
        synchronized (this) {
            if (mBackgroundActivityStartTokens != null
                    && !mBackgroundActivityStartTokens.isEmpty()) {
                pw.print(prefix);
                pw.println("Background activity start tokens (token: originating token):");
                for (int i = mBackgroundActivityStartTokens.size() - 1; i >= 0; i--) {
                    pw.print(prefix);
                    pw.print("  - ");
                    pw.print(mBackgroundActivityStartTokens.keyAt(i));
                    pw.print(": ");
                    pw.println(mBackgroundActivityStartTokens.valueAt(i));
                }
            }
            if (mBoundClientUids != null && mBoundClientUids.size() > 0) {
                pw.print(prefix);
                pw.print("BoundClientUids:");
                pw.println(Arrays.toString(mBoundClientUids.toArray()));
            }
        }
    }
}
+28 −165

File changed.

Preview size limit exceeded, changes collapsed.