• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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