1 /*
2  * Copyright (C) 2022 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.appop;
18 
19 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
20 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
21 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
22 import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
23 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
24 import static android.app.ActivityManager.ProcessCapability;
25 import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
26 import static android.app.AppOpsManager.MODE_ALLOWED;
27 import static android.app.AppOpsManager.MODE_FOREGROUND;
28 import static android.app.AppOpsManager.MODE_IGNORED;
29 import static android.app.AppOpsManager.OP_CAMERA;
30 import static android.app.AppOpsManager.OP_NONE;
31 import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
32 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
33 import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
34 import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
35 import static android.app.AppOpsManager.UID_STATE_TOP;
36 
37 import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
38 
39 import android.app.ActivityManager;
40 import android.app.ActivityManagerInternal;
41 import android.app.AppOpsManager;
42 import android.os.Handler;
43 import android.util.ArrayMap;
44 import android.util.SparseArray;
45 import android.util.SparseBooleanArray;
46 import android.util.SparseIntArray;
47 import android.util.SparseLongArray;
48 import android.util.TimeUtils;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.os.Clock;
52 import com.android.internal.util.function.pooled.PooledLambda;
53 
54 import java.io.PrintWriter;
55 import java.util.concurrent.Executor;
56 
57 class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
58 
59     private static final String LOG_TAG = AppOpsUidStateTrackerImpl.class.getSimpleName();
60 
61     private final DelayableExecutor mExecutor;
62     private final Clock mClock;
63     private ActivityManagerInternal mActivityManagerInternal;
64     private AppOpsService.Constants mConstants;
65 
66     private SparseIntArray mUidStates = new SparseIntArray();
67     private SparseIntArray mPendingUidStates = new SparseIntArray();
68     private SparseIntArray mCapability = new SparseIntArray();
69     private SparseIntArray mPendingCapability = new SparseIntArray();
70     private SparseBooleanArray mAppWidgetVisible = new SparseBooleanArray();
71     private SparseBooleanArray mPendingAppWidgetVisible = new SparseBooleanArray();
72     private SparseLongArray mPendingCommitTime = new SparseLongArray();
73     private SparseBooleanArray mPendingGone = new SparseBooleanArray();
74 
75     private ArrayMap<UidStateChangedCallback, Executor>
76             mUidStateChangedCallbacks = new ArrayMap<>();
77 
78     private final EventLog mEventLog;
79 
80     @VisibleForTesting
81     interface DelayableExecutor extends Executor {
82 
execute(Runnable runnable)83         void execute(Runnable runnable);
84 
executeDelayed(Runnable runnable, long delay)85         void executeDelayed(Runnable runnable, long delay);
86     }
87 
AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal, Handler handler, Executor lockingExecutor, Clock clock, AppOpsService.Constants constants)88     AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
89             Handler handler, Executor lockingExecutor, Clock clock,
90             AppOpsService.Constants constants) {
91 
92         this(activityManagerInternal, new DelayableExecutor() {
93             @Override
94             public void execute(Runnable runnable) {
95                 handler.post(() -> lockingExecutor.execute(runnable));
96             }
97 
98             @Override
99             public void executeDelayed(Runnable runnable, long delay) {
100                 handler.postDelayed(() -> lockingExecutor.execute(runnable), delay);
101             }
102         }, clock, constants, handler.getLooper().getThread());
103     }
104 
105     @VisibleForTesting
AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal, DelayableExecutor executor, Clock clock, AppOpsService.Constants constants, Thread executorThread)106     AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
107             DelayableExecutor executor, Clock clock, AppOpsService.Constants constants,
108             Thread executorThread) {
109         mActivityManagerInternal = activityManagerInternal;
110         mExecutor = executor;
111         mClock = clock;
112         mConstants = constants;
113 
114         mEventLog = new EventLog(executor, executorThread);
115     }
116 
117     @Override
getUidState(int uid)118     public int getUidState(int uid) {
119         return getUidStateLocked(uid);
120     }
121 
getUidStateLocked(int uid)122     private int getUidStateLocked(int uid) {
123         updateUidPendingStateIfNeeded(uid);
124         return mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
125     }
126 
127     @Override
evalMode(int uid, int code, int mode)128     public int evalMode(int uid, int code, int mode) {
129         if (mode != MODE_FOREGROUND) {
130             return mode;
131         }
132 
133         int uidState = getUidState(uid);
134         int uidCapability = getUidCapability(uid);
135         int result = evalModeInternal(uid, code, uidState, uidCapability);
136 
137         mEventLog.logEvalForegroundMode(uid, uidState, uidCapability, code, result);
138         return result;
139     }
140 
evalModeInternal(int uid, int code, int uidState, int uidCapability)141     private int evalModeInternal(int uid, int code, int uidState, int uidCapability) {
142 
143         if (getUidAppWidgetVisible(uid) || mActivityManagerInternal.isPendingTopUid(uid)
144                 || mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid)) {
145             return MODE_ALLOWED;
146         }
147 
148         int opCapability = getOpCapability(code);
149         if (opCapability != PROCESS_CAPABILITY_NONE) {
150             if ((uidCapability & opCapability) == 0) {
151                 return MODE_IGNORED;
152             } else {
153                 return MODE_ALLOWED;
154             }
155         }
156 
157         if (uidState > AppOpsManager.resolveFirstUnrestrictedUidState(code)) {
158             return MODE_IGNORED;
159         }
160 
161         return MODE_ALLOWED;
162     }
163 
getOpCapability(int opCode)164     private int getOpCapability(int opCode) {
165         switch (opCode) {
166             case AppOpsManager.OP_FINE_LOCATION:
167             case AppOpsManager.OP_COARSE_LOCATION:
168             case AppOpsManager.OP_MONITOR_LOCATION:
169             case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
170                 return PROCESS_CAPABILITY_FOREGROUND_LOCATION;
171             case OP_CAMERA:
172                 return PROCESS_CAPABILITY_FOREGROUND_CAMERA;
173             case OP_RECORD_AUDIO:
174             case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO:
175                 return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
176             default:
177                 return PROCESS_CAPABILITY_NONE;
178         }
179     }
180 
181     @Override
isUidInForeground(int uid)182     public boolean isUidInForeground(int uid) {
183         return evalMode(uid, OP_NONE, MODE_FOREGROUND) == MODE_ALLOWED;
184     }
185 
186     @Override
addUidStateChangedCallback(Executor executor, UidStateChangedCallback callback)187     public void addUidStateChangedCallback(Executor executor, UidStateChangedCallback callback) {
188         if (mUidStateChangedCallbacks.containsKey(callback)) {
189             throw new IllegalStateException("Callback is already registered.");
190         }
191 
192         mUidStateChangedCallbacks.put(callback, executor);
193     }
194 
195     @Override
removeUidStateChangedCallback(UidStateChangedCallback callback)196     public void removeUidStateChangedCallback(UidStateChangedCallback callback) {
197         if (!mUidStateChangedCallbacks.containsKey(callback)) {
198             throw new IllegalStateException("Callback is not registered.");
199         }
200         mUidStateChangedCallbacks.remove(callback);
201     }
202 
203     @Override
updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible)204     public void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible) {
205         int numUids = uidPackageNames.size();
206         for (int i = 0; i < numUids; i++) {
207             int uid = uidPackageNames.keyAt(i);
208             mPendingAppWidgetVisible.put(uid, visible);
209 
210             commitUidPendingState(uid);
211         }
212     }
213 
214     @Override
updateUidProcState(int uid, int procState, int capability)215     public void updateUidProcState(int uid, int procState, int capability) {
216         int uidState = processStateToUidState(procState);
217 
218         int prevUidState = mUidStates.get(uid, AppOpsManager.MIN_PRIORITY_UID_STATE);
219         int prevCapability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
220         int pendingUidState = mPendingUidStates.get(uid, MIN_PRIORITY_UID_STATE);
221         int pendingCapability = mPendingCapability.get(uid, PROCESS_CAPABILITY_NONE);
222         long pendingStateCommitTime = mPendingCommitTime.get(uid, 0);
223         if ((pendingStateCommitTime == 0
224                 && (uidState != prevUidState || capability != prevCapability))
225                 || (pendingStateCommitTime != 0
226                 && (uidState != pendingUidState || capability != pendingCapability))) {
227 
228             // If this process update results in a capability or uid state change, log it. It's
229             // not interesting otherwise.
230             mEventLog.logUpdateUidProcState(uid, procState, capability);
231             mPendingUidStates.put(uid, uidState);
232             mPendingCapability.put(uid, capability);
233 
234             if (procState == PROCESS_STATE_NONEXISTENT) {
235                 mPendingGone.put(uid, true);
236                 commitUidPendingState(uid);
237             } else if (uidState < prevUidState
238                     || (uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
239                     && prevUidState > UID_STATE_MAX_LAST_NON_RESTRICTED)) {
240                 // We are moving to a more important state, or the new state may be in the
241                 // foreground and the old state is in the background, then always do it
242                 // immediately.
243                 commitUidPendingState(uid);
244             } else if (uidState == prevUidState && capability != prevCapability) {
245                 // No change on process state, but process capability has changed.
246                 commitUidPendingState(uid);
247             } else if (uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED) {
248                 // We are moving to a less important state, but it doesn't cross the restriction
249                 // threshold.
250                 commitUidPendingState(uid);
251             } else if (pendingStateCommitTime == 0) {
252                 // We are moving to a less important state for the first time,
253                 // delay the application for a bit.
254                 final long settleTime;
255                 if (prevUidState <= UID_STATE_TOP) {
256                     settleTime = mConstants.TOP_STATE_SETTLE_TIME;
257                 } else if (prevUidState <= UID_STATE_FOREGROUND_SERVICE) {
258                     settleTime = mConstants.FG_SERVICE_STATE_SETTLE_TIME;
259                 } else {
260                     settleTime = mConstants.BG_STATE_SETTLE_TIME;
261                 }
262                 final long commitTime = mClock.elapsedRealtime() + settleTime;
263                 mPendingCommitTime.put(uid, commitTime);
264 
265                 mExecutor.executeDelayed(PooledLambda.obtainRunnable(
266                                 AppOpsUidStateTrackerImpl::updateUidPendingStateIfNeeded, this,
267                                 uid), settleTime + 1);
268             }
269         }
270     }
271 
272     @Override
dumpUidState(PrintWriter pw, int uid, long nowElapsed)273     public void dumpUidState(PrintWriter pw, int uid, long nowElapsed) {
274         int state = mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
275         // if no pendingState set to state to suppress output
276         int pendingState = mPendingUidStates.get(uid, state);
277         pw.print("    state=");
278         pw.println(AppOpsManager.getUidStateName(state));
279         if (state != pendingState) {
280             pw.print("    pendingState=");
281             pw.println(AppOpsManager.getUidStateName(pendingState));
282         }
283         int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
284         // if no pendingCapability set to capability to suppress output
285         int pendingCapability = mPendingCapability.get(uid, capability);
286         pw.print("    capability=");
287         ActivityManager.printCapabilitiesFull(pw, capability);
288         pw.println();
289         if (capability != pendingCapability) {
290             pw.print("    pendingCapability=");
291             ActivityManager.printCapabilitiesFull(pw, pendingCapability);
292             pw.println();
293         }
294         boolean appWidgetVisible = mAppWidgetVisible.get(uid, false);
295         // if no pendingAppWidgetVisible set to appWidgetVisible to suppress output
296         boolean pendingAppWidgetVisible = mPendingAppWidgetVisible.get(uid, appWidgetVisible);
297         pw.print("    appWidgetVisible=");
298         pw.println(appWidgetVisible);
299         if (appWidgetVisible != pendingAppWidgetVisible) {
300             pw.print("    pendingAppWidgetVisible=");
301             pw.println(pendingAppWidgetVisible);
302         }
303         long pendingStateCommitTime = mPendingCommitTime.get(uid, 0);
304         if (pendingStateCommitTime != 0) {
305             pw.print("    pendingStateCommitTime=");
306             TimeUtils.formatDuration(pendingStateCommitTime, nowElapsed, pw);
307             pw.println();
308         }
309     }
310 
311     @Override
dumpEvents(PrintWriter pw)312     public void dumpEvents(PrintWriter pw) {
313         mEventLog.dumpEvents(pw);
314     }
315 
updateUidPendingStateIfNeeded(int uid)316     private void updateUidPendingStateIfNeeded(int uid) {
317         updateUidPendingStateIfNeededLocked(uid);
318     }
319 
updateUidPendingStateIfNeededLocked(int uid)320     private void updateUidPendingStateIfNeededLocked(int uid) {
321         long pendingCommitTime = mPendingCommitTime.get(uid, 0);
322         if (pendingCommitTime != 0) {
323             long currentTime = mClock.elapsedRealtime();
324             if (currentTime < mPendingCommitTime.get(uid)) {
325                 return;
326             }
327             commitUidPendingState(uid);
328         }
329     }
330 
commitUidPendingState(int uid)331     private void commitUidPendingState(int uid) {
332         int pendingUidState = mPendingUidStates.get(uid,
333                 mUidStates.get(uid, MIN_PRIORITY_UID_STATE));
334         int pendingCapability = mPendingCapability.get(uid,
335                 mCapability.get(uid, PROCESS_CAPABILITY_NONE));
336         boolean pendingAppWidgetVisible = mPendingAppWidgetVisible.get(uid,
337                 mAppWidgetVisible.get(uid, false));
338 
339         int uidState = mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
340         int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
341         boolean appWidgetVisible = mAppWidgetVisible.get(uid, false);
342 
343         if (uidState != pendingUidState
344                 || capability != pendingCapability
345                 || appWidgetVisible != pendingAppWidgetVisible) {
346             boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
347                     != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
348                     || capability != pendingCapability
349                     || appWidgetVisible != pendingAppWidgetVisible;
350 
351             if (foregroundChange) {
352                 // To save on memory usage, log only interesting changes.
353                 mEventLog.logCommitUidState(uid, pendingUidState, pendingCapability,
354                         pendingAppWidgetVisible, appWidgetVisible != pendingAppWidgetVisible);
355             }
356 
357             for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
358                 UidStateChangedCallback cb = mUidStateChangedCallbacks.keyAt(i);
359                 Executor executor = mUidStateChangedCallbacks.valueAt(i);
360 
361                 executor.execute(PooledLambda.obtainRunnable(
362                         UidStateChangedCallback::onUidStateChanged, cb, uid, pendingUidState,
363                         foregroundChange));
364             }
365         }
366 
367         if (mPendingGone.get(uid, false)) {
368             mUidStates.delete(uid);
369             mCapability.delete(uid);
370             mAppWidgetVisible.delete(uid);
371             mPendingGone.delete(uid);
372         } else {
373             mUidStates.put(uid, pendingUidState);
374             mCapability.put(uid, pendingCapability);
375             mAppWidgetVisible.put(uid, pendingAppWidgetVisible);
376         }
377 
378         mPendingUidStates.delete(uid);
379         mPendingCapability.delete(uid);
380         mPendingAppWidgetVisible.delete(uid);
381         mPendingCommitTime.delete(uid);
382     }
383 
getUidCapability(int uid)384     private @ProcessCapability int getUidCapability(int uid) {
385         return mCapability.get(uid, ActivityManager.PROCESS_CAPABILITY_NONE);
386     }
387 
getUidAppWidgetVisible(int uid)388     private boolean getUidAppWidgetVisible(int uid) {
389         return mAppWidgetVisible.get(uid, false);
390     }
391 
392     private static class EventLog {
393 
394         // Memory usage: 16 * size bytes
395         private static final int UPDATE_UID_PROC_STATE_LOG_MAX_SIZE = 200;
396         // Memory usage: 20 * size bytes
397         private static final int COMMIT_UID_STATE_LOG_MAX_SIZE = 200;
398         // Memory usage: 24 * size bytes
399         private static final int EVAL_FOREGROUND_MODE_MAX_SIZE = 200;
400 
401         private static final int APP_WIDGET_VISIBLE = 1 << 0;
402         private static final int APP_WIDGET_VISIBLE_CHANGED = 1 << 1;
403 
404         private final DelayableExecutor mExecutor;
405         private final Thread mExecutorThread;
406 
407         private int[][] mUpdateUidProcStateLog = new int[UPDATE_UID_PROC_STATE_LOG_MAX_SIZE][3];
408         private long[] mUpdateUidProcStateLogTimestamps =
409                 new long[UPDATE_UID_PROC_STATE_LOG_MAX_SIZE];
410         private int mUpdateUidProcStateLogSize = 0;
411         private int mUpdateUidProcStateLogHead = 0;
412 
413         private int[][] mCommitUidStateLog = new int[COMMIT_UID_STATE_LOG_MAX_SIZE][4];
414         private long[] mCommitUidStateLogTimestamps = new long[COMMIT_UID_STATE_LOG_MAX_SIZE];
415         private int mCommitUidStateLogSize = 0;
416         private int mCommitUidStateLogHead = 0;
417 
418         private int[][] mEvalForegroundModeLog = new int[EVAL_FOREGROUND_MODE_MAX_SIZE][5];
419         private long[] mEvalForegroundModeLogTimestamps = new long[EVAL_FOREGROUND_MODE_MAX_SIZE];
420         private int mEvalForegroundModeLogSize = 0;
421         private int mEvalForegroundModeLogHead = 0;
422 
EventLog(DelayableExecutor executor, Thread executorThread)423         EventLog(DelayableExecutor executor, Thread executorThread) {
424             mExecutor = executor;
425             mExecutorThread = executorThread;
426         }
427 
logUpdateUidProcState(int uid, int procState, int capability)428         void logUpdateUidProcState(int uid, int procState, int capability) {
429             if (UPDATE_UID_PROC_STATE_LOG_MAX_SIZE == 0) {
430                 return;
431             }
432             mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logUpdateUidProcStateAsync,
433                     this, System.currentTimeMillis(), uid, procState, capability));
434         }
435 
logUpdateUidProcStateAsync(long timestamp, int uid, int procState, int capability)436         void logUpdateUidProcStateAsync(long timestamp, int uid, int procState, int capability) {
437             int idx = (mUpdateUidProcStateLogHead + mUpdateUidProcStateLogSize)
438                     % UPDATE_UID_PROC_STATE_LOG_MAX_SIZE;
439             if (mUpdateUidProcStateLogSize == UPDATE_UID_PROC_STATE_LOG_MAX_SIZE) {
440                 mUpdateUidProcStateLogHead =
441                         (mUpdateUidProcStateLogHead + 1) % UPDATE_UID_PROC_STATE_LOG_MAX_SIZE;
442             } else {
443                 mUpdateUidProcStateLogSize++;
444             }
445 
446             mUpdateUidProcStateLog[idx][0] = uid;
447             mUpdateUidProcStateLog[idx][1] = procState;
448             mUpdateUidProcStateLog[idx][2] = capability;
449             mUpdateUidProcStateLogTimestamps[idx] = timestamp;
450         }
451 
logCommitUidState(int uid, int uidState, int capability, boolean appWidgetVisible, boolean appWidgetVisibleChanged)452         void logCommitUidState(int uid, int uidState, int capability, boolean appWidgetVisible,
453                 boolean appWidgetVisibleChanged) {
454             if (COMMIT_UID_STATE_LOG_MAX_SIZE == 0) {
455                 return;
456             }
457             mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logCommitUidStateAsync,
458                     this, System.currentTimeMillis(), uid, uidState, capability, appWidgetVisible,
459                     appWidgetVisibleChanged));
460         }
461 
logCommitUidStateAsync(long timestamp, int uid, int uidState, int capability, boolean appWidgetVisible, boolean appWidgetVisibleChanged)462         void logCommitUidStateAsync(long timestamp, int uid, int uidState, int capability,
463                 boolean appWidgetVisible, boolean appWidgetVisibleChanged) {
464             int idx = (mCommitUidStateLogHead + mCommitUidStateLogSize)
465                     % COMMIT_UID_STATE_LOG_MAX_SIZE;
466             if (mCommitUidStateLogSize == COMMIT_UID_STATE_LOG_MAX_SIZE) {
467                 mCommitUidStateLogHead =
468                         (mCommitUidStateLogHead + 1) % COMMIT_UID_STATE_LOG_MAX_SIZE;
469             } else {
470                 mCommitUidStateLogSize++;
471             }
472 
473             mCommitUidStateLog[idx][0] = uid;
474             mCommitUidStateLog[idx][1] = uidState;
475             mCommitUidStateLog[idx][2] = capability;
476             mCommitUidStateLog[idx][3] = 0;
477             if (appWidgetVisible) {
478                 mCommitUidStateLog[idx][3] += APP_WIDGET_VISIBLE;
479             }
480             if (appWidgetVisibleChanged) {
481                 mCommitUidStateLog[idx][3] += APP_WIDGET_VISIBLE_CHANGED;
482             }
483             mCommitUidStateLogTimestamps[idx] = timestamp;
484         }
485 
logEvalForegroundMode(int uid, int uidState, int capability, int code, int result)486         void logEvalForegroundMode(int uid, int uidState, int capability, int code, int result) {
487             if (EVAL_FOREGROUND_MODE_MAX_SIZE == 0) {
488                 return;
489             }
490             mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logEvalForegroundModeAsync,
491                     this, System.currentTimeMillis(), uid, uidState, capability, code, result));
492         }
493 
logEvalForegroundModeAsync(long timestamp, int uid, int uidState, int capability, int code, int result)494         void logEvalForegroundModeAsync(long timestamp, int uid, int uidState, int capability,
495                 int code, int result) {
496             int idx = (mEvalForegroundModeLogHead + mEvalForegroundModeLogSize)
497                     % EVAL_FOREGROUND_MODE_MAX_SIZE;
498             if (mEvalForegroundModeLogSize == EVAL_FOREGROUND_MODE_MAX_SIZE) {
499                 mEvalForegroundModeLogHead =
500                         (mEvalForegroundModeLogHead + 1) % EVAL_FOREGROUND_MODE_MAX_SIZE;
501             } else {
502                 mEvalForegroundModeLogSize++;
503             }
504 
505             mEvalForegroundModeLog[idx][0] = uid;
506             mEvalForegroundModeLog[idx][1] = uidState;
507             mEvalForegroundModeLog[idx][2] = capability;
508             mEvalForegroundModeLog[idx][3] = code;
509             mEvalForegroundModeLog[idx][4] = result;
510             mEvalForegroundModeLogTimestamps[idx] = timestamp;
511         }
512 
dumpEvents(PrintWriter pw)513         void dumpEvents(PrintWriter pw) {
514             int updateIdx = 0;
515             int commitIdx = 0;
516             int evalIdx = 0;
517 
518             while (updateIdx < mUpdateUidProcStateLogSize
519                     || commitIdx < mCommitUidStateLogSize
520                     || evalIdx < mEvalForegroundModeLogSize) {
521                 int updatePtr = 0;
522                 int commitPtr = 0;
523                 int evalPtr = 0;
524                 if (UPDATE_UID_PROC_STATE_LOG_MAX_SIZE != 0) {
525                     updatePtr = (mUpdateUidProcStateLogHead + updateIdx)
526                             % UPDATE_UID_PROC_STATE_LOG_MAX_SIZE;
527                 }
528                 if (COMMIT_UID_STATE_LOG_MAX_SIZE != 0) {
529                     commitPtr = (mCommitUidStateLogHead + commitIdx)
530                             % COMMIT_UID_STATE_LOG_MAX_SIZE;
531                 }
532                 if (EVAL_FOREGROUND_MODE_MAX_SIZE != 0) {
533                     evalPtr = (mEvalForegroundModeLogHead + evalIdx)
534                             % EVAL_FOREGROUND_MODE_MAX_SIZE;
535                 }
536 
537                 long aTimestamp = updateIdx < mUpdateUidProcStateLogSize
538                         ? mUpdateUidProcStateLogTimestamps[updatePtr] : Long.MAX_VALUE;
539                 long bTimestamp = commitIdx < mCommitUidStateLogSize
540                         ? mCommitUidStateLogTimestamps[commitPtr] : Long.MAX_VALUE;
541                 long cTimestamp = evalIdx < mEvalForegroundModeLogSize
542                         ? mEvalForegroundModeLogTimestamps[evalPtr] : Long.MAX_VALUE;
543 
544                 if (aTimestamp <= bTimestamp && aTimestamp <= cTimestamp) {
545                     dumpUpdateUidProcState(pw, updatePtr);
546                     updateIdx++;
547                 } else if (bTimestamp <= cTimestamp) {
548                     dumpCommitUidState(pw, commitPtr);
549                     commitIdx++;
550                 } else {
551                     dumpEvalForegroundMode(pw, evalPtr);
552                     evalIdx++;
553                 }
554             }
555         }
556 
557         void dumpUpdateUidProcState(PrintWriter pw, int idx) {
558             long timestamp = mUpdateUidProcStateLogTimestamps[idx];
559             int uid = mUpdateUidProcStateLog[idx][0];
560             int procState = mUpdateUidProcStateLog[idx][1];
561             int capability = mUpdateUidProcStateLog[idx][2];
562 
563             TimeUtils.dumpTime(pw, timestamp);
564 
565             pw.print(" UPDATE_UID_PROC_STATE");
566 
567             pw.print(" uid=");
568             pw.print(String.format("%-8d", uid));
569 
570             pw.print(" procState=");
571             pw.print(String.format("%-30s", ActivityManager.procStateToString(procState)));
572 
573             pw.print(" capability=");
574             pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
575 
576             pw.println();
577         }
578 
579         void dumpCommitUidState(PrintWriter pw, int idx) {
580             long timestamp = mCommitUidStateLogTimestamps[idx];
581             int uid = mCommitUidStateLog[idx][0];
582             int uidState = mCommitUidStateLog[idx][1];
583             int capability = mCommitUidStateLog[idx][2];
584             boolean appWidgetVisible = (mCommitUidStateLog[idx][3] & APP_WIDGET_VISIBLE) != 0;
585             boolean appWidgetVisibleChanged =
586                     (mCommitUidStateLog[idx][3] & APP_WIDGET_VISIBLE_CHANGED) != 0;
587 
588             TimeUtils.dumpTime(pw, timestamp);
589 
590             pw.print(" COMMIT_UID_STATE     ");
591 
592             pw.print(" uid=");
593             pw.print(String.format("%-8d", uid));
594 
595             pw.print(" uidState=");
596             pw.print(String.format("%-30s", AppOpsManager.uidStateToString(uidState)));
597 
598             pw.print(" capability=");
599             pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
600 
601             pw.print(" appWidgetVisible=");
602             pw.print(appWidgetVisible);
603 
604             if (appWidgetVisibleChanged) {
605                 pw.print(" (changed)");
606             }
607 
608             pw.println();
609         }
610 
611         void dumpEvalForegroundMode(PrintWriter pw, int idx) {
612             long timestamp = mEvalForegroundModeLogTimestamps[idx];
613             int uid = mEvalForegroundModeLog[idx][0];
614             int uidState = mEvalForegroundModeLog[idx][1];
615             int capability = mEvalForegroundModeLog[idx][2];
616             int code = mEvalForegroundModeLog[idx][3];
617             int result = mEvalForegroundModeLog[idx][4];
618 
619             TimeUtils.dumpTime(pw, timestamp);
620 
621             pw.print(" EVAL_FOREGROUND_MODE ");
622 
623             pw.print(" uid=");
624             pw.print(String.format("%-8d", uid));
625 
626             pw.print(" uidState=");
627             pw.print(String.format("%-30s", AppOpsManager.uidStateToString(uidState)));
628 
629             pw.print(" capability=");
630             pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
631 
632             pw.print(" code=");
633             pw.print(String.format("%-20s", AppOpsManager.opToName(code)));
634 
635             pw.print(" result=");
636             pw.print(AppOpsManager.modeToName(result));
637 
638             pw.println();
639         }
640     }
641 }
642