1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import android.util.ArrayMap; 20 21 import com.android.internal.annotations.GuardedBy; 22 import com.android.internal.os.BackgroundThread; 23 24 import java.io.PrintWriter; 25 import java.util.concurrent.Executor; 26 import java.util.function.Predicate; 27 28 /** 29 * A quick lookup for all processes with visible activities. It also tracks the CPU usage of 30 * host process with foreground (resumed) activity. 31 */ 32 class VisibleActivityProcessTracker { 33 @GuardedBy("mProcMap") 34 private final ArrayMap<WindowProcessController, CpuTimeRecord> mProcMap = new ArrayMap<>(); 35 final Executor mBgExecutor = BackgroundThread.getExecutor(); 36 final ActivityTaskManagerService mAtms; 37 VisibleActivityProcessTracker(ActivityTaskManagerService atms)38 VisibleActivityProcessTracker(ActivityTaskManagerService atms) { 39 mAtms = atms; 40 } 41 42 /** Called when any activity is visible in the process that didn't have one. */ onAnyActivityVisible(WindowProcessController wpc)43 void onAnyActivityVisible(WindowProcessController wpc) { 44 final CpuTimeRecord r = new CpuTimeRecord(wpc); 45 synchronized (mProcMap) { 46 mProcMap.put(wpc, r); 47 } 48 if (wpc.hasResumedActivity()) { 49 r.mShouldGetCpuTime = true; 50 mBgExecutor.execute(r); 51 } 52 } 53 54 /** Called when all visible activities of the process are no longer visible. */ onAllActivitiesInvisible(WindowProcessController wpc)55 void onAllActivitiesInvisible(WindowProcessController wpc) { 56 final CpuTimeRecord r = removeProcess(wpc); 57 if (r != null && r.mShouldGetCpuTime) { 58 mBgExecutor.execute(r); 59 } 60 } 61 62 /** Called when an activity is resumed on a process which is known to have visible activity. */ onActivityResumedWhileVisible(WindowProcessController wpc)63 void onActivityResumedWhileVisible(WindowProcessController wpc) { 64 final CpuTimeRecord r; 65 synchronized (mProcMap) { 66 r = mProcMap.get(wpc); 67 } 68 if (r != null && !r.mShouldGetCpuTime) { 69 r.mShouldGetCpuTime = true; 70 mBgExecutor.execute(r); 71 } 72 } 73 hasResumedActivity(int uid)74 boolean hasResumedActivity(int uid) { 75 return match(uid, WindowProcessController::hasResumedActivity); 76 } 77 78 /** 79 * Returns {@code true} if the uid has a process that contains an activity with 80 * {@link ActivityRecord#mVisibleRequested} or {@link ActivityRecord#isVisible()} is true. 81 */ hasVisibleActivity(int uid)82 boolean hasVisibleActivity(int uid) { 83 return match(uid, null /* predicate */); 84 } 85 match(int uid, Predicate<WindowProcessController> predicate)86 private boolean match(int uid, Predicate<WindowProcessController> predicate) { 87 synchronized (mProcMap) { 88 for (int i = mProcMap.size() - 1; i >= 0; i--) { 89 final WindowProcessController wpc = mProcMap.keyAt(i); 90 if (wpc.mUid == uid && (predicate == null || predicate.test(wpc))) { 91 return true; 92 } 93 } 94 } 95 return false; 96 } 97 removeProcess(WindowProcessController wpc)98 CpuTimeRecord removeProcess(WindowProcessController wpc) { 99 synchronized (mProcMap) { 100 return mProcMap.remove(wpc); 101 } 102 } 103 dump(PrintWriter pw, String prefix)104 void dump(PrintWriter pw, String prefix) { 105 pw.print(prefix + "VisibleActivityProcess:["); 106 synchronized (mProcMap) { 107 for (int i = mProcMap.size() - 1; i >= 0; i--) { 108 pw.print(" " + mProcMap.keyAt(i)); 109 } 110 } 111 pw.println("]"); 112 } 113 114 /** 115 * Get CPU time in background thread because it will access proc files or the lock of cpu 116 * tracker is held by a background thread. 117 */ 118 private class CpuTimeRecord implements Runnable { 119 private final WindowProcessController mProc; 120 private long mCpuTime; 121 private boolean mHasStartCpuTime; 122 boolean mShouldGetCpuTime; 123 CpuTimeRecord(WindowProcessController wpc)124 CpuTimeRecord(WindowProcessController wpc) { 125 mProc = wpc; 126 } 127 128 @Override run()129 public void run() { 130 if (mProc.getPid() == 0) { 131 // The process is dead. 132 return; 133 } 134 if (!mHasStartCpuTime) { 135 mHasStartCpuTime = true; 136 mCpuTime = mProc.getCpuTime(); 137 } else { 138 final long diff = mProc.getCpuTime() - mCpuTime; 139 if (diff > 0) { 140 mAtms.mAmInternal.updateForegroundTimeIfOnBattery( 141 mProc.mInfo.packageName, mProc.mInfo.uid, diff); 142 } 143 } 144 } 145 } 146 } 147