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