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.am;
18 
19 import android.os.SystemClock;
20 import android.util.Pair;
21 import android.util.Slog;
22 import android.util.SparseArray;
23 
24 /**
25  * While starting activity, WindowManager posts a runnable to DisplayThread to updateOomAdj.
26  * The latency of the thread switch could cause client app failure when the app is checking
27  * {@link ActivityManagerService#isUidActive} before updateOomAdj is done.
28  *
29  * Use PendingStartActivityUids to save uid after WindowManager start activity and before
30  * updateOomAdj is done.
31  *
32  * <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock!
33  */
34 final class PendingStartActivityUids {
35     static final String TAG = ActivityManagerService.TAG;
36 
37     public static final long INVALID_TIME = 0;
38 
39     // Key is uid, value is Pair of pid and SystemClock.elapsedRealtime() when the
40     // uid is added.
41     private final SparseArray<Pair<Integer, Long>> mPendingUids = new SparseArray();
42 
43     /** Returns {@code true} if the uid is put to the pending array. Otherwise it existed. */
add(int uid, int pid)44     synchronized boolean add(int uid, int pid) {
45         if (mPendingUids.get(uid) == null) {
46             mPendingUids.put(uid, new Pair<>(pid, SystemClock.elapsedRealtime()));
47             return true;
48         }
49         return false;
50     }
51 
delete(int uid, long nowElapsed)52     synchronized void delete(int uid, long nowElapsed) {
53         final Pair<Integer, Long> pendingPid = mPendingUids.get(uid);
54         if (pendingPid != null) {
55             if (nowElapsed < pendingPid.second) {
56                 Slog.i(TAG,
57                         "updateOomAdj start time is before than pendingPid added,"
58                         + " don't delete it");
59                 return;
60             }
61             final long delay = SystemClock.elapsedRealtime() - pendingPid.second;
62             if (delay >= 1000 /*ms*/) {
63                 Slog.i(TAG,
64                         "PendingStartActivityUids startActivity to updateOomAdj delay:"
65                                 + delay + "ms," + " uid:" + uid);
66             }
67             mPendingUids.delete(uid);
68         }
69     }
70 
71     /**
72      * Return the elapsedRealtime when the uid is added to the mPendingUids map.
73      * @param uid
74      * @param pid
75      * @return elapsedRealtime if the uid is in the mPendingUids map;
76      *         INVALID_TIME if the uid is not in the mPendingUids map.
77      */
getPendingTopPidTime(int uid, int pid)78     synchronized long getPendingTopPidTime(int uid, int pid) {
79         long ret = INVALID_TIME;
80         final Pair<Integer, Long> pendingPid = mPendingUids.get(uid);
81         if (pendingPid != null && pendingPid.first == pid) {
82             ret = pendingPid.second;
83         }
84         return ret;
85     }
86 
isPendingTopUid(int uid)87     synchronized boolean isPendingTopUid(int uid) {
88         return mPendingUids.get(uid) != null;
89     }
90 
91     // Must called with AMS locked.
enqueuePendingTopAppIfNecessaryLocked(ActivityManagerService ams)92     synchronized void enqueuePendingTopAppIfNecessaryLocked(ActivityManagerService ams) {
93         for (int i = 0, size = mPendingUids.size(); i < size; i++) {
94             final Pair<Integer, Long> p = mPendingUids.valueAt(i);
95             final ProcessRecord app;
96             synchronized (ams.mPidsSelfLocked) {
97                 app = ams.mPidsSelfLocked.get(p.first);
98             }
99             if (app != null) {
100                 ams.enqueueOomAdjTargetLocked(app);
101             }
102         }
103     }
104 
clear()105     synchronized void clear() {
106         mPendingUids.clear();
107     }
108 }
109