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.am;
18 
19 import android.annotation.NonNull;
20 import android.annotation.UserIdInt;
21 import android.app.ActivityManager;
22 import android.content.Context;
23 import android.os.PowerExemptionManager;
24 import android.os.PowerExemptionManager.ReasonCode;
25 import android.os.SystemClock;
26 import android.os.UserHandle;
27 import android.provider.DeviceConfig;
28 import android.util.ArrayMap;
29 import android.util.ArraySet;
30 import android.util.SparseArray;
31 
32 import com.android.internal.annotations.GuardedBy;
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.server.am.BaseAppStateEvents.MaxTrackingDurationConfig;
35 import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy;
36 
37 import java.io.PrintWriter;
38 import java.lang.reflect.Constructor;
39 import java.util.LinkedList;
40 
41 /**
42  * Base class to track certain state event of apps.
43  */
44 abstract class BaseAppStateEventsTracker
45         <T extends BaseAppStateEventsPolicy, U extends BaseAppStateEvents>
46         extends BaseAppStateTracker<T> implements BaseAppStateEvents.Factory<U> {
47     static final boolean DEBUG_BASE_APP_STATE_EVENTS_TRACKER = false;
48 
49     @GuardedBy("mLock")
50     final UidProcessMap<U> mPkgEvents = new UidProcessMap<>();
51 
52     @GuardedBy("mLock")
53     final ArraySet<Integer> mTopUids = new ArraySet<>();
54 
BaseAppStateEventsTracker(Context context, AppRestrictionController controller, Constructor<? extends Injector<T>> injector, Object outerContext)55     BaseAppStateEventsTracker(Context context, AppRestrictionController controller,
56             Constructor<? extends Injector<T>> injector, Object outerContext) {
57         super(context, controller, injector, outerContext);
58     }
59 
60     @VisibleForTesting
reset()61     void reset() {
62         synchronized (mLock) {
63             mPkgEvents.clear();
64             mTopUids.clear();
65         }
66     }
67 
68     @GuardedBy("mLock")
getUidEventsLocked(int uid)69     U getUidEventsLocked(int uid) {
70         U events = null;
71         final ArrayMap<String, U> map = mPkgEvents.getMap().get(uid);
72         if (map == null) {
73             return null;
74         }
75         for (int i = map.size() - 1; i >= 0; i--) {
76             final U event = map.valueAt(i);
77             if (event != null) {
78                 if (events == null) {
79                     events = createAppStateEvents(uid, event.mPackageName);
80                 }
81                 events.add(event);
82             }
83         }
84         return events;
85     }
86 
trim(long earliest)87     void trim(long earliest) {
88         synchronized (mLock) {
89             trimLocked(earliest);
90         }
91     }
92 
93     @GuardedBy("mLock")
trimLocked(long earliest)94     void trimLocked(long earliest) {
95         final SparseArray<ArrayMap<String, U>> map = mPkgEvents.getMap();
96         for (int i = map.size() - 1; i >= 0; i--) {
97             final ArrayMap<String, U> val = map.valueAt(i);
98             for (int j = val.size() - 1; j >= 0; j--) {
99                 final U v = val.valueAt(j);
100                 v.trim(earliest);
101                 if (v.isEmpty()) {
102                     val.removeAt(j);
103                 }
104             }
105             if (val.size() == 0) {
106                 map.removeAt(i);
107             }
108         }
109     }
110 
isUidOnTop(int uid)111     boolean isUidOnTop(int uid) {
112         synchronized (mLock) {
113             return mTopUids.contains(uid);
114         }
115     }
116 
117     @GuardedBy("mLock")
onUntrackingUidLocked(int uid)118     void onUntrackingUidLocked(int uid) {
119     }
120 
121     @Override
onUidProcStateChanged(final int uid, final int procState)122     void onUidProcStateChanged(final int uid, final int procState) {
123         synchronized (mLock) {
124             if (mPkgEvents.getMap().indexOfKey(uid) < 0) {
125                 // If we're not tracking its events, ignore its UID state changes.
126                 return;
127             }
128             onUidProcStateChangedUncheckedLocked(uid, procState);
129         }
130     }
131 
132     @GuardedBy("mLock")
onUidProcStateChangedUncheckedLocked(final int uid, final int procState)133     void onUidProcStateChangedUncheckedLocked(final int uid, final int procState) {
134         if (procState < ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
135             mTopUids.add(uid);
136         } else {
137             mTopUids.remove(uid);
138         }
139     }
140 
141     @Override
onUidGone(final int uid)142     void onUidGone(final int uid) {
143         synchronized (mLock) {
144             mTopUids.remove(uid);
145         }
146     }
147 
148     @Override
onUidRemoved(final int uid)149     void onUidRemoved(final int uid) {
150         synchronized (mLock) {
151             mPkgEvents.getMap().remove(uid);
152             onUntrackingUidLocked(uid);
153         }
154     }
155 
156     @Override
onUserRemoved(final @UserIdInt int userId)157     void onUserRemoved(final @UserIdInt int userId) {
158         synchronized (mLock) {
159             final SparseArray<ArrayMap<String, U>> map = mPkgEvents.getMap();
160             for (int i = map.size() - 1; i >= 0; i--) {
161                 final int uid = map.keyAt(i);
162                 if (UserHandle.getUserId(uid) == userId) {
163                     map.removeAt(i);
164                     onUntrackingUidLocked(uid);
165                 }
166             }
167         }
168     }
169 
170     @Override
dump(PrintWriter pw, String prefix)171     void dump(PrintWriter pw, String prefix) {
172         final T policy = mInjector.getPolicy();
173         synchronized (mLock) {
174             final long now = SystemClock.elapsedRealtime();
175             final SparseArray<ArrayMap<String, U>> map = mPkgEvents.getMap();
176             for (int i = map.size() - 1; i >= 0; i--) {
177                 final int uid = map.keyAt(i);
178                 final ArrayMap<String, U> val = map.valueAt(i);
179                 for (int j = val.size() - 1; j >= 0; j--) {
180                     final String packageName = val.keyAt(j);
181                     final U events = val.valueAt(j);
182                     dumpEventHeaderLocked(pw, prefix, packageName, uid, events, policy);
183                     dumpEventLocked(pw, prefix, events, now);
184                 }
185             }
186         }
187         dumpOthers(pw, prefix);
188         policy.dump(pw, prefix);
189     }
190 
dumpOthers(PrintWriter pw, String prefix)191     void dumpOthers(PrintWriter pw, String prefix) {
192     }
193 
194     @GuardedBy("mLock")
dumpEventHeaderLocked(PrintWriter pw, String prefix, String packageName, int uid, U events, T policy)195     void dumpEventHeaderLocked(PrintWriter pw, String prefix, String packageName, int uid, U events,
196             T policy) {
197         pw.print(prefix);
198         pw.print("* ");
199         pw.print(packageName);
200         pw.print('/');
201         pw.print(UserHandle.formatUid(uid));
202         pw.print(" exemption=");
203         pw.println(policy.getExemptionReasonString(packageName, uid, events.mExemptReason));
204     }
205 
206     @GuardedBy("mLock")
dumpEventLocked(PrintWriter pw, String prefix, U events, long now)207     void dumpEventLocked(PrintWriter pw, String prefix, U events, long now) {
208         events.dump(pw, "  " + prefix, now);
209     }
210 
211     abstract static class BaseAppStateEventsPolicy<V extends BaseAppStateEventsTracker>
212             extends BaseAppStatePolicy<V> implements MaxTrackingDurationConfig {
213         /**
214          * The key to the maximum duration we'd keep tracking, events earlier than that
215          * will be discarded.
216          */
217         final @NonNull String mKeyMaxTrackingDuration;
218 
219         /**
220          * The default to the {@link #mMaxTrackingDuration}.
221          */
222         final long mDefaultMaxTrackingDuration;
223 
224         /**
225          * The maximum duration we'd keep tracking, events earlier than that will be discarded.
226          */
227         volatile long mMaxTrackingDuration;
228 
BaseAppStateEventsPolicy(@onNull Injector<?> injector, @NonNull V tracker, @NonNull String keyTrackerEnabled, boolean defaultTrackerEnabled, @NonNull String keyMaxTrackingDuration, long defaultMaxTrackingDuration)229         BaseAppStateEventsPolicy(@NonNull Injector<?> injector, @NonNull V tracker,
230                 @NonNull String keyTrackerEnabled, boolean defaultTrackerEnabled,
231                 @NonNull String keyMaxTrackingDuration, long defaultMaxTrackingDuration) {
232             super(injector, tracker, keyTrackerEnabled, defaultTrackerEnabled);
233             mKeyMaxTrackingDuration = keyMaxTrackingDuration;
234             mDefaultMaxTrackingDuration = defaultMaxTrackingDuration;
235         }
236 
237         @Override
onPropertiesChanged(String name)238         public void onPropertiesChanged(String name) {
239             if (mKeyMaxTrackingDuration.equals(name)) {
240                 updateMaxTrackingDuration();
241             } else {
242                 super.onPropertiesChanged(name);
243             }
244         }
245 
246         @Override
onSystemReady()247         public void onSystemReady() {
248             super.onSystemReady();
249             updateMaxTrackingDuration();
250         }
251 
252         /**
253          * Called when the maximum duration we'd keep tracking has been changed.
254          */
onMaxTrackingDurationChanged(long maxDuration)255         public abstract void onMaxTrackingDurationChanged(long maxDuration);
256 
updateMaxTrackingDuration()257         void updateMaxTrackingDuration() {
258             long max = DeviceConfig.getLong(
259                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
260                     mKeyMaxTrackingDuration, mDefaultMaxTrackingDuration);
261             if (max != mMaxTrackingDuration) {
262                 mMaxTrackingDuration = max;
263                 onMaxTrackingDurationChanged(max);
264             }
265         }
266 
267         @Override
getMaxTrackingDuration()268         public long getMaxTrackingDuration() {
269             return mMaxTrackingDuration;
270         }
271 
getExemptionReasonString(String packageName, int uid, @ReasonCode int reason)272         String getExemptionReasonString(String packageName, int uid, @ReasonCode int reason) {
273             return PowerExemptionManager.reasonCodeToString(reason);
274         }
275 
276         @Override
dump(PrintWriter pw, String prefix)277         void dump(PrintWriter pw, String prefix) {
278             super.dump(pw, prefix);
279             if (isEnabled()) {
280                 pw.print(prefix);
281                 pw.print(mKeyMaxTrackingDuration);
282                 pw.print('=');
283                 pw.println(mMaxTrackingDuration);
284             }
285         }
286     }
287 
288     /**
289      * Simple event table, with only one track of events.
290      */
291     static class SimplePackageEvents extends BaseAppStateTimeEvents {
292         static final int DEFAULT_INDEX = 0;
293 
SimplePackageEvents(int uid, String packageName, MaxTrackingDurationConfig maxTrackingDurationConfig)294         SimplePackageEvents(int uid, String packageName,
295                 MaxTrackingDurationConfig maxTrackingDurationConfig) {
296             super(uid, packageName, 1, TAG, maxTrackingDurationConfig);
297             mEvents[DEFAULT_INDEX] = new LinkedList<Long>();
298         }
299 
getTotalEvents(long now)300         long getTotalEvents(long now) {
301             return getTotalEvents(now, DEFAULT_INDEX);
302         }
303 
getTotalEventsSince(long since, long now)304         long getTotalEventsSince(long since, long now) {
305             return getTotalEventsSince(since, now, DEFAULT_INDEX);
306         }
307 
308         @Override
formatEventTypeLabel(int index)309         String formatEventTypeLabel(int index) {
310             return "";
311         }
312     }
313 }
314