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