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.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPES_MAX_INDEX; 20 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; 21 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK; 22 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; 23 import static android.content.pm.ServiceInfo.foregroundServiceTypeToLabel; 24 import static android.os.PowerExemptionManager.REASON_DENIED; 25 26 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 27 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 28 import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX; 29 import static com.android.server.am.BaseAppStateTracker.ONE_DAY; 30 import static com.android.server.am.BaseAppStateTracker.ONE_HOUR; 31 32 import android.annotation.NonNull; 33 import android.app.ActivityManagerInternal.ForegroundServiceStateListener; 34 import android.app.IProcessObserver; 35 import android.content.ComponentName; 36 import android.content.Context; 37 import android.content.pm.ServiceInfo.ForegroundServiceType; 38 import android.os.AppBackgroundRestrictionsInfo; 39 import android.os.Handler; 40 import android.os.Message; 41 import android.os.PowerExemptionManager.ReasonCode; 42 import android.os.RemoteException; 43 import android.os.SystemClock; 44 import android.os.UserHandle; 45 import android.provider.DeviceConfig; 46 import android.service.notification.NotificationListenerService; 47 import android.service.notification.StatusBarNotification; 48 import android.util.ArrayMap; 49 import android.util.Slog; 50 import android.util.SparseArray; 51 import android.util.SparseBooleanArray; 52 import android.util.TimeUtils; 53 import android.util.proto.ProtoOutputStream; 54 55 import com.android.internal.annotations.GuardedBy; 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.internal.os.SomeArgs; 58 import com.android.server.am.AppFGSTracker.AppFGSPolicy; 59 import com.android.server.am.AppFGSTracker.PackageDurations; 60 import com.android.server.am.AppRestrictionController.TrackerType; 61 import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy; 62 import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent; 63 import com.android.server.am.BaseAppStateTracker.Injector; 64 65 import java.io.PrintWriter; 66 import java.lang.reflect.Constructor; 67 import java.util.Arrays; 68 import java.util.LinkedList; 69 70 /** 71 * The tracker for monitoring abusive (long-running) FGS. 72 */ 73 final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, PackageDurations> 74 implements ForegroundServiceStateListener { 75 static final String TAG = TAG_WITH_CLASS_NAME ? "AppFGSTracker" : TAG_AM; 76 77 static final boolean DEBUG_BACKGROUND_FGS_TRACKER = false; 78 79 private final MyHandler mHandler; 80 81 @GuardedBy("mLock") 82 private final UidProcessMap<SparseBooleanArray> mFGSNotificationIDs = new UidProcessMap<>(); 83 84 // Unlocked since it's only accessed in single thread. 85 private final ArrayMap<PackageDurations, Long> mTmpPkgDurations = new ArrayMap<>(); 86 87 @VisibleForTesting 88 final NotificationListener mNotificationListener = new NotificationListener(); 89 90 final IProcessObserver.Stub mProcessObserver = new IProcessObserver.Stub() { 91 @Override 92 public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) { 93 } 94 95 @Override 96 public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) { 97 final String packageName = mAppRestrictionController.getPackageName(pid); 98 if (packageName != null) { 99 mHandler.obtainMessage(MyHandler.MSG_FOREGROUND_SERVICES_CHANGED, 100 uid, serviceTypes, packageName).sendToTarget(); 101 } 102 } 103 104 @Override 105 public void onProcessStarted(int pid, int processUid, int packageUid, String packageName, 106 String processName) { 107 } 108 109 @Override 110 public void onProcessDied(int pid, int uid) { 111 } 112 }; 113 114 @Override onForegroundServiceStateChanged(String packageName, int uid, int pid, boolean started)115 public void onForegroundServiceStateChanged(String packageName, 116 int uid, int pid, boolean started) { 117 mHandler.obtainMessage(started ? MyHandler.MSG_FOREGROUND_SERVICES_STARTED 118 : MyHandler.MSG_FOREGROUND_SERVICES_STOPPED, pid, uid, packageName).sendToTarget(); 119 } 120 121 @Override onForegroundServiceNotificationUpdated(String packageName, int uid, int foregroundId, boolean canceling)122 public void onForegroundServiceNotificationUpdated(String packageName, int uid, 123 int foregroundId, boolean canceling) { 124 final SomeArgs args = SomeArgs.obtain(); 125 args.argi1 = uid; 126 args.argi2 = foregroundId; 127 args.arg1 = packageName; 128 args.arg2 = canceling ? Boolean.TRUE : Boolean.FALSE; 129 mHandler.obtainMessage(MyHandler.MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED, args) 130 .sendToTarget(); 131 } 132 133 private static class MyHandler extends Handler { 134 static final int MSG_FOREGROUND_SERVICES_STARTED = 0; 135 static final int MSG_FOREGROUND_SERVICES_STOPPED = 1; 136 static final int MSG_FOREGROUND_SERVICES_CHANGED = 2; 137 static final int MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED = 3; 138 static final int MSG_CHECK_LONG_RUNNING_FGS = 4; 139 static final int MSG_NOTIFICATION_POSTED = 5; 140 static final int MSG_NOTIFICATION_REMOVED = 6; 141 142 private final AppFGSTracker mTracker; 143 MyHandler(AppFGSTracker tracker)144 MyHandler(AppFGSTracker tracker) { 145 super(tracker.mBgHandler.getLooper()); 146 mTracker = tracker; 147 } 148 149 @Override handleMessage(Message msg)150 public void handleMessage(Message msg) { 151 switch (msg.what) { 152 case MSG_FOREGROUND_SERVICES_STARTED: 153 mTracker.handleForegroundServicesChanged( 154 (String) msg.obj, msg.arg1, msg.arg2, true); 155 break; 156 case MSG_FOREGROUND_SERVICES_STOPPED: 157 mTracker.handleForegroundServicesChanged( 158 (String) msg.obj, msg.arg1, msg.arg2, false); 159 break; 160 case MSG_FOREGROUND_SERVICES_CHANGED: 161 mTracker.handleForegroundServicesChanged( 162 (String) msg.obj, msg.arg1, msg.arg2); 163 break; 164 case MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED: 165 final SomeArgs args = (SomeArgs) msg.obj; 166 mTracker.handleForegroundServiceNotificationUpdated( 167 (String) args.arg1, args.argi1, args.argi2, (Boolean) args.arg2); 168 args.recycle(); 169 break; 170 case MSG_CHECK_LONG_RUNNING_FGS: 171 mTracker.checkLongRunningFgs(); 172 break; 173 case MSG_NOTIFICATION_POSTED: 174 mTracker.handleNotificationPosted((String) msg.obj, msg.arg1, msg.arg2); 175 break; 176 case MSG_NOTIFICATION_REMOVED: 177 mTracker.handleNotificationRemoved((String) msg.obj, msg.arg1, msg.arg2); 178 break; 179 } 180 } 181 } 182 AppFGSTracker(Context context, AppRestrictionController controller)183 AppFGSTracker(Context context, AppRestrictionController controller) { 184 this(context, controller, null, null); 185 } 186 AppFGSTracker(Context context, AppRestrictionController controller, Constructor<? extends Injector<AppFGSPolicy>> injector, Object outerContext)187 AppFGSTracker(Context context, AppRestrictionController controller, 188 Constructor<? extends Injector<AppFGSPolicy>> injector, Object outerContext) { 189 super(context, controller, injector, outerContext); 190 mHandler = new MyHandler(this); 191 mInjector.setPolicy(new AppFGSPolicy(mInjector, this)); 192 } 193 194 @Override getType()195 @TrackerType int getType() { 196 return AppRestrictionController.TRACKER_TYPE_FGS; 197 } 198 199 @Override onSystemReady()200 void onSystemReady() { 201 super.onSystemReady(); 202 mInjector.getActivityManagerInternal().addForegroundServiceStateListener(this); 203 mInjector.getActivityManagerInternal().registerProcessObserver(mProcessObserver); 204 } 205 206 @VisibleForTesting 207 @Override reset()208 void reset() { 209 mHandler.removeMessages(MyHandler.MSG_CHECK_LONG_RUNNING_FGS); 210 super.reset(); 211 } 212 213 @Override createAppStateEvents(int uid, String packageName)214 public PackageDurations createAppStateEvents(int uid, String packageName) { 215 return new PackageDurations(uid, packageName, mInjector.getPolicy(), this); 216 } 217 218 @Override createAppStateEvents(PackageDurations other)219 public PackageDurations createAppStateEvents(PackageDurations other) { 220 return new PackageDurations(other); 221 } 222 handleForegroundServicesChanged(String packageName, int pid, int uid, boolean started)223 private void handleForegroundServicesChanged(String packageName, int pid, int uid, 224 boolean started) { 225 if (!mInjector.getPolicy().isEnabled()) { 226 return; 227 } 228 final long now = SystemClock.elapsedRealtime(); 229 boolean longRunningFGSGone = false; 230 final int exemptReason = mInjector.getPolicy().shouldExemptUid(uid); 231 if (DEBUG_BACKGROUND_FGS_TRACKER) { 232 Slog.i(TAG, (started ? "Starting" : "Stopping") + " fgs in " 233 + packageName + "/" + UserHandle.formatUid(uid) 234 + " exemptReason=" + exemptReason); 235 } 236 synchronized (mLock) { 237 PackageDurations pkg = mPkgEvents.get(uid, packageName); 238 if (pkg == null) { 239 pkg = createAppStateEvents(uid, packageName); 240 mPkgEvents.put(uid, packageName, pkg); 241 } 242 final boolean wasLongRunning = pkg.isLongRunning(); 243 pkg.addEvent(started, now); 244 longRunningFGSGone = wasLongRunning && !pkg.hasForegroundServices(); 245 if (longRunningFGSGone) { 246 pkg.setIsLongRunning(false); 247 } 248 pkg.mExemptReason = exemptReason; 249 // Reschedule the checks. 250 scheduleDurationCheckLocked(now); 251 } 252 if (longRunningFGSGone) { 253 // The long-running FGS is gone, cancel the notification. 254 mInjector.getPolicy().onLongRunningFgsGone(packageName, uid); 255 } 256 } 257 handleForegroundServiceNotificationUpdated(String packageName, int uid, int notificationId, boolean canceling)258 private void handleForegroundServiceNotificationUpdated(String packageName, int uid, 259 int notificationId, boolean canceling) { 260 synchronized (mLock) { 261 SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, packageName); 262 if (!canceling) { 263 if (notificationIDs == null) { 264 notificationIDs = new SparseBooleanArray(); 265 mFGSNotificationIDs.put(uid, packageName, notificationIDs); 266 } 267 notificationIDs.put(notificationId, false); 268 } else { 269 if (notificationIDs != null) { 270 final int indexOfKey = notificationIDs.indexOfKey(notificationId); 271 if (indexOfKey >= 0) { 272 final boolean wasVisible = notificationIDs.valueAt(indexOfKey); 273 notificationIDs.removeAt(indexOfKey); 274 if (notificationIDs.size() == 0) { 275 mFGSNotificationIDs.remove(uid, packageName); 276 } 277 // Walk through the list of FGS notification IDs and see if there are any 278 // visible ones. 279 for (int i = notificationIDs.size() - 1; i >= 0; i--) { 280 if (notificationIDs.valueAt(i)) { 281 // Still visible, nothing to do. 282 return; 283 } 284 } 285 if (wasVisible) { 286 // That was the last visible notification, notify the listeners. 287 notifyListenersOnStateChange(uid, packageName, false, 288 SystemClock.elapsedRealtime(), 289 STATE_TYPE_FGS_WITH_NOTIFICATION); 290 } 291 } 292 } 293 } 294 } 295 } 296 297 @GuardedBy("mLock") hasForegroundServiceNotificationsLocked(String packageName, int uid)298 private boolean hasForegroundServiceNotificationsLocked(String packageName, int uid) { 299 final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, packageName); 300 if (notificationIDs == null || notificationIDs.size() == 0) { 301 return false; 302 } 303 for (int i = notificationIDs.size() - 1; i >= 0; i--) { 304 if (notificationIDs.valueAt(i)) { 305 return true; 306 } 307 } 308 return false; 309 } 310 handleNotificationPosted(String pkgName, int uid, int notificationId)311 private void handleNotificationPosted(String pkgName, int uid, int notificationId) { 312 synchronized (mLock) { 313 final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, pkgName); 314 final int indexOfKey; 315 if (notificationIDs == null 316 || (indexOfKey = notificationIDs.indexOfKey(notificationId)) < 0) { 317 return; 318 } 319 if (notificationIDs.valueAt(indexOfKey)) { 320 // It's already visible. 321 return; 322 } 323 boolean anyVisible = false; 324 // Walk through the list of FGS notification IDs and see if there are any visible ones. 325 for (int i = notificationIDs.size() - 1; i >= 0; i--) { 326 if (notificationIDs.valueAt(i)) { 327 anyVisible = true; 328 break; 329 } 330 } 331 notificationIDs.setValueAt(indexOfKey, true); 332 if (!anyVisible) { 333 // We didn't have any visible FGS notifications but now we have one, 334 // let the listeners know. 335 notifyListenersOnStateChange(uid, pkgName, true, SystemClock.elapsedRealtime(), 336 STATE_TYPE_FGS_WITH_NOTIFICATION); 337 } 338 } 339 } 340 handleNotificationRemoved(String pkgName, int uid, int notificationId)341 private void handleNotificationRemoved(String pkgName, int uid, int notificationId) { 342 synchronized (mLock) { 343 final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, pkgName); 344 final int indexOfKey; 345 if (notificationIDs == null 346 || (indexOfKey = notificationIDs.indexOfKey(notificationId)) < 0) { 347 return; 348 } 349 if (!notificationIDs.valueAt(indexOfKey)) { 350 // It's already invisible. 351 return; 352 } 353 notificationIDs.setValueAt(indexOfKey, false); 354 // Walk through the list of FGS notification IDs and see if there are any visible ones. 355 for (int i = notificationIDs.size() - 1; i >= 0; i--) { 356 if (notificationIDs.valueAt(i)) { 357 // Still visible, nothing to do. 358 return; 359 } 360 } 361 // Nothing is visible now, let the listeners know. 362 notifyListenersOnStateChange(uid, pkgName, false, SystemClock.elapsedRealtime(), 363 STATE_TYPE_FGS_WITH_NOTIFICATION); 364 } 365 } 366 367 @GuardedBy("mLock") scheduleDurationCheckLocked(long now)368 private void scheduleDurationCheckLocked(long now) { 369 // Look for the active FGS with longest running time till now. 370 final SparseArray<ArrayMap<String, PackageDurations>> map = mPkgEvents.getMap(); 371 long longest = -1; 372 for (int i = map.size() - 1; i >= 0; i--) { 373 final ArrayMap<String, PackageDurations> val = map.valueAt(i); 374 for (int j = val.size() - 1; j >= 0; j--) { 375 final PackageDurations pkg = val.valueAt(j); 376 if (!pkg.hasForegroundServices() || pkg.isLongRunning()) { 377 // No FGS or it's a known long-running FGS, ignore it. 378 continue; 379 } 380 longest = Math.max(getTotalDurations(pkg, now), longest); 381 } 382 } 383 // Schedule a check in the future. 384 mHandler.removeMessages(MyHandler.MSG_CHECK_LONG_RUNNING_FGS); 385 if (longest >= 0) { 386 // We'd add the "service start foreground timeout", as the apps are allowed 387 // to call startForeground() within that timeout after the FGS being started. 388 final long future = mInjector.getServiceStartForegroundTimeout() 389 + Math.max(0, mInjector.getPolicy().getFgsLongRunningThreshold() - longest); 390 if (DEBUG_BACKGROUND_FGS_TRACKER) { 391 Slog.i(TAG, "Scheduling a FGS duration check at " 392 + TimeUtils.formatDuration(future)); 393 } 394 mHandler.sendEmptyMessageDelayed(MyHandler.MSG_CHECK_LONG_RUNNING_FGS, future); 395 } else if (DEBUG_BACKGROUND_FGS_TRACKER) { 396 Slog.i(TAG, "Not scheduling FGS duration check"); 397 } 398 } 399 checkLongRunningFgs()400 private void checkLongRunningFgs() { 401 final AppFGSPolicy policy = mInjector.getPolicy(); 402 final ArrayMap<PackageDurations, Long> pkgWithLongFgs = mTmpPkgDurations; 403 final long now = SystemClock.elapsedRealtime(); 404 final long threshold = policy.getFgsLongRunningThreshold(); 405 final long windowSize = policy.getFgsLongRunningWindowSize(); 406 final long trimTo = Math.max(0, now - windowSize); 407 408 synchronized (mLock) { 409 final SparseArray<ArrayMap<String, PackageDurations>> map = mPkgEvents.getMap(); 410 for (int i = map.size() - 1; i >= 0; i--) { 411 final ArrayMap<String, PackageDurations> val = map.valueAt(i); 412 for (int j = val.size() - 1; j >= 0; j--) { 413 final PackageDurations pkg = val.valueAt(j); 414 if (pkg.hasForegroundServices() && !pkg.isLongRunning()) { 415 final long totalDuration = getTotalDurations(pkg, now); 416 if (totalDuration >= threshold) { 417 pkgWithLongFgs.put(pkg, totalDuration); 418 pkg.setIsLongRunning(true); 419 if (DEBUG_BACKGROUND_FGS_TRACKER) { 420 Slog.i(TAG, pkg.mPackageName 421 + "/" + UserHandle.formatUid(pkg.mUid) 422 + " has FGS running for " 423 + TimeUtils.formatDuration(totalDuration) 424 + " over " + TimeUtils.formatDuration(windowSize)); 425 } 426 } 427 } 428 } 429 } 430 // Trim the duration list, we don't need to keep track of all old records. 431 trim(trimTo); 432 } 433 434 final int size = pkgWithLongFgs.size(); 435 if (size > 0) { 436 // Sort it by the durations. 437 final Integer[] indices = new Integer[size]; 438 for (int i = 0; i < size; i++) { 439 indices[i] = i; 440 } 441 Arrays.sort(indices, (a, b) -> Long.compare( 442 pkgWithLongFgs.valueAt(a), pkgWithLongFgs.valueAt(b))); 443 // Notify it in the order of from longest to shortest durations. 444 for (int i = size - 1; i >= 0; i--) { 445 final PackageDurations pkg = pkgWithLongFgs.keyAt(indices[i]); 446 policy.onLongRunningFgs(pkg.mPackageName, pkg.mUid, pkg.mExemptReason); 447 } 448 pkgWithLongFgs.clear(); 449 } 450 451 synchronized (mLock) { 452 scheduleDurationCheckLocked(now); 453 } 454 } 455 handleForegroundServicesChanged(String packageName, int uid, int serviceTypes)456 private void handleForegroundServicesChanged(String packageName, int uid, int serviceTypes) { 457 if (!mInjector.getPolicy().isEnabled()) { 458 return; 459 } 460 final int exemptReason = mInjector.getPolicy().shouldExemptUid(uid); 461 final long now = SystemClock.elapsedRealtime(); 462 if (DEBUG_BACKGROUND_FGS_TRACKER) { 463 Slog.i(TAG, "Updating fgs type for " + packageName + "/" + UserHandle.formatUid(uid) 464 + " to " + Integer.toHexString(serviceTypes) 465 + " exemptReason=" + exemptReason); 466 } 467 synchronized (mLock) { 468 PackageDurations pkg = mPkgEvents.get(uid, packageName); 469 if (pkg == null) { 470 pkg = new PackageDurations(uid, packageName, mInjector.getPolicy(), this); 471 mPkgEvents.put(uid, packageName, pkg); 472 } 473 pkg.setForegroundServiceType(serviceTypes, now); 474 pkg.mExemptReason = exemptReason; 475 } 476 } 477 onBgFgsMonitorEnabled(boolean enabled)478 private void onBgFgsMonitorEnabled(boolean enabled) { 479 if (enabled) { 480 synchronized (mLock) { 481 scheduleDurationCheckLocked(SystemClock.elapsedRealtime()); 482 } 483 try { 484 mNotificationListener.registerAsSystemService(mContext, 485 new ComponentName(mContext, NotificationListener.class), 486 UserHandle.USER_ALL); 487 } catch (RemoteException e) { 488 // Intra-process call, should never happen. 489 } 490 } else { 491 try { 492 mNotificationListener.unregisterAsSystemService(); 493 } catch (RemoteException e) { 494 // Intra-process call, should never happen. 495 } 496 mHandler.removeMessages(MyHandler.MSG_CHECK_LONG_RUNNING_FGS); 497 synchronized (mLock) { 498 mPkgEvents.clear(); 499 } 500 } 501 } 502 onBgFgsLongRunningThresholdChanged()503 private void onBgFgsLongRunningThresholdChanged() { 504 synchronized (mLock) { 505 if (mInjector.getPolicy().isEnabled()) { 506 scheduleDurationCheckLocked(SystemClock.elapsedRealtime()); 507 } 508 } 509 } 510 foregroundServiceTypeToIndex(@oregroundServiceType int serviceType)511 static int foregroundServiceTypeToIndex(@ForegroundServiceType int serviceType) { 512 return serviceType == FOREGROUND_SERVICE_TYPE_NONE ? 0 513 : Integer.numberOfTrailingZeros(serviceType) + 1; 514 } 515 indexToForegroundServiceType(int index)516 static @ForegroundServiceType int indexToForegroundServiceType(int index) { 517 return index == PackageDurations.DEFAULT_INDEX 518 ? FOREGROUND_SERVICE_TYPE_NONE : (1 << (index - 1)); 519 } 520 getTotalDurations(PackageDurations pkg, long now)521 long getTotalDurations(PackageDurations pkg, long now) { 522 return getTotalDurations(pkg.mPackageName, pkg.mUid, now, 523 foregroundServiceTypeToIndex(FOREGROUND_SERVICE_TYPE_NONE)); 524 } 525 526 @Override getTotalDurations(int uid, long now)527 long getTotalDurations(int uid, long now) { 528 return getTotalDurations(uid, now, 529 foregroundServiceTypeToIndex(FOREGROUND_SERVICE_TYPE_NONE)); 530 } 531 hasForegroundServices(String packageName, int uid)532 boolean hasForegroundServices(String packageName, int uid) { 533 synchronized (mLock) { 534 final PackageDurations pkg = mPkgEvents.get(uid, packageName); 535 return pkg != null && pkg.hasForegroundServices(); 536 } 537 } 538 hasForegroundServices(int uid)539 boolean hasForegroundServices(int uid) { 540 synchronized (mLock) { 541 final SparseArray<ArrayMap<String, PackageDurations>> map = mPkgEvents.getMap(); 542 final ArrayMap<String, PackageDurations> pkgs = map.get(uid); 543 if (pkgs != null) { 544 for (int i = pkgs.size() - 1; i >= 0; i--) { 545 final PackageDurations pkg = pkgs.valueAt(i); 546 if (pkg.hasForegroundServices()) { 547 return true; 548 } 549 } 550 } 551 return false; 552 } 553 } 554 hasForegroundServiceNotifications(String packageName, int uid)555 boolean hasForegroundServiceNotifications(String packageName, int uid) { 556 synchronized (mLock) { 557 return hasForegroundServiceNotificationsLocked(packageName, uid); 558 } 559 } 560 hasForegroundServiceNotifications(int uid)561 boolean hasForegroundServiceNotifications(int uid) { 562 synchronized (mLock) { 563 final SparseArray<ArrayMap<String, SparseBooleanArray>> map = 564 mFGSNotificationIDs.getMap(); 565 final ArrayMap<String, SparseBooleanArray> pkgs = map.get(uid); 566 if (pkgs != null) { 567 for (int i = pkgs.size() - 1; i >= 0; i--) { 568 if (hasForegroundServiceNotificationsLocked(pkgs.keyAt(i), uid)) { 569 return true; 570 } 571 } 572 } 573 } 574 return false; 575 } 576 577 @Override getTrackerInfoForStatsd(int uid)578 byte[] getTrackerInfoForStatsd(int uid) { 579 final long fgsDurations = getTotalDurations(uid, SystemClock.elapsedRealtime()); 580 if (fgsDurations == 0L) { 581 // Not interested 582 return null; 583 } 584 final ProtoOutputStream proto = new ProtoOutputStream(); 585 proto.write(AppBackgroundRestrictionsInfo.FgsTrackerInfo.FGS_NOTIFICATION_VISIBLE, 586 hasForegroundServiceNotifications(uid)); 587 proto.write(AppBackgroundRestrictionsInfo.FgsTrackerInfo.FGS_DURATION, fgsDurations); 588 proto.flush(); 589 return proto.getBytes(); 590 } 591 592 @Override dump(PrintWriter pw, String prefix)593 void dump(PrintWriter pw, String prefix) { 594 pw.print(prefix); 595 pw.println("APP FOREGROUND SERVICE TRACKER:"); 596 super.dump(pw, " " + prefix); 597 } 598 599 @Override dumpOthers(PrintWriter pw, String prefix)600 void dumpOthers(PrintWriter pw, String prefix) { 601 pw.print(prefix); 602 pw.println("APPS WITH ACTIVE FOREGROUND SERVICES:"); 603 prefix = " " + prefix; 604 synchronized (mLock) { 605 final SparseArray<ArrayMap<String, SparseBooleanArray>> map = 606 mFGSNotificationIDs.getMap(); 607 if (map.size() == 0) { 608 pw.print(prefix); 609 pw.println("(none)"); 610 } 611 for (int i = 0, size = map.size(); i < size; i++) { 612 final int uid = map.keyAt(i); 613 final String uidString = UserHandle.formatUid(uid); 614 final ArrayMap<String, SparseBooleanArray> pkgs = map.valueAt(i); 615 for (int j = 0, numOfPkgs = pkgs.size(); j < numOfPkgs; j++) { 616 final String pkgName = pkgs.keyAt(j); 617 pw.print(prefix); 618 pw.print(pkgName); 619 pw.print('/'); 620 pw.print(uidString); 621 pw.print(" notification="); 622 pw.println(hasForegroundServiceNotificationsLocked(pkgName, uid)); 623 } 624 } 625 } 626 } 627 628 /** 629 * Tracks the durations with active FGS for a given package. 630 */ 631 static class PackageDurations extends BaseAppStateDurations<BaseTimeEvent> { 632 private final AppFGSTracker mTracker; 633 634 /** 635 * Whether or not this package is considered as having long-running FGS. 636 */ 637 private boolean mIsLongRunning; 638 639 /** 640 * The current foreground service types, should be a combination of the values in 641 * {@link android.content.pm.ServiceInfo.ForegroundServiceType}. 642 */ 643 private int mForegroundServiceTypes; 644 645 /** 646 * The index to the duration list array, where it holds the overall FGS stats of this 647 * package. 648 */ 649 static final int DEFAULT_INDEX = foregroundServiceTypeToIndex(FOREGROUND_SERVICE_TYPE_NONE); 650 PackageDurations(int uid, String packageName, MaxTrackingDurationConfig maxTrackingDurationConfig, AppFGSTracker tracker)651 PackageDurations(int uid, String packageName, 652 MaxTrackingDurationConfig maxTrackingDurationConfig, AppFGSTracker tracker) { 653 super(uid, packageName, FOREGROUND_SERVICE_TYPES_MAX_INDEX + 1, TAG, 654 maxTrackingDurationConfig); 655 mEvents[DEFAULT_INDEX] = new LinkedList<>(); 656 mTracker = tracker; 657 } 658 PackageDurations(@onNull PackageDurations other)659 PackageDurations(@NonNull PackageDurations other) { 660 super(other); 661 mIsLongRunning = other.mIsLongRunning; 662 mForegroundServiceTypes = other.mForegroundServiceTypes; 663 mTracker = other.mTracker; 664 } 665 666 /** 667 * Add a foreground service start/stop event. 668 */ addEvent(boolean startFgs, long now)669 void addEvent(boolean startFgs, long now) { 670 addEvent(startFgs, new BaseTimeEvent(now), DEFAULT_INDEX); 671 if (!startFgs && !hasForegroundServices()) { 672 mIsLongRunning = false; 673 } 674 675 if (!startFgs && mForegroundServiceTypes != FOREGROUND_SERVICE_TYPE_NONE) { 676 // Save the stop time per service type. 677 for (int i = 1; i < mEvents.length; i++) { 678 if (mEvents[i] == null) { 679 continue; 680 } 681 if (isActive(i)) { 682 mEvents[i].add(new BaseTimeEvent(now)); 683 notifyListenersOnStateChangeIfNecessary(false, now, 684 indexToForegroundServiceType(i)); 685 } 686 } 687 mForegroundServiceTypes = FOREGROUND_SERVICE_TYPE_NONE; 688 } 689 } 690 691 /** 692 * Called on the service type changes via the {@link android.app.Service#startForeground}. 693 */ setForegroundServiceType(int serviceTypes, long now)694 void setForegroundServiceType(int serviceTypes, long now) { 695 if (serviceTypes == mForegroundServiceTypes || !hasForegroundServices()) { 696 // Nothing to do. 697 return; 698 } 699 int changes = serviceTypes ^ mForegroundServiceTypes; 700 for (int serviceType = Integer.highestOneBit(changes); serviceType != 0;) { 701 final int i = foregroundServiceTypeToIndex(serviceType); 702 if (i < mEvents.length) { 703 if ((serviceTypes & serviceType) != 0) { 704 // Start this type. 705 if (mEvents[i] == null) { 706 mEvents[i] = new LinkedList<>(); 707 } 708 if (!isActive(i)) { 709 mEvents[i].add(new BaseTimeEvent(now)); 710 notifyListenersOnStateChangeIfNecessary(true, now, serviceType); 711 } 712 } else { 713 // Stop this type. 714 if (mEvents[i] != null && isActive(i)) { 715 mEvents[i].add(new BaseTimeEvent(now)); 716 notifyListenersOnStateChangeIfNecessary(false, now, serviceType); 717 } 718 } 719 } 720 changes &= ~serviceType; 721 serviceType = Integer.highestOneBit(changes); 722 } 723 mForegroundServiceTypes = serviceTypes; 724 } 725 notifyListenersOnStateChangeIfNecessary(boolean start, long now, @ForegroundServiceType int serviceType)726 private void notifyListenersOnStateChangeIfNecessary(boolean start, long now, 727 @ForegroundServiceType int serviceType) { 728 int stateType; 729 switch (serviceType) { 730 case FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK: 731 stateType = BaseAppStateDurationsTracker.STATE_TYPE_FGS_MEDIA_PLAYBACK; 732 break; 733 case FOREGROUND_SERVICE_TYPE_LOCATION: 734 stateType = BaseAppStateDurationsTracker.STATE_TYPE_FGS_LOCATION; 735 break; 736 default: 737 return; 738 } 739 mTracker.notifyListenersOnStateChange(mUid, mPackageName, start, now, stateType); 740 } 741 setIsLongRunning(boolean isLongRunning)742 void setIsLongRunning(boolean isLongRunning) { 743 mIsLongRunning = isLongRunning; 744 } 745 isLongRunning()746 boolean isLongRunning() { 747 return mIsLongRunning; 748 } 749 hasForegroundServices()750 boolean hasForegroundServices() { 751 return isActive(DEFAULT_INDEX); 752 } 753 754 @Override formatEventTypeLabel(int index)755 String formatEventTypeLabel(int index) { 756 if (index == DEFAULT_INDEX) { 757 return "Overall foreground services: "; 758 } else { 759 return foregroundServiceTypeToLabel(indexToForegroundServiceType(index)) + ": "; 760 } 761 } 762 } 763 764 @VisibleForTesting 765 class NotificationListener extends NotificationListenerService { 766 @Override onNotificationPosted(StatusBarNotification sbn, RankingMap map)767 public void onNotificationPosted(StatusBarNotification sbn, RankingMap map) { 768 if (DEBUG_BACKGROUND_FGS_TRACKER) { 769 Slog.i(TAG, "Notification posted: " + sbn); 770 } 771 mHandler.obtainMessage(MyHandler.MSG_NOTIFICATION_POSTED, 772 sbn.getUid(), sbn.getId(), sbn.getPackageName()).sendToTarget(); 773 } 774 775 @Override onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason)776 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, 777 int reason) { 778 if (DEBUG_BACKGROUND_FGS_TRACKER) { 779 Slog.i(TAG, "Notification removed: " + sbn); 780 } 781 mHandler.obtainMessage(MyHandler.MSG_NOTIFICATION_REMOVED, 782 sbn.getUid(), sbn.getId(), sbn.getPackageName()).sendToTarget(); 783 } 784 } 785 786 static final class AppFGSPolicy extends BaseAppStateEventsPolicy<AppFGSTracker> { 787 /** 788 * Whether or not we should enable the monitoring on abusive FGS. 789 */ 790 static final String KEY_BG_FGS_MONITOR_ENABLED = 791 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "fgs_monitor_enabled"; 792 793 /** 794 * The size of the sliding window in which the accumulated FGS durations are checked 795 * against the threshold. 796 */ 797 static final String KEY_BG_FGS_LONG_RUNNING_WINDOW = 798 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "fgs_long_running_window"; 799 800 /** 801 * The threshold at where the accumulated FGS durations are considered as "long-running" 802 * within the given window. 803 */ 804 static final String KEY_BG_FGS_LONG_RUNNING_THRESHOLD = 805 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "fgs_long_running_threshold"; 806 807 /** 808 * If a package has run FGS with "mediaPlayback" over this threshold, it won't be considered 809 * as a long-running FGS. 810 */ 811 static final String KEY_BG_FGS_MEDIA_PLAYBACK_THRESHOLD = 812 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "fgs_media_playback_threshold"; 813 814 /** 815 * If a package has run FGS with "location" over this threshold, it won't be considered 816 * as a long-running FGS. 817 */ 818 static final String KEY_BG_FGS_LOCATION_THRESHOLD = 819 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "fgs_location_threshold"; 820 821 /** 822 * Default value to {@link #mTrackerEnabled}. 823 */ 824 static final boolean DEFAULT_BG_FGS_MONITOR_ENABLED = true; 825 826 /** 827 * Default value to {@link #mMaxTrackingDuration}. 828 */ 829 static final long DEFAULT_BG_FGS_LONG_RUNNING_WINDOW = ONE_DAY; 830 831 /** 832 * Default value to {@link #mBgFgsLongRunningThresholdMs}. 833 */ 834 static final long DEFAULT_BG_FGS_LONG_RUNNING_THRESHOLD = 20 * ONE_HOUR; 835 836 /** 837 * Default value to {@link #mBgFgsMediaPlaybackThresholdMs}. 838 */ 839 static final long DEFAULT_BG_FGS_MEDIA_PLAYBACK_THRESHOLD = 4 * ONE_HOUR; 840 841 /** 842 * Default value to {@link #mBgFgsLocationThresholdMs}. 843 */ 844 static final long DEFAULT_BG_FGS_LOCATION_THRESHOLD = 4 * ONE_HOUR; 845 846 /** 847 * @see #KEY_BG_FGS_LONG_RUNNING_THRESHOLD. 848 */ 849 private volatile long mBgFgsLongRunningThresholdMs = DEFAULT_BG_FGS_LONG_RUNNING_THRESHOLD; 850 851 /** 852 * @see #KEY_BG_FGS_MEDIA_PLAYBACK_THRESHOLD. 853 */ 854 private volatile long mBgFgsMediaPlaybackThresholdMs = 855 DEFAULT_BG_FGS_MEDIA_PLAYBACK_THRESHOLD; 856 857 /** 858 * @see #KEY_BG_FGS_LOCATION_THRESHOLD. 859 */ 860 private volatile long mBgFgsLocationThresholdMs = DEFAULT_BG_FGS_LOCATION_THRESHOLD; 861 AppFGSPolicy(@onNull Injector injector, @NonNull AppFGSTracker tracker)862 AppFGSPolicy(@NonNull Injector injector, @NonNull AppFGSTracker tracker) { 863 super(injector, tracker, KEY_BG_FGS_MONITOR_ENABLED, DEFAULT_BG_FGS_MONITOR_ENABLED, 864 KEY_BG_FGS_LONG_RUNNING_WINDOW, DEFAULT_BG_FGS_LONG_RUNNING_WINDOW); 865 } 866 867 @Override onSystemReady()868 public void onSystemReady() { 869 super.onSystemReady(); 870 updateBgFgsLongRunningThreshold(); 871 updateBgFgsMediaPlaybackThreshold(); 872 updateBgFgsLocationThreshold(); 873 } 874 875 @Override onPropertiesChanged(String name)876 public void onPropertiesChanged(String name) { 877 switch (name) { 878 case KEY_BG_FGS_LONG_RUNNING_THRESHOLD: 879 updateBgFgsLongRunningThreshold(); 880 break; 881 case KEY_BG_FGS_MEDIA_PLAYBACK_THRESHOLD: 882 updateBgFgsMediaPlaybackThreshold(); 883 break; 884 case KEY_BG_FGS_LOCATION_THRESHOLD: 885 updateBgFgsLocationThreshold(); 886 break; 887 default: 888 super.onPropertiesChanged(name); 889 break; 890 } 891 } 892 893 @Override onTrackerEnabled(boolean enabled)894 public void onTrackerEnabled(boolean enabled) { 895 mTracker.onBgFgsMonitorEnabled(enabled); 896 } 897 898 @Override onMaxTrackingDurationChanged(long maxDuration)899 public void onMaxTrackingDurationChanged(long maxDuration) { 900 mTracker.onBgFgsLongRunningThresholdChanged(); 901 } 902 updateBgFgsLongRunningThreshold()903 private void updateBgFgsLongRunningThreshold() { 904 final long threshold = DeviceConfig.getLong( 905 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 906 KEY_BG_FGS_LONG_RUNNING_THRESHOLD, 907 DEFAULT_BG_FGS_LONG_RUNNING_THRESHOLD); 908 if (threshold != mBgFgsLongRunningThresholdMs) { 909 mBgFgsLongRunningThresholdMs = threshold; 910 mTracker.onBgFgsLongRunningThresholdChanged(); 911 } 912 } 913 updateBgFgsMediaPlaybackThreshold()914 private void updateBgFgsMediaPlaybackThreshold() { 915 mBgFgsMediaPlaybackThresholdMs = DeviceConfig.getLong( 916 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 917 KEY_BG_FGS_MEDIA_PLAYBACK_THRESHOLD, 918 DEFAULT_BG_FGS_MEDIA_PLAYBACK_THRESHOLD); 919 } 920 updateBgFgsLocationThreshold()921 private void updateBgFgsLocationThreshold() { 922 mBgFgsLocationThresholdMs = DeviceConfig.getLong( 923 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 924 KEY_BG_FGS_LOCATION_THRESHOLD, 925 DEFAULT_BG_FGS_LOCATION_THRESHOLD); 926 } 927 getFgsLongRunningThreshold()928 long getFgsLongRunningThreshold() { 929 return mBgFgsLongRunningThresholdMs; 930 } 931 getFgsLongRunningWindowSize()932 long getFgsLongRunningWindowSize() { 933 return getMaxTrackingDuration(); 934 } 935 getFGSMediaPlaybackThreshold()936 long getFGSMediaPlaybackThreshold() { 937 return mBgFgsMediaPlaybackThresholdMs; 938 } 939 getLocationFGSThreshold()940 long getLocationFGSThreshold() { 941 return mBgFgsLocationThresholdMs; 942 } 943 onLongRunningFgs(String packageName, int uid, @ReasonCode int exemptReason)944 void onLongRunningFgs(String packageName, int uid, @ReasonCode int exemptReason) { 945 if (exemptReason != REASON_DENIED) { 946 return; 947 } 948 final long now = SystemClock.elapsedRealtime(); 949 final long window = getFgsLongRunningWindowSize(); 950 final long since = Math.max(0, now - window); 951 if (shouldExemptMediaPlaybackFGS(packageName, uid, now, window)) { 952 return; 953 } 954 if (shouldExemptLocationFGS(packageName, uid, now, since)) { 955 return; 956 } 957 mTracker.mAppRestrictionController.postLongRunningFgsIfNecessary(packageName, uid); 958 } 959 shouldExemptMediaPlaybackFGS(String packageName, int uid, long now, long window)960 boolean shouldExemptMediaPlaybackFGS(String packageName, int uid, long now, long window) { 961 final long mediaPlaybackMs = mTracker.mAppRestrictionController 962 .getCompositeMediaPlaybackDurations(packageName, uid, now, window); 963 if (mediaPlaybackMs > 0 && mediaPlaybackMs >= getFGSMediaPlaybackThreshold()) { 964 if (DEBUG_BACKGROUND_FGS_TRACKER) { 965 Slog.i(TAG, "Ignoring long-running FGS in " + packageName + "/" 966 + UserHandle.formatUid(uid) + " media playback for " 967 + TimeUtils.formatDuration(mediaPlaybackMs)); 968 } 969 return true; 970 } 971 return false; 972 } 973 shouldExemptLocationFGS(String packageName, int uid, long now, long since)974 boolean shouldExemptLocationFGS(String packageName, int uid, long now, long since) { 975 final long locationMs = mTracker.mAppRestrictionController 976 .getForegroundServiceTotalDurationsSince(packageName, uid, since, now, 977 FOREGROUND_SERVICE_TYPE_LOCATION); 978 if (locationMs > 0 && locationMs >= getLocationFGSThreshold()) { 979 if (DEBUG_BACKGROUND_FGS_TRACKER) { 980 Slog.i(TAG, "Ignoring long-running FGS in " + packageName + "/" 981 + UserHandle.formatUid(uid) + " location for " 982 + TimeUtils.formatDuration(locationMs)); 983 } 984 return true; 985 } 986 return false; 987 } 988 989 @Override getExemptionReasonString(String packageName, int uid, @ReasonCode int reason)990 String getExemptionReasonString(String packageName, int uid, @ReasonCode int reason) { 991 if (reason != REASON_DENIED) { 992 return super.getExemptionReasonString(packageName, uid, reason); 993 } 994 final long now = SystemClock.elapsedRealtime(); 995 final long window = getFgsLongRunningWindowSize(); 996 final long since = Math.max(0, now - getFgsLongRunningWindowSize()); 997 return "{mediaPlayback=" + shouldExemptMediaPlaybackFGS(packageName, uid, now, window) 998 + ", location=" + shouldExemptLocationFGS(packageName, uid, now, since) + "}"; 999 } 1000 onLongRunningFgsGone(String packageName, int uid)1001 void onLongRunningFgsGone(String packageName, int uid) { 1002 mTracker.mAppRestrictionController 1003 .cancelLongRunningFGSNotificationIfNecessary(packageName, uid); 1004 } 1005 1006 @Override dump(PrintWriter pw, String prefix)1007 void dump(PrintWriter pw, String prefix) { 1008 pw.print(prefix); 1009 pw.println("APP FOREGROUND SERVICE TRACKER POLICY SETTINGS:"); 1010 final String indent = " "; 1011 prefix = indent + prefix; 1012 super.dump(pw, prefix); 1013 if (isEnabled()) { 1014 pw.print(prefix); 1015 pw.print(KEY_BG_FGS_LONG_RUNNING_THRESHOLD); 1016 pw.print('='); 1017 pw.println(mBgFgsLongRunningThresholdMs); 1018 pw.print(prefix); 1019 pw.print(KEY_BG_FGS_MEDIA_PLAYBACK_THRESHOLD); 1020 pw.print('='); 1021 pw.println(mBgFgsMediaPlaybackThresholdMs); 1022 pw.print(prefix); 1023 pw.print(KEY_BG_FGS_LOCATION_THRESHOLD); 1024 pw.print('='); 1025 pw.println(mBgFgsLocationThresholdMs); 1026 } 1027 } 1028 } 1029 } 1030