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 static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
20 import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
21 import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN;
22 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
23 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
24 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE;
25 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
26 import static android.os.PowerExemptionManager.REASON_DENIED;
27 import static android.os.PowerExemptionManager.REASON_PROC_STATE_FGS;
28 import static android.os.PowerExemptionManager.REASON_PROC_STATE_TOP;
29 import static android.os.PowerExemptionManager.reasonCodeToString;
30 
31 import static com.android.server.am.BaseAppStateTracker.ONE_MINUTE;
32 
33 import android.annotation.NonNull;
34 import android.app.ActivityManager.RestrictionLevel;
35 import android.content.Context;
36 import android.os.Handler;
37 import android.os.Message;
38 import android.os.PowerExemptionManager.ReasonCode;
39 import android.os.SystemClock;
40 import android.os.UserHandle;
41 import android.provider.DeviceConfig;
42 import android.util.ArrayMap;
43 import android.util.Slog;
44 import android.util.SparseArray;
45 import android.util.TimeUtils;
46 
47 import com.android.internal.annotations.GuardedBy;
48 import com.android.internal.annotations.VisibleForTesting;
49 import com.android.internal.app.ProcessMap;
50 import com.android.server.am.BaseAppStateTimeSlotEventsTracker.BaseAppStateTimeSlotEventsPolicy;
51 import com.android.server.am.BaseAppStateTimeSlotEventsTracker.SimpleAppStateTimeslotEvents;
52 
53 import java.io.PrintWriter;
54 import java.lang.reflect.Constructor;
55 
56 /**
57  * Base class to track {@link #BaseAppStateTimeSlotEvents}.
58  */
59 abstract class BaseAppStateTimeSlotEventsTracker
60         <T extends BaseAppStateTimeSlotEventsPolicy, U extends SimpleAppStateTimeslotEvents>
61         extends BaseAppStateEventsTracker<T, U> {
62     static final String TAG = "BaseAppStateTimeSlotEventsTracker";
63 
64     static final boolean DEBUG_APP_STATE_TIME_SLOT_EVENT_TRACKER = false;
65 
66     // Unlocked since it's only accessed in single thread.
67     private final ArrayMap<U, Integer> mTmpPkgs = new ArrayMap<>();
68 
69     private H mHandler;
70 
BaseAppStateTimeSlotEventsTracker(Context context, AppRestrictionController controller, Constructor<? extends Injector<T>> injector, Object outerContext)71     BaseAppStateTimeSlotEventsTracker(Context context, AppRestrictionController controller,
72             Constructor<? extends Injector<T>> injector, Object outerContext) {
73         super(context, controller, injector, outerContext);
74         mHandler = new H(this);
75     }
76 
onNewEvent(String packageName, int uid)77     void onNewEvent(String packageName, int uid) {
78         mHandler.obtainMessage(H.MSG_NEW_EVENT, uid, 0, packageName).sendToTarget();
79     }
80 
handleNewEvent(String packageName, int uid)81     void handleNewEvent(String packageName, int uid) {
82         if (mInjector.getPolicy().shouldExempt(packageName, uid) != REASON_DENIED) {
83             return;
84         }
85         final long now = SystemClock.elapsedRealtime();
86         boolean notify = false;
87         int totalEvents;
88         synchronized (mLock) {
89             U pkgEvents = mPkgEvents.get(uid, packageName);
90             if (pkgEvents == null) {
91                 pkgEvents = createAppStateEvents(uid, packageName);
92                 mPkgEvents.put(uid, packageName, pkgEvents);
93             }
94             pkgEvents.addEvent(now, SimpleAppStateTimeslotEvents.DEFAULT_INDEX);
95             totalEvents = pkgEvents.getTotalEvents(now, SimpleAppStateTimeslotEvents.DEFAULT_INDEX);
96             notify = totalEvents >= mInjector.getPolicy().getNumOfEventsThreshold();
97         }
98         if (notify) {
99             mInjector.getPolicy().onExcessiveEvents(
100                     packageName, uid, totalEvents, now);
101         }
102     }
103 
onMonitorEnabled(boolean enabled)104     void onMonitorEnabled(boolean enabled) {
105         if (!enabled) {
106             synchronized (mLock) {
107                 mPkgEvents.clear();
108             }
109         }
110     }
111 
onNumOfEventsThresholdChanged(int threshold)112     void onNumOfEventsThresholdChanged(int threshold) {
113         final long now = SystemClock.elapsedRealtime();
114         synchronized (mLock) {
115             SparseArray<ArrayMap<String, U>> pkgEvents = mPkgEvents.getMap();
116             for (int i = pkgEvents.size() - 1; i >= 0; i--) {
117                 final ArrayMap<String, U> pkgs = pkgEvents.valueAt(i);
118                 for (int j = pkgs.size() - 1; j >= 0; j--) {
119                     final U pkg = pkgs.valueAt(j);
120                     int totalEvents = pkg.getTotalEvents(now,
121                             SimpleAppStateTimeslotEvents.DEFAULT_INDEX);
122                     if (totalEvents >= threshold) {
123                         mTmpPkgs.put(pkg, totalEvents);
124                     }
125                 }
126             }
127         }
128         for (int i = mTmpPkgs.size() - 1; i >= 0; i--) {
129             final U pkg = mTmpPkgs.keyAt(i);
130             mInjector.getPolicy().onExcessiveEvents(
131                     pkg.mPackageName, pkg.mUid, mTmpPkgs.valueAt(i), now);
132         }
133         mTmpPkgs.clear();
134     }
135 
136     @GuardedBy("mLock")
getTotalEventsLocked(int uid, long now)137     int getTotalEventsLocked(int uid, long now) {
138         final U events = getUidEventsLocked(uid);
139         if (events == null) {
140             return 0;
141         }
142         return events.getTotalEvents(now, SimpleAppStateTimeslotEvents.DEFAULT_INDEX);
143     }
144 
trimEvents()145     private void trimEvents() {
146         final long now = SystemClock.elapsedRealtime();
147         trim(Math.max(0, now - mInjector.getPolicy().getMaxTrackingDuration()));
148     }
149 
150     @Override
onUserInteractionStarted(String packageName, int uid)151     void onUserInteractionStarted(String packageName, int uid) {
152         mInjector.getPolicy().onUserInteractionStarted(packageName, uid);
153     }
154 
155     static class H extends Handler {
156         static final int MSG_NEW_EVENT = 0;
157 
158         final BaseAppStateTimeSlotEventsTracker mTracker;
159 
H(BaseAppStateTimeSlotEventsTracker tracker)160         H(BaseAppStateTimeSlotEventsTracker tracker) {
161             super(tracker.mBgHandler.getLooper());
162             mTracker = tracker;
163         }
164 
165         @Override
handleMessage(Message msg)166         public void handleMessage(Message msg) {
167             switch (msg.what) {
168                 case MSG_NEW_EVENT:
169                     mTracker.handleNewEvent((String) msg.obj, msg.arg1);
170                     break;
171             }
172         }
173     }
174 
175     static class BaseAppStateTimeSlotEventsPolicy<E extends BaseAppStateTimeSlotEventsTracker>
176             extends BaseAppStateEventsPolicy<E> {
177 
178         final String mKeyNumOfEventsThreshold;
179         final int mDefaultNumOfEventsThreshold;
180 
181         @NonNull
182         private final Object mLock;
183 
184         @GuardedBy("mLock")
185         private final ProcessMap<Long> mExcessiveEventPkgs = new ProcessMap<>();
186 
187         long mTimeSlotSize = DEBUG_APP_STATE_TIME_SLOT_EVENT_TRACKER
188                     ? SimpleAppStateTimeslotEvents.DEFAULT_TIME_SLOT_SIZE_DEBUG
189                     : SimpleAppStateTimeslotEvents.DEFAULT_TIME_SLOT_SIZE;
190 
191         volatile int mNumOfEventsThreshold;
192 
BaseAppStateTimeSlotEventsPolicy(@onNull Injector injector, @NonNull E tracker, @NonNull String keyTrackerEnabled, boolean defaultTrackerEnabled, @NonNull String keyMaxTrackingDuration, long defaultMaxTrackingDuration, @NonNull String keyNumOfEventsThreshold, int defaultNumOfEventsThreshold)193         BaseAppStateTimeSlotEventsPolicy(@NonNull Injector injector, @NonNull E tracker,
194                 @NonNull String keyTrackerEnabled, boolean defaultTrackerEnabled,
195                 @NonNull String keyMaxTrackingDuration, long defaultMaxTrackingDuration,
196                 @NonNull String keyNumOfEventsThreshold, int defaultNumOfEventsThreshold) {
197             super(injector, tracker, keyTrackerEnabled, defaultTrackerEnabled,
198                     keyMaxTrackingDuration, defaultMaxTrackingDuration);
199             mKeyNumOfEventsThreshold = keyNumOfEventsThreshold;
200             mDefaultNumOfEventsThreshold = defaultNumOfEventsThreshold;
201             mLock = tracker.mLock;
202         }
203 
204         @Override
onSystemReady()205         public void onSystemReady() {
206             super.onSystemReady();
207             updateNumOfEventsThreshold();
208         }
209 
210         @Override
onPropertiesChanged(String name)211         public void onPropertiesChanged(String name) {
212             if (mKeyNumOfEventsThreshold.equals(name)) {
213                 updateNumOfEventsThreshold();
214             } else {
215                 super.onPropertiesChanged(name);
216             }
217         }
218 
219         @Override
onTrackerEnabled(boolean enabled)220         public void onTrackerEnabled(boolean enabled) {
221             mTracker.onMonitorEnabled(enabled);
222         }
223 
224         @Override
onMaxTrackingDurationChanged(long maxDuration)225         public void onMaxTrackingDurationChanged(long maxDuration) {
226             mTracker.mBgHandler.post(mTracker::trimEvents);
227         }
228 
updateNumOfEventsThreshold()229         private void updateNumOfEventsThreshold() {
230             final int threshold = DeviceConfig.getInt(
231                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
232                     mKeyNumOfEventsThreshold,
233                     mDefaultNumOfEventsThreshold);
234             if (threshold != mNumOfEventsThreshold) {
235                 mNumOfEventsThreshold = threshold;
236                 mTracker.onNumOfEventsThresholdChanged(threshold);
237             }
238         }
239 
getNumOfEventsThreshold()240         int getNumOfEventsThreshold() {
241             return mNumOfEventsThreshold;
242         }
243 
getTimeSlotSize()244         long getTimeSlotSize() {
245             return mTimeSlotSize;
246         }
247 
248         @VisibleForTesting
setTimeSlotSize(long size)249         void setTimeSlotSize(long size) {
250             mTimeSlotSize = size;
251         }
252 
getEventName()253         String getEventName() {
254             return "event";
255         }
256 
onExcessiveEvents(String packageName, int uid, int numOfEvents, long now)257         void onExcessiveEvents(String packageName, int uid, int numOfEvents, long now) {
258             boolean notifyController = false;
259             synchronized (mLock) {
260                 Long ts = mExcessiveEventPkgs.get(packageName, uid);
261                 if (ts == null) {
262                     if (DEBUG_APP_STATE_TIME_SLOT_EVENT_TRACKER) {
263                         Slog.i(TAG, "Excessive amount of " + getEventName() + " from "
264                                 + packageName + "/" + UserHandle.formatUid(uid) + ": " + numOfEvents
265                                 + " over " + TimeUtils.formatDuration(getMaxTrackingDuration()));
266                     }
267                     mExcessiveEventPkgs.put(packageName, uid, now);
268                     notifyController = true;
269                 }
270             }
271             if (notifyController) {
272                 mTracker.mAppRestrictionController.refreshAppRestrictionLevelForUid(
273                         uid, REASON_MAIN_FORCED_BY_SYSTEM,
274                         REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE, true);
275             }
276         }
277 
278         /**
279          * Whether or not we should ignore the incoming event.
280          */
shouldExempt(String packageName, int uid)281         @ReasonCode int shouldExempt(String packageName, int uid) {
282             if (mTracker.isUidOnTop(uid)) {
283                 if (DEBUG_APP_STATE_TIME_SLOT_EVENT_TRACKER) {
284                     Slog.i(TAG, "Ignoring event from " + packageName + "/"
285                             + UserHandle.formatUid(uid) + ": top");
286                 }
287                 return REASON_PROC_STATE_TOP;
288             }
289             if (mTracker.mAppRestrictionController.hasForegroundServices(packageName, uid)) {
290                 if (DEBUG_APP_STATE_TIME_SLOT_EVENT_TRACKER) {
291                     Slog.i(TAG, "Ignoring event " + packageName + "/"
292                             + UserHandle.formatUid(uid) + ": has active FGS");
293                 }
294                 return REASON_PROC_STATE_FGS;
295             }
296             final @ReasonCode int reason = shouldExemptUid(uid);
297             if (reason != REASON_DENIED) {
298                 if (DEBUG_APP_STATE_TIME_SLOT_EVENT_TRACKER) {
299                     Slog.i(TAG, "Ignoring event " + packageName + "/" + UserHandle.formatUid(uid)
300                             + ": " + reasonCodeToString(reason));
301                 }
302                 return reason;
303             }
304             return REASON_DENIED;
305         }
306 
307         @Override
308         @RestrictionLevel
getProposedRestrictionLevel(String packageName, int uid, @RestrictionLevel int maxLevel)309         public int getProposedRestrictionLevel(String packageName, int uid,
310                 @RestrictionLevel int maxLevel) {
311             synchronized (mLock) {
312                 final int level = mExcessiveEventPkgs.get(packageName, uid) == null
313                         || !mTracker.mAppRestrictionController.isAutoRestrictAbusiveAppEnabled()
314                         ? RESTRICTION_LEVEL_ADAPTIVE_BUCKET
315                         : RESTRICTION_LEVEL_RESTRICTED_BUCKET;
316                 if (maxLevel > RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
317                     return level;
318                 } else if (maxLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
319                     return RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
320                 }
321                 return RESTRICTION_LEVEL_UNKNOWN;
322             }
323         }
324 
onUserInteractionStarted(String packageName, int uid)325         void onUserInteractionStarted(String packageName, int uid) {
326             boolean notifyController = false;
327             synchronized (mLock) {
328                 notifyController = mExcessiveEventPkgs.remove(packageName, uid) != null;
329             }
330             mTracker.mAppRestrictionController.refreshAppRestrictionLevelForUid(uid,
331                     REASON_MAIN_USAGE, REASON_SUB_USAGE_USER_INTERACTION, true);
332         }
333 
334         @Override
dump(PrintWriter pw, String prefix)335         void dump(PrintWriter pw, String prefix) {
336             super.dump(pw, prefix);
337             if (isEnabled()) {
338                 pw.print(prefix);
339                 pw.print(mKeyNumOfEventsThreshold);
340                 pw.print('=');
341                 pw.println(mDefaultNumOfEventsThreshold);
342             }
343             pw.print(prefix);
344             pw.print("event_time_slot_size=");
345             pw.println(getTimeSlotSize());
346         }
347     }
348 
349     /**
350      * A simple time-slot based event table, with only one track of events.
351      */
352     static class SimpleAppStateTimeslotEvents extends BaseAppStateTimeSlotEvents {
353         static final int DEFAULT_INDEX = 0;
354         static final long DEFAULT_TIME_SLOT_SIZE = 15 * ONE_MINUTE;
355         static final long DEFAULT_TIME_SLOT_SIZE_DEBUG = ONE_MINUTE;
356 
SimpleAppStateTimeslotEvents(int uid, @NonNull String packageName, long timeslotSize, @NonNull String tag, @NonNull MaxTrackingDurationConfig maxTrackingDurationConfig)357         SimpleAppStateTimeslotEvents(int uid, @NonNull String packageName,
358                 long timeslotSize, @NonNull String tag,
359                 @NonNull MaxTrackingDurationConfig maxTrackingDurationConfig) {
360             super(uid, packageName, 1, timeslotSize, tag, maxTrackingDurationConfig);
361         }
362 
SimpleAppStateTimeslotEvents(SimpleAppStateTimeslotEvents other)363         SimpleAppStateTimeslotEvents(SimpleAppStateTimeslotEvents other) {
364             super(other);
365         }
366 
367         @Override
formatEventTypeLabel(int index)368         String formatEventTypeLabel(int index) {
369             return "";
370         }
371 
372         @Override
formatEventSummary(long now, int index)373         String formatEventSummary(long now, int index) {
374             if (mEvents[DEFAULT_INDEX] == null || mEvents[DEFAULT_INDEX].size() == 0) {
375                 return "(none)";
376             }
377             final int total = getTotalEvents(now, DEFAULT_INDEX);
378             return "total=" + total + ", latest="
379                     + getTotalEventsSince(mCurSlotStartTime[DEFAULT_INDEX], now, DEFAULT_INDEX)
380                     + "(slot=" + TimeUtils.formatTime(mCurSlotStartTime[DEFAULT_INDEX], now) + ")";
381         }
382     }
383 }
384