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