blob: 0e2b71c0263725da74a2368ad55fd6e6e838d71e [file] [log] [blame]
/*
* 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.io.PrintWriter;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
/**
* 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) {
return match(uid, WindowProcessController::hasResumedActivity);
}
/**
* Returns {@code true} if the uid has a process that contains an activity with
* {@link ActivityRecord#mVisibleRequested} or {@link ActivityRecord#isVisible()} is true.
*/
boolean hasVisibleActivity(int uid) {
return match(uid, null /* predicate */);
}
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))) {
return true;
}
}
}
return false;
}
CpuTimeRecord removeProcess(WindowProcessController wpc) {
synchronized (mProcMap) {
return mProcMap.remove(wpc);
}
}
void dump(PrintWriter pw, String prefix) {
pw.print(prefix + "VisibleActivityProcess:[");
synchronized (mProcMap) {
for (int i = mProcMap.size() - 1; i >= 0; i--) {
pw.print(" " + mProcMap.keyAt(i));
}
}
pw.println("]");
}
/**
* 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);
}
}
}
}
}