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

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

Track process with visible activities

- A quick lookup without locking WM to provide whether a process
  or uid has visible or resumed activity.
- Move cpu usage collection from the path of resume/pause in WM
  lock to background thread.
- Do not get cpu time if the next activity is still in the same
  process, to reduce unnecessary accesses of proc file. E.g.
  if there are 3 foreground activity switches in the same process,
  it can save 6 accesses.
- Change hasForegroundActivities to use source state. It was set
  by OomAdjuster when ComputeOomAdjCallback#onVisibleActivity or
  onPausedActivity or onStoppingActivity is called, which may
  not be the latest state.

Bug: 171989664
Test: WindowProcessControllerTests#testComputeProcessActivityState
Change-Id: I3fb42e455232c8718549af5b7bb1ca638558d827
parent 11cbe5ff
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -1252,7 +1252,6 @@ class ProcessRecord implements WindowProcessListener {

    void setHasForegroundActivities(boolean hasForegroundActivities) {
        mHasForegroundActivities = hasForegroundActivities;
        mWindowProcessController.setHasForegroundActivities(hasForegroundActivities);
    }

    boolean hasForegroundActivities() {
+0 −11
Original line number Diff line number Diff line
@@ -445,7 +445,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    private Task task;              // the task this is in.
    private long createTime = System.currentTimeMillis();
    long lastVisibleTime;         // last time this activity became visible
    long cpuTimeAtResume;         // the cpu time of host process at the time of resuming activity
    long pauseTime;               // last time we started pausing the activity
    long launchTickTime;          // base time for launch tick messages
    long topResumedStateLossTime; // last time we reported top resumed state loss to an activity
@@ -5010,16 +5009,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        resumeKeyDispatchingLocked();
        final Task stack = getRootTask();
        mStackSupervisor.mNoAnimActivities.clear();

        // Mark the point when the activity is resuming
        // TODO: To be more accurate, the mark should be before the onCreate,
        //       not after the onResume. But for subsequent starts, onResume is fine.
        if (hasProcess()) {
            cpuTimeAtResume = app.getCpuTime();
        } else {
            cpuTimeAtResume = 0; // Couldn't get the cpu time of process
        }

        returningOptions = null;

        if (canTurnScreenOn()) {
+4 −10
Original line number Diff line number Diff line
@@ -366,6 +366,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    PendingIntentController mPendingIntentController;
    IntentFirewall mIntentFirewall;

    final VisibleActivityProcessTracker mVisibleActivityProcessTracker;

    /* Global service lock used by the package the owns this service. */
    final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
    /**
@@ -741,6 +743,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        mSystemThread = ActivityThread.currentActivityThread();
        mUiContext = mSystemThread.getSystemUiContext();
        mLifecycleManager = new ClientLifecycleManager();
        mVisibleActivityProcessTracker = new VisibleActivityProcessTracker(this);
        mInternal = new LocalService();
        GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
        mWindowOrganizerController = new WindowOrganizerController(this);
@@ -6103,16 +6106,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {

        @Override
        public boolean hasResumedActivity(int uid) {
            synchronized (mGlobalLock) {
                final ArraySet<WindowProcessController> processes = mProcessMap.getProcesses(uid);
                for (int i = 0, n = processes.size(); i < n; i++) {
                    final WindowProcessController process = processes.valueAt(i);
                    if (process.hasResumedActivity()) {
                        return true;
                    }
                }
            }
            return false;
            return mVisibleActivityProcessTracker.hasResumedActivity(uid);
        }

        public void setBackgroundActivityStartCallback(
+0 −14
Original line number Diff line number Diff line
@@ -162,7 +162,6 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
@@ -5689,19 +5688,6 @@ class Task extends WindowContainer<WindowContainer> {

        if (prev != null) {
            prev.resumeKeyDispatchingLocked();

            if (prev.hasProcess() && prev.cpuTimeAtResume > 0) {
                final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
                if (diff > 0) {
                    final Runnable r = PooledLambda.obtainRunnable(
                            ActivityManagerInternal::updateForegroundTimeIfOnBattery,
                            mAtmService.mAmInternal, prev.info.packageName,
                            prev.info.applicationInfo.uid,
                            diff);
                    mAtmService.mH.post(r);
                }
            }
            prev.cpuTimeAtResume = 0; // reset it
        }

        mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
+122 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.util.ArrayMap;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;

import java.util.concurrent.Executor;

/**
 * A quick lookup for all processes with visible activities. It also tracks the CPU usage of
 * host process with foreground (resumed) activity.
 */
class VisibleActivityProcessTracker {
    @GuardedBy("mProcMap")
    private final ArrayMap<WindowProcessController, CpuTimeRecord> mProcMap = new ArrayMap<>();
    final Executor mBgExecutor = BackgroundThread.getExecutor();
    final ActivityTaskManagerService mAtms;

    VisibleActivityProcessTracker(ActivityTaskManagerService atms) {
        mAtms = atms;
    }

    /** Called when any activity is visible in the process that didn't have one. */
    void onAnyActivityVisible(WindowProcessController wpc) {
        final CpuTimeRecord r = new CpuTimeRecord(wpc);
        synchronized (mProcMap) {
            mProcMap.put(wpc, r);
        }
        if (wpc.hasResumedActivity()) {
            r.mShouldGetCpuTime = true;
            mBgExecutor.execute(r);
        }
    }

    /** Called when all visible activities of the process are no longer visible. */
    void onAllActivitiesInvisible(WindowProcessController wpc) {
        final CpuTimeRecord r = removeProcess(wpc);
        if (r != null && r.mShouldGetCpuTime) {
            mBgExecutor.execute(r);
        }
    }

    /** Called when an activity is resumed on a process which is known to have visible activity. */
    void onActivityResumedWhileVisible(WindowProcessController wpc) {
        final CpuTimeRecord r;
        synchronized (mProcMap) {
            r = mProcMap.get(wpc);
        }
        if (r != null && !r.mShouldGetCpuTime) {
            r.mShouldGetCpuTime = true;
            mBgExecutor.execute(r);
        }
    }

    boolean hasResumedActivity(int uid) {
        synchronized (mProcMap) {
            for (int i = mProcMap.size() - 1; i >= 0; i--) {
                final WindowProcessController wpc = mProcMap.keyAt(i);
                if (wpc.mUid == uid && wpc.hasResumedActivity()) {
                    return true;
                }
            }
        }
        return false;
    }

    CpuTimeRecord removeProcess(WindowProcessController wpc) {
        synchronized (mProcMap) {
            return mProcMap.remove(wpc);
        }
    }

    /**
     * Get CPU time in background thread because it will access proc files or the lock of cpu
     * tracker is held by a background thread.
     */
    private class CpuTimeRecord implements Runnable {
        private final WindowProcessController mProc;
        private long mCpuTime;
        private boolean mHasStartCpuTime;
        boolean mShouldGetCpuTime;

        CpuTimeRecord(WindowProcessController wpc) {
            mProc = wpc;
        }

        @Override
        public void run() {
            if (mProc.getPid() == 0) {
                // The process is dead.
                return;
            }
            if (!mHasStartCpuTime) {
                mHasStartCpuTime = true;
                mCpuTime = mProc.getCpuTime();
            } else {
                final long diff = mProc.getCpuTime() - mCpuTime;
                if (diff > 0) {
                    mAtms.mAmInternal.updateForegroundTimeIfOnBattery(
                            mProc.mInfo.packageName, mProc.mInfo.uid, diff);
                }
            }
        }
    }
}
Loading