• 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.Manifest.permission.MANAGE_ACTIVITY_TASKS;
20 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
21 import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
22 import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
23 import static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
24 import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
25 import static android.app.ActivityManager.RESTRICTION_LEVEL_EXEMPTED;
26 import static android.app.ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED;
27 import static android.app.ActivityManager.RESTRICTION_LEVEL_MAX;
28 import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
29 import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN;
30 import static android.app.ActivityManager.RESTRICTION_LEVEL_UNRESTRICTED;
31 import static android.app.ActivityManager.RESTRICTION_LEVEL_USER_LAUNCH_ONLY;
32 import static android.app.ActivityManager.RESTRICTION_REASON_DEFAULT;
33 import static android.app.ActivityManager.RESTRICTION_REASON_DORMANT;
34 import static android.app.ActivityManager.RESTRICTION_REASON_POLICY;
35 import static android.app.ActivityManager.RESTRICTION_REASON_SYSTEM_HEALTH;
36 import static android.app.ActivityManager.RESTRICTION_REASON_USAGE;
37 import static android.app.ActivityManager.RESTRICTION_REASON_USER;
38 import static android.app.ActivityManager.RESTRICTION_SUBREASON_MAX_LENGTH;
39 import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
40 import static android.app.ActivityManager.UID_OBSERVER_GONE;
41 import static android.app.ActivityManager.UID_OBSERVER_IDLE;
42 import static android.app.ActivityManager.UID_OBSERVER_PROCSTATE;
43 import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
44 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
45 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
46 import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
47 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
48 import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_UNDEFINED;
49 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED;
50 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_USER_FLAG_INTERACTION;
51 import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
52 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
53 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
54 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
55 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
56 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
57 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
58 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
59 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
60 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
61 import static android.app.usage.UsageStatsManager.reasonToString;
62 import static android.content.Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER;
63 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
64 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
65 import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
66 import static android.os.PowerExemptionManager.REASON_ACTIVE_DEVICE_ADMIN;
67 import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
68 import static android.os.PowerExemptionManager.REASON_CARRIER_PRIVILEGED_APP;
69 import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
70 import static android.os.PowerExemptionManager.REASON_DENIED;
71 import static android.os.PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
72 import static android.os.PowerExemptionManager.REASON_DEVICE_OWNER;
73 import static android.os.PowerExemptionManager.REASON_DISALLOW_APPS_CONTROL;
74 import static android.os.PowerExemptionManager.REASON_DPO_PROTECTED_APP;
75 import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_PLATFORM_VPN;
76 import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_VPN;
77 import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
78 import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
79 import static android.os.PowerExemptionManager.REASON_PROFILE_OWNER;
80 import static android.os.PowerExemptionManager.REASON_ROLE_DIALER;
81 import static android.os.PowerExemptionManager.REASON_ROLE_EMERGENCY;
82 import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
83 import static android.os.PowerExemptionManager.REASON_SYSTEM_EXEMPT_APP_OP;
84 import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE;
85 import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
86 import static android.os.PowerExemptionManager.getExemptionReasonForStatsd;
87 import static android.os.PowerExemptionManager.reasonCodeToString;
88 import static android.os.Process.SYSTEM_UID;
89 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
90 
91 import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
92 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
93 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
94 import static com.android.server.am.AppFGSTracker.foregroundServiceTypeToIndex;
95 import static com.android.server.am.BaseAppStateTracker.ONE_DAY;
96 
97 import android.annotation.CurrentTimeMillisLong;
98 import android.annotation.IntDef;
99 import android.annotation.NonNull;
100 import android.annotation.Nullable;
101 import android.annotation.UserIdInt;
102 import android.app.ActivityManager;
103 import android.app.ActivityManager.RestrictionLevel;
104 import android.app.ActivityManager.RestrictionReason;
105 import android.app.ActivityManager.RestrictionSource;
106 import android.app.ActivityManagerInternal;
107 import android.app.ActivityManagerInternal.AppBackgroundRestrictionListener;
108 import android.app.AppOpsManager;
109 import android.app.IActivityManager;
110 import android.app.IUidObserver;
111 import android.app.Notification;
112 import android.app.NotificationManager;
113 import android.app.PendingIntent;
114 import android.app.UidObserver;
115 import android.app.role.OnRoleHoldersChangedListener;
116 import android.app.role.RoleManager;
117 import android.app.usage.AppStandbyInfo;
118 import android.app.usage.UsageStatsManager;
119 import android.content.BroadcastReceiver;
120 import android.content.Context;
121 import android.content.Intent;
122 import android.content.IntentFilter;
123 import android.content.pm.ApplicationInfo;
124 import android.content.pm.ModuleInfo;
125 import android.content.pm.PackageInfo;
126 import android.content.pm.PackageManager;
127 import android.content.pm.PackageManagerInternal;
128 import android.content.pm.ServiceInfo;
129 import android.content.pm.ServiceInfo.ForegroundServiceType;
130 import android.graphics.drawable.Icon;
131 import android.net.Uri;
132 import android.os.AppBackgroundRestrictionsInfo;
133 import android.os.Build;
134 import android.os.Environment;
135 import android.os.Handler;
136 import android.os.HandlerExecutor;
137 import android.os.HandlerThread;
138 import android.os.Looper;
139 import android.os.Message;
140 import android.os.PowerExemptionManager.ReasonCode;
141 import android.os.RemoteException;
142 import android.os.SystemClock;
143 import android.os.UserHandle;
144 import android.os.UserManager;
145 import android.provider.DeviceConfig;
146 import android.provider.DeviceConfig.OnPropertiesChangedListener;
147 import android.provider.DeviceConfig.Properties;
148 import android.provider.Settings;
149 import android.telephony.TelephonyManager;
150 import android.telephony.TelephonyManager.CarrierPrivilegesCallback;
151 import android.text.TextUtils;
152 import android.util.ArraySet;
153 import android.util.AtomicFile;
154 import android.util.Pair;
155 import android.util.Slog;
156 import android.util.SparseArray;
157 import android.util.SparseArrayMap;
158 import android.util.TimeUtils;
159 import android.util.Xml;
160 import android.util.proto.ProtoOutputStream;
161 
162 import com.android.internal.annotations.GuardedBy;
163 import com.android.internal.annotations.VisibleForTesting;
164 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
165 import com.android.internal.util.ArrayUtils;
166 import com.android.internal.util.FrameworkStatsLog;
167 import com.android.internal.util.function.TriConsumer;
168 import com.android.modules.utils.TypedXmlPullParser;
169 import com.android.modules.utils.TypedXmlSerializer;
170 import com.android.server.AppStateTracker;
171 import com.android.server.LocalServices;
172 import com.android.server.SystemConfig;
173 import com.android.server.am.AppBatteryTracker.ImmutableBatteryUsage;
174 import com.android.server.apphibernation.AppHibernationManagerInternal;
175 import com.android.server.pm.UserManagerInternal;
176 import com.android.server.usage.AppStandbyInternal;
177 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
178 
179 import org.xmlpull.v1.XmlPullParser;
180 import org.xmlpull.v1.XmlPullParserException;
181 
182 import java.io.ByteArrayOutputStream;
183 import java.io.File;
184 import java.io.FileInputStream;
185 import java.io.FileOutputStream;
186 import java.io.IOException;
187 import java.io.InputStream;
188 import java.io.PrintWriter;
189 import java.lang.annotation.Retention;
190 import java.lang.annotation.RetentionPolicy;
191 import java.util.ArrayList;
192 import java.util.Arrays;
193 import java.util.Collections;
194 import java.util.Comparator;
195 import java.util.HashMap;
196 import java.util.List;
197 import java.util.Set;
198 import java.util.concurrent.CopyOnWriteArraySet;
199 import java.util.concurrent.atomic.AtomicBoolean;
200 import java.util.function.Consumer;
201 
202 /**
203  * This class tracks various state of the apps and mutates their restriction levels accordingly.
204  */
205 public final class AppRestrictionController {
206     static final String TAG = TAG_WITH_CLASS_NAME ? "AppRestrictionController" : TAG_AM;
207     static final boolean DEBUG_BG_RESTRICTION_CONTROLLER = false;
208 
209     /**
210      * The prefix for the sub-namespace of our device configs under
211      * the {@link android.provider.DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
212      */
213     static final String DEVICE_CONFIG_SUBNAMESPACE_PREFIX = "bg_";
214 
215     static final int STOCK_PM_FLAGS = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
216             | MATCH_DISABLED_UNTIL_USED_COMPONENTS;
217 
218     /**
219      * Whether or not to show the foreground service manager on tapping notifications.
220      */
221     private static final boolean ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER = true;
222 
223     /**
224      * Whether or not to show the action to the foreground service manager when
225      * posting the notification for background restriction.
226      */
227     private static final boolean ENABLE_SHOW_FGS_MANAGER_ACTION_ON_BG_RESTRICTION = false;
228 
229     private static final String APP_RESTRICTION_SETTINGS_DIRNAME = "apprestriction";
230     private static final String APP_RESTRICTION_SETTINGS_FILENAME = "settings.xml";
231 
232     private static final String TAG_SETTINGS = "settings";
233     private static final String ATTR_PACKAGE = "package";
234     private static final String ATTR_UID = "uid";
235     private static final String ATTR_CUR_LEVEL = "curlevel";
236     private static final String ATTR_LEVEL_TS = "levelts";
237     private static final String ATTR_REASON = "reason";
238 
239     private static final String[] ROLES_IN_INTEREST = {
240         RoleManager.ROLE_DIALER,
241         RoleManager.ROLE_EMERGENCY,
242     };
243 
244     private final Context mContext;
245     private final HandlerThread mBgHandlerThread;
246     private final BgHandler mBgHandler;
247     private final HandlerExecutor mBgExecutor;
248     private final HandlerExecutor mExecutor;
249 
250     // No lock is needed, as it's immutable after initialization in constructor.
251     private final ArrayList<BaseAppStateTracker> mAppStateTrackers = new ArrayList<>();
252 
253     @VisibleForTesting
254     @GuardedBy("mSettingsLock")
255     final RestrictionSettings mRestrictionSettings = new RestrictionSettings();
256 
257     private final CopyOnWriteArraySet<AppBackgroundRestrictionListener> mRestrictionListeners =
258             new CopyOnWriteArraySet<>();
259 
260     /**
261      * A mapping between the UID/Pkg and its pending work which should be triggered on inactive;
262      * an active UID/pkg pair should have an entry here, although its pending work could be null.
263      */
264     @GuardedBy("mSettingsLock")
265     private final SparseArrayMap<String, Runnable> mActiveUids = new SparseArrayMap<>();
266 
267     // No lock is needed as it's accessed in bg handler thread only.
268     private final ArrayList<Runnable> mTmpRunnables = new ArrayList<>();
269 
270     /**
271      * Power-save allowlisted app-ids (not including except-idle-allowlisted ones).
272      */
273     private int[] mDeviceIdleAllowlist = new int[0]; // No lock is needed.
274 
275     /**
276      * Power-save allowlisted app-ids (including except-idle-allowlisted ones).
277      */
278     private int[] mDeviceIdleExceptIdleAllowlist = new int[0]; // No lock is needed.
279 
280     /**
281      * The pre-configured system app-ids in the power-save allow list.
282      *
283      * @see #mDeviceIdleAllowlist.
284      */
285     private final ArraySet<Integer> mSystemDeviceIdleAllowlist = new ArraySet<>();
286 
287     /**
288      * The pre-configured system app-ids in the power-save allow list, except-idle.
289      *
290      * @see #mDeviceIdleExceptIdleAllowlist.
291      */
292     private final ArraySet<Integer> mSystemDeviceIdleExceptIdleAllowlist = new ArraySet<>();
293 
294     private final Object mLock = new Object();
295     private final Object mSettingsLock = new Object();
296     private final Injector mInjector;
297     private final NotificationHelper mNotificationHelper;
298 
299     private final OnRoleHoldersChangedListener mRoleHolderChangedListener =
300             this::onRoleHoldersChanged;
301 
302     /**
303      * The key is the UID, the value is the list of the roles it holds.
304      */
305     @GuardedBy("mLock")
306     private final SparseArray<ArrayList<String>> mUidRolesMapping = new SparseArray<>();
307 
308     /**
309      * Cache the package name and information about if it's a system module.
310      */
311     @GuardedBy("mLock")
312     private final HashMap<String, Boolean> mSystemModulesCache = new HashMap<>();
313 
314     /**
315      * The pre-config packages that are exempted from the background restrictions.
316      */
317     ArraySet<String> mBgRestrictionExemptioFromSysConfig;
318 
319     /**
320      * Lock specifically for bookkeeping around the carrier-privileged app set.
321      * Do not acquire any other locks while holding this one. Methods that
322      * require this lock to be held are named with a "CPL" suffix.
323      */
324     private final Object mCarrierPrivilegedLock = new Object();
325 
326     /**
327      * List of carrier-privileged apps that should be excluded from standby,
328      * the key of this array here is the phone id.
329      */
330     @GuardedBy("mCarrierPrivilegedLock")
331     private final SparseArray<Set<String>> mCarrierPrivilegedApps = new SparseArray<>();
332 
333     /**
334      * Holding the callbacks to the carrier privileged app changes.
335      *
336      * it's lock free.
337      */
338     private volatile ArrayList<PhoneCarrierPrivilegesCallback> mCarrierPrivilegesCallbacks;
339 
340     /**
341      * Whether or not we've loaded the restriction settings from the persistent storage.
342      */
343     private final AtomicBoolean mRestrictionSettingsXmlLoaded = new AtomicBoolean();
344 
345     final ActivityManagerService mActivityManagerService;
346 
347     private volatile boolean mLockedBootCompleted = false;
348 
349     static final int TRACKER_TYPE_UNKNOWN = 0;
350     static final int TRACKER_TYPE_BATTERY = 1;
351     static final int TRACKER_TYPE_BATTERY_EXEMPTION = 2;
352     static final int TRACKER_TYPE_FGS = 3;
353     static final int TRACKER_TYPE_MEDIA_SESSION = 4;
354     static final int TRACKER_TYPE_PERMISSION = 5;
355     static final int TRACKER_TYPE_BROADCAST_EVENTS = 6;
356     static final int TRACKER_TYPE_BIND_SERVICE_EVENTS = 7;
357 
358     @IntDef(prefix = { "TRACKER_TYPE_" }, value = {
359             TRACKER_TYPE_UNKNOWN,
360             TRACKER_TYPE_BATTERY,
361             TRACKER_TYPE_BATTERY_EXEMPTION,
362             TRACKER_TYPE_FGS,
363             TRACKER_TYPE_MEDIA_SESSION,
364             TRACKER_TYPE_PERMISSION,
365             TRACKER_TYPE_BROADCAST_EVENTS,
366             TRACKER_TYPE_BIND_SERVICE_EVENTS,
367     })
368     @interface TrackerType {}
369 
370     private final TrackerInfo mEmptyTrackerInfo = new TrackerInfo();
371 
372     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
373         @Override
374         public void onReceive(Context context, Intent intent) {
375             final String action = intent.getAction();
376             switch (intent.getAction()) {
377                 case Intent.ACTION_PACKAGE_ADDED: {
378                     if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
379                         final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
380                         if (uid >= 0) {
381                             onUidAdded(uid);
382                         }
383                     }
384                 } break;
385                 case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
386                     final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
387                     final Uri data = intent.getData();
388                     String ssp;
389                     if (uid >= 0 && data != null
390                             && (ssp = data.getSchemeSpecificPart()) != null) {
391                         onPackageRemoved(ssp, uid);
392                     }
393                 } break;
394                 case Intent.ACTION_UID_REMOVED: {
395                     if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
396                         final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
397                         if (uid >= 0) {
398                             onUidRemoved(uid);
399                         }
400                     }
401                 } break;
402                 case Intent.ACTION_USER_ADDED: {
403                     final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
404                     if (userId >= 0) {
405                         onUserAdded(userId);
406                     }
407                 } break;
408                 case Intent.ACTION_USER_STARTED: {
409                     final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
410                     if (userId >= 0) {
411                         onUserStarted(userId);
412                     }
413                 } break;
414                 case Intent.ACTION_USER_STOPPED: {
415                     final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
416                     if (userId >= 0) {
417                         onUserStopped(userId);
418                     }
419                 } break;
420                 case Intent.ACTION_USER_REMOVED: {
421                     final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
422                     if (userId >= 0) {
423                         onUserRemoved(userId);
424                     }
425                 } break;
426                 case TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED: {
427                     unregisterCarrierPrivilegesCallbacks();
428                     registerCarrierPrivilegesCallbacks();
429                 } break;
430             }
431         }
432     };
433 
434     private final BroadcastReceiver mBootReceiver = new BroadcastReceiver() {
435         @Override
436         public void onReceive(Context context, Intent intent) {
437             final String action = intent.getAction();
438             switch (intent.getAction()) {
439                 case Intent.ACTION_LOCKED_BOOT_COMPLETED: {
440                     onLockedBootCompleted();
441                 } break;
442             }
443         }
444     };
445 
446     /**
447      * The restriction levels that each package is on, the levels here are defined in
448      * {@link android.app.ActivityManager.RESTRICTION_LEVEL_*}.
449      */
450     final class RestrictionSettings {
451         @GuardedBy("mSettingsLock")
452         final SparseArrayMap<String, PkgSettings> mRestrictionLevels = new SparseArrayMap();
453 
454         final class PkgSettings {
455             private final String mPackageName;
456             private final int mUid;
457 
458             private @RestrictionLevel int mCurrentRestrictionLevel;
459             private @RestrictionLevel int mLastRestrictionLevel;
460             private @CurrentTimeMillisLong long mLevelChangeTime;
461             private int mReason;
462 
463             private @CurrentTimeMillisLong long[] mLastNotificationShownTime;
464             private int[] mNotificationId;
465 
PkgSettings(String packageName, int uid)466             PkgSettings(String packageName, int uid) {
467                 mPackageName = packageName;
468                 mUid = uid;
469                 mCurrentRestrictionLevel = mLastRestrictionLevel = RESTRICTION_LEVEL_UNKNOWN;
470             }
471 
472             @GuardedBy("mSettingsLock")
update(@estrictionLevel int level, int reason, int subReason)473             @RestrictionLevel int update(@RestrictionLevel int level, int reason, int subReason) {
474                 if (level != mCurrentRestrictionLevel) {
475                     mLastRestrictionLevel = mCurrentRestrictionLevel;
476                     mCurrentRestrictionLevel = level;
477                     mLevelChangeTime = mInjector.currentTimeMillis();
478                     mReason = (REASON_MAIN_MASK & reason) | (REASON_SUB_MASK & subReason);
479                     mBgHandler.obtainMessage(BgHandler.MSG_APP_RESTRICTION_LEVEL_CHANGED,
480                             mUid, level, mPackageName).sendToTarget();
481                 }
482                 return mLastRestrictionLevel;
483             }
484 
485             @Override
486             @GuardedBy("mSettingsLock")
toString()487             public String toString() {
488                 final StringBuilder sb = new StringBuilder(128);
489                 sb.append("RestrictionLevel{");
490                 sb.append(Integer.toHexString(System.identityHashCode(this)));
491                 sb.append(':');
492                 sb.append(mPackageName);
493                 sb.append('/');
494                 sb.append(UserHandle.formatUid(mUid));
495                 sb.append('}');
496                 sb.append(' ');
497                 sb.append(ActivityManager.restrictionLevelToName(mCurrentRestrictionLevel));
498                 sb.append('(');
499                 sb.append(reasonToString(mReason));
500                 sb.append(')');
501                 return sb.toString();
502             }
503 
dump(PrintWriter pw, @CurrentTimeMillisLong long now)504             void dump(PrintWriter pw, @CurrentTimeMillisLong long now) {
505                 synchronized (mSettingsLock) {
506                     pw.print(toString());
507                     if (mLastRestrictionLevel != RESTRICTION_LEVEL_UNKNOWN) {
508                         pw.print('/');
509                         pw.print(ActivityManager.restrictionLevelToName(mLastRestrictionLevel));
510                     }
511                     pw.print(" levelChange=");
512                     TimeUtils.formatDuration(mLevelChangeTime - now, pw);
513                     if (mLastNotificationShownTime != null) {
514                         for (int i = 0; i < mLastNotificationShownTime.length; i++) {
515                             if (mLastNotificationShownTime[i] > 0) {
516                                 pw.print(" lastNoti(");
517                                 pw.print(mNotificationHelper.notificationTypeToString(i));
518                                 pw.print(")=");
519                                 TimeUtils.formatDuration(mLastNotificationShownTime[i] - now, pw);
520                             }
521                         }
522                     }
523                 }
524                 pw.print(" effectiveExemption=");
525                 pw.print(reasonCodeToString(getBackgroundRestrictionExemptionReason(mUid)));
526             }
527 
getPackageName()528             String getPackageName() {
529                 return mPackageName;
530             }
531 
getUid()532             int getUid() {
533                 return mUid;
534             }
535 
536             @GuardedBy("mSettingsLock")
getCurrentRestrictionLevel()537             @RestrictionLevel int getCurrentRestrictionLevel() {
538                 return mCurrentRestrictionLevel;
539             }
540 
541             @GuardedBy("mSettingsLock")
getLastRestrictionLevel()542             @RestrictionLevel int getLastRestrictionLevel() {
543                 return mLastRestrictionLevel;
544             }
545 
546             @GuardedBy("mSettingsLock")
getReason()547             int getReason() {
548                 return mReason;
549             }
550 
551             @GuardedBy("mSettingsLock")
getLastNotificationTime( @otificationHelper.NotificationType int notificationType)552             @CurrentTimeMillisLong long getLastNotificationTime(
553                     @NotificationHelper.NotificationType int notificationType) {
554                 if (mLastNotificationShownTime == null) {
555                     return 0;
556                 }
557                 return mLastNotificationShownTime[notificationType];
558             }
559 
560             @GuardedBy("mSettingsLock")
setLastNotificationTime(@otificationHelper.NotificationType int notificationType, @CurrentTimeMillisLong long timestamp)561             void setLastNotificationTime(@NotificationHelper.NotificationType int notificationType,
562                     @CurrentTimeMillisLong long timestamp) {
563                 setLastNotificationTime(notificationType, timestamp, true);
564             }
565 
566             @VisibleForTesting
567             @GuardedBy("mSettingsLock")
setLastNotificationTime(@otificationHelper.NotificationType int notificationType, @CurrentTimeMillisLong long timestamp, boolean persist)568             void setLastNotificationTime(@NotificationHelper.NotificationType int notificationType,
569                     @CurrentTimeMillisLong long timestamp, boolean persist) {
570                 if (mLastNotificationShownTime == null) {
571                     mLastNotificationShownTime =
572                             new long[NotificationHelper.NOTIFICATION_TYPE_LAST];
573                 }
574                 mLastNotificationShownTime[notificationType] = timestamp;
575                 if (persist && mRestrictionSettingsXmlLoaded.get()) {
576                     schedulePersistToXml(UserHandle.getUserId(mUid));
577                 }
578             }
579 
580             @GuardedBy("mSettingsLock")
getNotificationId(@otificationHelper.NotificationType int notificationType)581             int getNotificationId(@NotificationHelper.NotificationType int notificationType) {
582                 if (mNotificationId == null) {
583                     return 0;
584                 }
585                 return mNotificationId[notificationType];
586             }
587 
588             @GuardedBy("mSettingsLock")
setNotificationId(@otificationHelper.NotificationType int notificationType, int notificationId)589             void setNotificationId(@NotificationHelper.NotificationType int notificationType,
590                     int notificationId) {
591                 if (mNotificationId == null) {
592                     mNotificationId = new int[NotificationHelper.NOTIFICATION_TYPE_LAST];
593                 }
594                 mNotificationId[notificationType] = notificationId;
595             }
596 
597             @VisibleForTesting
598             @GuardedBy("mSettingsLock")
setLevelChangeTime(@urrentTimeMillisLong long timestamp)599             void setLevelChangeTime(@CurrentTimeMillisLong long timestamp) {
600                 mLevelChangeTime = timestamp;
601             }
602 
603             @GuardedBy("mSettingsLock")
604             @Override
clone()605             public Object clone() {
606                 final PkgSettings newObj = new PkgSettings(mPackageName, mUid);
607                 newObj.mCurrentRestrictionLevel = mCurrentRestrictionLevel;
608                 newObj.mLastRestrictionLevel = mLastRestrictionLevel;
609                 newObj.mLevelChangeTime = mLevelChangeTime;
610                 newObj.mReason = mReason;
611                 if (mLastNotificationShownTime != null) {
612                     newObj.mLastNotificationShownTime = Arrays.copyOf(
613                             mLastNotificationShownTime, mLastNotificationShownTime.length);
614                 }
615                 if (mNotificationId != null) {
616                     newObj.mNotificationId = Arrays.copyOf(mNotificationId, mNotificationId.length);
617                 }
618                 return newObj;
619             }
620 
621             @GuardedBy("mSettingsLock")
622             @Override
equals(Object other)623             public boolean equals(Object other) {
624                 if (other == this) {
625                     return true;
626                 }
627                 if (other == null || !(other instanceof PkgSettings)) {
628                     return false;
629                 }
630                 final PkgSettings otherSettings = (PkgSettings) other;
631                 return otherSettings.mUid == mUid
632                         && otherSettings.mCurrentRestrictionLevel == mCurrentRestrictionLevel
633                         && otherSettings.mLastRestrictionLevel == mLastRestrictionLevel
634                         && otherSettings.mLevelChangeTime == mLevelChangeTime
635                         && otherSettings.mReason == mReason
636                         && TextUtils.equals(otherSettings.mPackageName, mPackageName)
637                         && Arrays.equals(otherSettings.mLastNotificationShownTime,
638                                 mLastNotificationShownTime)
639                         && Arrays.equals(otherSettings.mNotificationId, mNotificationId);
640             }
641         }
642 
643         /**
644          * Update the restriction level.
645          *
646          * @return The previous restriction level.
647          */
update(String packageName, int uid, @RestrictionLevel int level, int reason, int subReason)648         @RestrictionLevel int update(String packageName, int uid, @RestrictionLevel int level,
649                 int reason, int subReason) {
650             synchronized (mSettingsLock) {
651                 PkgSettings settings = getRestrictionSettingsLocked(uid, packageName);
652                 if (settings == null) {
653                     settings = new PkgSettings(packageName, uid);
654                     mRestrictionLevels.add(uid, packageName, settings);
655                 }
656                 return settings.update(level, reason, subReason);
657             }
658         }
659 
660         /**
661          * @return The reason of why it's in this level.
662          */
getReason(String packageName, int uid)663         int getReason(String packageName, int uid) {
664             synchronized (mSettingsLock) {
665                 final PkgSettings settings = mRestrictionLevels.get(uid, packageName);
666                 return settings != null ? settings.getReason()
667                         : (REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_UNDEFINED);
668             }
669         }
670 
getRestrictionLevel(int uid)671         @RestrictionLevel int getRestrictionLevel(int uid) {
672             synchronized (mSettingsLock) {
673                 final int uidKeyIndex = mRestrictionLevels.indexOfKey(uid);
674                 if (uidKeyIndex < 0) {
675                     return RESTRICTION_LEVEL_UNKNOWN;
676                 }
677                 final int numPackages = mRestrictionLevels.numElementsForKeyAt(uidKeyIndex);
678                 if (numPackages == 0) {
679                     return RESTRICTION_LEVEL_UNKNOWN;
680                 }
681                 @RestrictionLevel int level = RESTRICTION_LEVEL_UNKNOWN;
682                 for (int i = 0; i < numPackages; i++) {
683                     final PkgSettings setting = mRestrictionLevels.valueAt(uidKeyIndex, i);
684                     if (setting != null) {
685                         final @RestrictionLevel int l = setting.getCurrentRestrictionLevel();
686                         level = (level == RESTRICTION_LEVEL_UNKNOWN) ? l : Math.min(level, l);
687                     }
688                 }
689                 return level;
690             }
691         }
692 
getRestrictionLevel(int uid, String packageName)693         @RestrictionLevel int getRestrictionLevel(int uid, String packageName) {
694             synchronized (mSettingsLock) {
695                 final PkgSettings settings = getRestrictionSettingsLocked(uid, packageName);
696                 return settings == null
697                         ? getRestrictionLevel(uid) : settings.getCurrentRestrictionLevel();
698             }
699         }
700 
getRestrictionLevel(String packageName, @UserIdInt int userId)701         @RestrictionLevel int getRestrictionLevel(String packageName, @UserIdInt int userId) {
702             final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
703             final int uid = pm.getPackageUid(packageName, STOCK_PM_FLAGS, userId);
704             return getRestrictionLevel(uid, packageName);
705         }
706 
getLastRestrictionLevel(int uid, String packageName)707         private @RestrictionLevel int getLastRestrictionLevel(int uid, String packageName) {
708             synchronized (mSettingsLock) {
709                 final PkgSettings settings = mRestrictionLevels.get(uid, packageName);
710                 return settings == null
711                         ? RESTRICTION_LEVEL_UNKNOWN : settings.getLastRestrictionLevel();
712             }
713         }
714 
715         @GuardedBy("mSettingsLock")
forEachPackageInUidLocked(int uid, @NonNull TriConsumer<String, Integer, Integer> consumer)716         void forEachPackageInUidLocked(int uid,
717                 @NonNull TriConsumer<String, Integer, Integer> consumer) {
718             final int uidKeyIndex = mRestrictionLevels.indexOfKey(uid);
719             if (uidKeyIndex < 0) {
720                 return;
721             }
722             final int numPackages = mRestrictionLevels.numElementsForKeyAt(uidKeyIndex);
723             for (int i = 0; i < numPackages; i++) {
724                 final PkgSettings settings = mRestrictionLevels.valueAt(uidKeyIndex, i);
725                 consumer.accept(mRestrictionLevels.keyAt(uidKeyIndex, i),
726                         settings.getCurrentRestrictionLevel(), settings.getReason());
727             }
728         }
729 
730         @GuardedBy("mSettingsLock")
forEachUidLocked(@onNull Consumer<Integer> consumer)731         void forEachUidLocked(@NonNull Consumer<Integer> consumer) {
732             for (int i = mRestrictionLevels.numMaps() - 1; i >= 0; i--) {
733                 consumer.accept(mRestrictionLevels.keyAt(i));
734             }
735         }
736 
737         @GuardedBy("mSettingsLock")
getRestrictionSettingsLocked(int uid, String packageName)738         PkgSettings getRestrictionSettingsLocked(int uid, String packageName) {
739             return mRestrictionLevels.get(uid, packageName);
740         }
741 
removeUser(@serIdInt int userId)742         void removeUser(@UserIdInt int userId) {
743             synchronized (mSettingsLock) {
744                 for (int i = mRestrictionLevels.numMaps() - 1; i >= 0; i--) {
745                     final int uid = mRestrictionLevels.keyAt(i);
746                     if (UserHandle.getUserId(uid) != userId) {
747                         continue;
748                     }
749                     mRestrictionLevels.deleteAt(i);
750                 }
751             }
752         }
753 
removePackage(String pkgName, int uid)754         void removePackage(String pkgName, int uid) {
755             removePackage(pkgName, uid, true);
756         }
757 
removePackage(String pkgName, int uid, boolean persist)758         void removePackage(String pkgName, int uid, boolean persist) {
759             synchronized (mSettingsLock) {
760                 final int keyIndex = mRestrictionLevels.indexOfKey(uid);
761                 mRestrictionLevels.delete(uid, pkgName);
762                 if (keyIndex >= 0 && mRestrictionLevels.numElementsForKeyAt(keyIndex) == 0) {
763                     mRestrictionLevels.deleteAt(keyIndex);
764                 }
765             }
766             if (persist && mRestrictionSettingsXmlLoaded.get()) {
767                 schedulePersistToXml(UserHandle.getUserId(uid));
768             }
769         }
770 
removeUid(int uid)771         void removeUid(int uid) {
772             removeUid(uid, true);
773         }
774 
removeUid(int uid, boolean persist)775         void removeUid(int uid, boolean persist) {
776             synchronized (mSettingsLock) {
777                 mRestrictionLevels.delete(uid);
778             }
779             if (persist && mRestrictionSettingsXmlLoaded.get()) {
780                 schedulePersistToXml(UserHandle.getUserId(uid));
781             }
782         }
783 
784         @VisibleForTesting
reset()785         void reset() {
786             synchronized (mSettingsLock) {
787                 for (int i = mRestrictionLevels.numMaps() - 1; i >= 0; i--) {
788                     mRestrictionLevels.deleteAt(i);
789                 }
790             }
791         }
792 
793         @VisibleForTesting
resetToDefault()794         void resetToDefault() {
795             synchronized (mSettingsLock) {
796                 mRestrictionLevels.forEach(settings -> {
797                     settings.mCurrentRestrictionLevel = RESTRICTION_LEVEL_UNKNOWN;
798                     settings.mLastRestrictionLevel = RESTRICTION_LEVEL_UNKNOWN;
799                     settings.mLevelChangeTime = 0L;
800                     settings.mReason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_UNDEFINED;
801                     if (settings.mLastNotificationShownTime != null) {
802                         for (int i = 0; i < settings.mLastNotificationShownTime.length; i++) {
803                             settings.mLastNotificationShownTime[i] = 0L;
804                         }
805                     }
806                 });
807             }
808         }
809 
dump(PrintWriter pw, String prefix)810         void dump(PrintWriter pw, String prefix) {
811             final ArrayList<PkgSettings> settings = new ArrayList<>();
812             synchronized (mSettingsLock) {
813                 mRestrictionLevels.forEach(setting -> settings.add(setting));
814             }
815             Collections.sort(settings, Comparator.comparingInt(PkgSettings::getUid));
816             final long now = mInjector.currentTimeMillis();
817             for (int i = 0, size = settings.size(); i < size; i++) {
818                 pw.print(prefix);
819                 pw.print('#');
820                 pw.print(i);
821                 pw.print(' ');
822                 settings.get(i).dump(pw, now);
823                 pw.println();
824             }
825         }
826 
827         @VisibleForTesting
schedulePersistToXml(@serIdInt int userId)828         void schedulePersistToXml(@UserIdInt int userId) {
829             mBgHandler.obtainMessage(BgHandler.MSG_PERSIST_RESTRICTION_SETTINGS, userId, 0)
830                     .sendToTarget();
831         }
832 
833         @VisibleForTesting
scheduleLoadFromXml()834         void scheduleLoadFromXml() {
835             mBgHandler.sendEmptyMessage(BgHandler.MSG_LOAD_RESTRICTION_SETTINGS);
836         }
837 
838         @VisibleForTesting
getXmlFileNameForUser(@serIdInt int userId)839         File getXmlFileNameForUser(@UserIdInt int userId) {
840             final File dir = new File(mInjector.getDataSystemDeDirectory(
841                     userId), APP_RESTRICTION_SETTINGS_DIRNAME);
842             return new File(dir, APP_RESTRICTION_SETTINGS_FILENAME);
843         }
844 
845         @VisibleForTesting
loadFromXml(boolean applyLevel)846         void loadFromXml(boolean applyLevel) {
847             final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
848             for (int userId : allUsers) {
849                 loadFromXml(userId, applyLevel);
850             }
851             mRestrictionSettingsXmlLoaded.set(true);
852         }
853 
loadFromXml(@serIdInt int userId, boolean applyLevel)854         void loadFromXml(@UserIdInt int userId, boolean applyLevel) {
855             final File file = getXmlFileNameForUser(userId);
856             if (!file.exists()) {
857                 return;
858             }
859             final long[] ts = new long[NotificationHelper.NOTIFICATION_TYPE_LAST];
860             try (InputStream in = new FileInputStream(file)) {
861                 final TypedXmlPullParser parser = Xml.resolvePullParser(in);
862                 final long now = SystemClock.elapsedRealtime();
863                 int type;
864                 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
865                     if (type != XmlPullParser.START_TAG) {
866                         continue;
867                     }
868                     final String tagName = parser.getName();
869                     if (!TAG_SETTINGS.equals(tagName)) {
870                         Slog.w(TAG, "Unexpected tag name: " + tagName);
871                         continue;
872                     }
873                     loadOneFromXml(parser, now, ts, applyLevel);
874                 }
875                 if (DEBUG_BG_RESTRICTION_CONTROLLER) {
876                     Slog.i(TAG, "Loaded from " + file);
877                 }
878             } catch (IOException | XmlPullParserException e) {
879             }
880         }
881 
loadOneFromXml(TypedXmlPullParser parser, long now, long[] ts, boolean applyLevel)882         private void loadOneFromXml(TypedXmlPullParser parser, long now, long[] ts,
883                 boolean applyLevel) {
884             // Reset the buffer.
885             for (int i = 0; i < ts.length; i++) {
886                 ts[i] = 0L;
887             }
888             // Walk through the attributes.
889             int uid = 0;
890             String packageName = null;
891             int curLevel = RESTRICTION_LEVEL_UNKNOWN;
892             int reason = REASON_MAIN_DEFAULT;
893             long levelTs = 0L;
894             for (int i = 0; i < parser.getAttributeCount(); i++) {
895                 try {
896                     final String attrName = parser.getAttributeName(i);
897                     final String attrValue = parser.getAttributeValue(i);
898                     switch (attrName) {
899                         case ATTR_UID:
900                             uid = Integer.parseInt(attrValue);
901                             break;
902                         case ATTR_PACKAGE:
903                             packageName = attrValue;
904                             break;
905                         case ATTR_CUR_LEVEL:
906                             curLevel = Integer.parseInt(attrValue);
907                             break;
908                         case ATTR_LEVEL_TS:
909                             levelTs = Long.parseLong(attrValue);
910                             break;
911                         case ATTR_REASON:
912                             reason = Integer.parseInt(attrValue);
913                             break;
914                         default:
915                             @NotificationHelper.NotificationType int type =
916                                     NotificationHelper.notificationTimeAttrToType(attrName);
917                             ts[type] = Long.parseLong(attrValue);
918                             break;
919                     }
920                 } catch (IllegalArgumentException e) {
921                 }
922             }
923             if (uid != 0) {
924                 if (DEBUG_BG_RESTRICTION_CONTROLLER) {
925                     Slog.i(TAG, "Restoring " + packageName + "/" + UserHandle.formatUid(uid)
926                             + " level=" + curLevel + " reason=" + Integer.toHexString(reason)
927                             + " ts=" + levelTs + " noti=" + Arrays.toString(ts));
928                 }
929                 final PkgSettings pkgSettings;
930                 synchronized (mSettingsLock) {
931                     pkgSettings = getRestrictionSettingsLocked(uid, packageName);
932                     if (pkgSettings == null) {
933                         return;
934                     }
935                     for (int i = 0; i < ts.length; i++) {
936                         if (pkgSettings.getLastNotificationTime(i) == 0 && ts[i] != 0) {
937                             pkgSettings.setLastNotificationTime(i, ts[i], false);
938                         }
939                     }
940                     if (pkgSettings.mCurrentRestrictionLevel >= curLevel) {
941                         // The current restriction level is the same or more restrictive,
942                         // don't restore.
943                         return;
944                     }
945                 }
946                 final int curBucket = mInjector.getAppStandbyInternal().getAppStandbyBucket(
947                         packageName, UserHandle.getUserId(uid), now, false);
948                 if (applyLevel) {
949                     applyRestrictionLevel(packageName, uid, curLevel, mEmptyTrackerInfo,
950                             curBucket, true, reason & REASON_MAIN_MASK, reason & REASON_SUB_MASK);
951                 } else {
952                     pkgSettings.update(curLevel,
953                             reason & REASON_MAIN_MASK, reason & REASON_SUB_MASK);
954                 }
955                 synchronized (mSettingsLock) {
956                     // Restore the mLevelChangeTime too.
957                     pkgSettings.setLevelChangeTime(levelTs);
958                 }
959             }
960         }
961 
962         @VisibleForTesting
persistToXml(@serIdInt int userId)963         void persistToXml(@UserIdInt int userId) {
964             final File file = getXmlFileNameForUser(userId);
965             final File dir = file.getParentFile();
966             if (!dir.isDirectory() && !dir.mkdirs()) {
967                 Slog.w(TAG, "Failed to create folder for " + userId);
968                 return;
969             }
970             final AtomicFile atomicFile = new AtomicFile(file);
971             FileOutputStream stream = null;
972             try {
973                 stream = atomicFile.startWrite();
974                 stream.write(toXmlByteArray(userId));
975             } catch (Exception e) {
976                 Slog.e(TAG, "Failed to write file " + file, e);
977                 if (stream != null) {
978                     atomicFile.failWrite(stream);
979                 }
980                 return;
981             }
982             atomicFile.finishWrite(stream);
983             if (DEBUG_BG_RESTRICTION_CONTROLLER) {
984                 Slog.i(TAG, "Successfully written to " + atomicFile);
985             }
986         }
987 
toXmlByteArray(@serIdInt int userId)988         private byte[] toXmlByteArray(@UserIdInt int userId) {
989             try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
990                 final TypedXmlSerializer serializer = Xml.resolveSerializer(os);
991 
992                 serializer.startDocument(/* encoding */ null, /* standalone */ true);
993 
994                 synchronized (mSettingsLock) {
995                     for (int i = mRestrictionLevels.numMaps() - 1; i >= 0; i--) {
996                         for (int j = mRestrictionLevels.numElementsForKeyAt(i) - 1; j >= 0; j--) {
997                             final PkgSettings settings = mRestrictionLevels.valueAt(i, j);
998                             final int uid = settings.getUid();
999                             if (UserHandle.getUserId(uid) != userId) {
1000                                 continue;
1001                             }
1002                             serializer.startTag(null, TAG_SETTINGS);
1003                             serializer.attributeInt(null, ATTR_UID, uid);
1004                             serializer.attribute(null, ATTR_PACKAGE, settings.getPackageName());
1005                             serializer.attributeInt(null, ATTR_CUR_LEVEL,
1006                                     settings.mCurrentRestrictionLevel);
1007                             serializer.attributeLong(null, ATTR_LEVEL_TS,
1008                                     settings.mLevelChangeTime);
1009                             serializer.attributeInt(null, ATTR_REASON, settings.mReason);
1010                             for (int k = 0; k < NotificationHelper.NOTIFICATION_TYPE_LAST; k++) {
1011                                 serializer.attributeLong(null,
1012                                         NotificationHelper.notificationTypeToTimeAttr(k),
1013                                         settings.getLastNotificationTime(k));
1014                             }
1015                             serializer.endTag(null, TAG_SETTINGS);
1016                         }
1017                     }
1018                 }
1019 
1020                 serializer.endDocument();
1021                 serializer.flush();
1022 
1023                 return os.toByteArray();
1024             } catch (IOException e) {
1025                 return null;
1026             }
1027         }
1028 
1029         @VisibleForTesting
removeXml()1030         void removeXml() {
1031             final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
1032             for (int userId : allUsers) {
1033                 getXmlFileNameForUser(userId).delete();
1034             }
1035         }
1036 
1037         @Override
clone()1038         public Object clone() {
1039             final RestrictionSettings newObj = new RestrictionSettings();
1040             synchronized (mSettingsLock) {
1041                 for (int i = mRestrictionLevels.numMaps() - 1; i >= 0; i--) {
1042                     for (int j = mRestrictionLevels.numElementsForKeyAt(i) - 1; j >= 0; j--) {
1043                         final PkgSettings settings = mRestrictionLevels.valueAt(i, j);
1044                         newObj.mRestrictionLevels.add(mRestrictionLevels.keyAt(i),
1045                                 mRestrictionLevels.keyAt(i, j), (PkgSettings) settings.clone());
1046                     }
1047                 }
1048             }
1049             return newObj;
1050         }
1051 
1052         @Override
equals(Object other)1053         public boolean equals(Object other) {
1054             if (other == this) {
1055                 return true;
1056             }
1057             if (other == null || !(other instanceof RestrictionSettings)) {
1058                 return false;
1059             }
1060             final SparseArrayMap<String, PkgSettings> otherSettings = ((RestrictionSettings) other)
1061                     .mRestrictionLevels;
1062             synchronized (mSettingsLock) {
1063                 if (otherSettings.numMaps() != mRestrictionLevels.numMaps()) {
1064                     return false;
1065                 }
1066                 for (int i = mRestrictionLevels.numMaps() - 1; i >= 0; i--) {
1067                     final int uid = mRestrictionLevels.keyAt(i);
1068                     if (otherSettings.numElementsForKey(uid)
1069                             != mRestrictionLevels.numElementsForKeyAt(i)) {
1070                         return false;
1071                     }
1072                     for (int j = mRestrictionLevels.numElementsForKeyAt(i) - 1; j >= 0; j--) {
1073                         final PkgSettings settings = mRestrictionLevels.valueAt(i, j);
1074                         if (!settings.equals(otherSettings.get(uid, settings.getPackageName()))) {
1075                             return false;
1076                         }
1077                     }
1078                 }
1079             }
1080             return true;
1081         }
1082     }
1083 
1084     final class ConstantsObserver implements OnPropertiesChangedListener {
1085         /**
1086          * Whether or not to set the app to restricted standby bucket automatically
1087          * when it's background-restricted.
1088          */
1089         static final String KEY_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION =
1090                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "auto_restricted_bucket_on_bg_restricted";
1091 
1092         /**
1093          * Whether or not to move the app to restricted standby level automatically
1094          * when system detects it's abusive.
1095          */
1096         static final String KEY_BG_AUTO_RESTRICT_ABUSIVE_APPS =
1097                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "auto_restrict_abusive_apps";
1098 
1099         /**
1100          * The minimal interval in ms before posting a notification again on abusive behaviors
1101          * of a certain package.
1102          */
1103         static final String KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL =
1104                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "abusive_notification_minimal_interval";
1105 
1106         /**
1107          * The minimal interval in ms before posting a notification again on long running FGS
1108          * from a certain package.
1109          */
1110         static final String KEY_BG_LONG_FGS_NOTIFICATION_MINIMAL_INTERVAL =
1111                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "long_fgs_notification_minimal_interval";
1112 
1113         /**
1114          * The behavior for an app with a FGS and its notification is still showing, when the system
1115          * detects it's abusive and should be put into bg restricted level. {@code true} - we'll
1116          * show the prompt to user, {@code false} - we'll not show it.
1117          */
1118         static final String KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED =
1119                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_with_noti_to_bg_restricted";
1120 
1121         /**
1122          * The behavior for an app with a FGS and its notification is still showing, when the system
1123          * detects it's running for a very long time, should we prompt the user.
1124          * {@code true} - we'll show the prompt to user, {@code false} - we'll not show it.
1125          */
1126         static final String KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING =
1127                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_with_noti_on_long_running";
1128 
1129         /**
1130          * The behavior for an app with a FGS, when the system detects it's running for
1131          * a very long time, should we prompt the user.
1132          * {@code true} - we'll show the prompt to user, {@code false} - we'll not show it.
1133          */
1134         static final String KEY_BG_PROMPT_FGS_ON_LONG_RUNNING =
1135                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_on_long_running";
1136 
1137         /**
1138          * The list of packages to be exempted from all these background restrictions.
1139          */
1140         static final String KEY_BG_RESTRICTION_EXEMPTED_PACKAGES =
1141                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "restriction_exempted_packages";
1142 
1143         /**
1144          * Whether or not to show the notification for abusive apps, i.e. when the system
1145          * detects it's draining significant amount of battery in the background.
1146          * {@code true} - we'll show the prompt to user, {@code false} - we'll not show it.
1147          */
1148         static final String KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED =
1149                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_abusive_apps_to_bg_restricted";
1150 
1151         /**
1152          * Default value to {@link #mBgAutoRestrictAbusiveApps}.
1153          */
1154         static final boolean DEFAULT_BG_AUTO_RESTRICT_ABUSIVE_APPS = true;
1155 
1156         /**
1157          * Default value to {@link #mBgAutoRestrictedBucket}.
1158          */
1159         static final boolean DEFAULT_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION = false;
1160 
1161         /**
1162          * Default value to {@link #mBgAbusiveNotificationMinIntervalMs}.
1163          */
1164         static final long DEFAULT_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL_MS = 30 * ONE_DAY;
1165 
1166         /**
1167          * Default value to {@link #mBgAbusiveNotificationMinIntervalMs}.
1168          */
1169         static final long DEFAULT_BG_LONG_FGS_NOTIFICATION_MINIMAL_INTERVAL_MS = 30 * ONE_DAY;
1170 
1171         /**
1172          * Default value to {@link #mBgPromptFgsWithNotiOnLongRunning}.
1173          */
1174         static final boolean DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING = false;
1175 
1176         /**
1177          * Default value to {@link #mBgPromptFgsOnLongRunning}.
1178          */
1179         static final boolean DEFAULT_BG_PROMPT_FGS_ON_LONG_RUNNING = true;
1180 
1181         /**
1182          * Default value to {@link #mBgPromptFgsWithNotiToBgRestricted}.
1183          */
1184         final boolean mDefaultBgPromptFgsWithNotiToBgRestricted;
1185 
1186         /**
1187          * Default value to {@link #mBgPromptAbusiveAppsToBgRestricted}.
1188          */
1189         final boolean mDefaultBgPromptAbusiveAppToBgRestricted;
1190 
1191         volatile boolean mBgAutoRestrictedBucket;
1192 
1193         volatile boolean mBgAutoRestrictAbusiveApps;
1194 
1195         volatile long mBgAbusiveNotificationMinIntervalMs;
1196 
1197         volatile long mBgLongFgsNotificationMinIntervalMs;
1198 
1199         /**
1200          * @see #KEY_BG_RESTRICTION_EXEMPTED_PACKAGES.
1201          *
1202          *<p> Mutations on them would result in copy-on-write.</p>
1203          */
1204         volatile Set<String> mBgRestrictionExemptedPackages = Collections.emptySet();
1205 
1206         /**
1207          * @see #KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED.
1208          */
1209         volatile boolean mBgPromptFgsWithNotiToBgRestricted;
1210 
1211         /**
1212          * @see #KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING.
1213          */
1214         volatile boolean mBgPromptFgsWithNotiOnLongRunning;
1215 
1216         /**
1217          * @see #KEY_BG_PROMPT_FGS_ON_LONG_RUNNING.
1218          */
1219         volatile boolean mBgPromptFgsOnLongRunning;
1220 
1221         /**
1222          * @see #KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED.
1223          */
1224         volatile boolean mBgPromptAbusiveAppsToBgRestricted;
1225 
ConstantsObserver(Handler handler, Context context)1226         ConstantsObserver(Handler handler, Context context) {
1227             mDefaultBgPromptFgsWithNotiToBgRestricted = context.getResources().getBoolean(
1228                     com.android.internal.R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted);
1229             mDefaultBgPromptAbusiveAppToBgRestricted = context.getResources().getBoolean(
1230                     com.android.internal.R.bool.config_bg_prompt_abusive_apps_to_bg_restricted);
1231         }
1232 
1233         @Override
onPropertiesChanged(Properties properties)1234         public void onPropertiesChanged(Properties properties) {
1235             for (String name : properties.getKeyset()) {
1236                 if (name == null || !name.startsWith(DEVICE_CONFIG_SUBNAMESPACE_PREFIX)) {
1237                     return;
1238                 }
1239                 switch (name) {
1240                     case KEY_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION:
1241                         updateBgAutoRestrictedBucketChanged();
1242                         break;
1243                     case KEY_BG_AUTO_RESTRICT_ABUSIVE_APPS:
1244                         updateBgAutoRestrictAbusiveApps();
1245                         break;
1246                     case KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL:
1247                         updateBgAbusiveNotificationMinimalInterval();
1248                         break;
1249                     case KEY_BG_LONG_FGS_NOTIFICATION_MINIMAL_INTERVAL:
1250                         updateBgLongFgsNotificationMinimalInterval();
1251                         break;
1252                     case KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED:
1253                         updateBgPromptFgsWithNotiToBgRestricted();
1254                         break;
1255                     case KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING:
1256                         updateBgPromptFgsWithNotiOnLongRunning();
1257                         break;
1258                     case KEY_BG_PROMPT_FGS_ON_LONG_RUNNING:
1259                         updateBgPromptFgsOnLongRunning();
1260                         break;
1261                     case KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED:
1262                         updateBgPromptAbusiveAppToBgRestricted();
1263                         break;
1264                     case KEY_BG_RESTRICTION_EXEMPTED_PACKAGES:
1265                         updateBgRestrictionExemptedPackages();
1266                         break;
1267                 }
1268                 AppRestrictionController.this.onPropertiesChanged(name);
1269             }
1270         }
1271 
start()1272         public void start() {
1273             updateDeviceConfig();
1274         }
1275 
updateDeviceConfig()1276         void updateDeviceConfig() {
1277             updateBgAutoRestrictedBucketChanged();
1278             updateBgAutoRestrictAbusiveApps();
1279             updateBgAbusiveNotificationMinimalInterval();
1280             updateBgLongFgsNotificationMinimalInterval();
1281             updateBgPromptFgsWithNotiToBgRestricted();
1282             updateBgPromptFgsWithNotiOnLongRunning();
1283             updateBgPromptFgsOnLongRunning();
1284             updateBgPromptAbusiveAppToBgRestricted();
1285             updateBgRestrictionExemptedPackages();
1286         }
1287 
updateBgAutoRestrictedBucketChanged()1288         private void updateBgAutoRestrictedBucketChanged() {
1289             boolean oldValue = mBgAutoRestrictedBucket;
1290             mBgAutoRestrictedBucket = DeviceConfig.getBoolean(
1291                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1292                     KEY_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION,
1293                     DEFAULT_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION);
1294             if (oldValue != mBgAutoRestrictedBucket) {
1295                 dispatchAutoRestrictedBucketFeatureFlagChanged(mBgAutoRestrictedBucket);
1296             }
1297         }
1298 
updateBgAutoRestrictAbusiveApps()1299         private void updateBgAutoRestrictAbusiveApps() {
1300             mBgAutoRestrictAbusiveApps = DeviceConfig.getBoolean(
1301                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1302                     KEY_BG_AUTO_RESTRICT_ABUSIVE_APPS,
1303                     DEFAULT_BG_AUTO_RESTRICT_ABUSIVE_APPS);
1304         }
1305 
updateBgAbusiveNotificationMinimalInterval()1306         private void updateBgAbusiveNotificationMinimalInterval() {
1307             mBgAbusiveNotificationMinIntervalMs = DeviceConfig.getLong(
1308                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1309                     KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL,
1310                     DEFAULT_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL_MS);
1311         }
1312 
updateBgLongFgsNotificationMinimalInterval()1313         private void updateBgLongFgsNotificationMinimalInterval() {
1314             mBgLongFgsNotificationMinIntervalMs = DeviceConfig.getLong(
1315                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1316                     KEY_BG_LONG_FGS_NOTIFICATION_MINIMAL_INTERVAL,
1317                     DEFAULT_BG_LONG_FGS_NOTIFICATION_MINIMAL_INTERVAL_MS);
1318         }
1319 
updateBgPromptFgsWithNotiToBgRestricted()1320         private void updateBgPromptFgsWithNotiToBgRestricted() {
1321             mBgPromptFgsWithNotiToBgRestricted = DeviceConfig.getBoolean(
1322                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1323                     KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED,
1324                     mDefaultBgPromptFgsWithNotiToBgRestricted);
1325         }
1326 
updateBgPromptFgsWithNotiOnLongRunning()1327         private void updateBgPromptFgsWithNotiOnLongRunning() {
1328             mBgPromptFgsWithNotiOnLongRunning = DeviceConfig.getBoolean(
1329                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1330                     KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING,
1331                     DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING);
1332         }
1333 
updateBgPromptFgsOnLongRunning()1334         private void updateBgPromptFgsOnLongRunning() {
1335             mBgPromptFgsOnLongRunning = DeviceConfig.getBoolean(
1336                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1337                     KEY_BG_PROMPT_FGS_ON_LONG_RUNNING,
1338                     DEFAULT_BG_PROMPT_FGS_ON_LONG_RUNNING);
1339         }
1340 
updateBgPromptAbusiveAppToBgRestricted()1341         private void updateBgPromptAbusiveAppToBgRestricted() {
1342             mBgPromptAbusiveAppsToBgRestricted = DeviceConfig.getBoolean(
1343                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1344                     KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED,
1345                     mDefaultBgPromptAbusiveAppToBgRestricted);
1346         }
1347 
updateBgRestrictionExemptedPackages()1348         private void updateBgRestrictionExemptedPackages() {
1349             final String settings = DeviceConfig.getString(
1350                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1351                     KEY_BG_RESTRICTION_EXEMPTED_PACKAGES,
1352                     null);
1353             if (settings == null) {
1354                 mBgRestrictionExemptedPackages = Collections.emptySet();
1355                 return;
1356             }
1357             final String[] settingsList = settings.split(",");
1358             final ArraySet<String> packages = new ArraySet<>();
1359             for (String pkg : settingsList) {
1360                 packages.add(pkg);
1361             }
1362             mBgRestrictionExemptedPackages = Collections.unmodifiableSet(packages);
1363         }
1364 
dump(PrintWriter pw, String prefix)1365         void dump(PrintWriter pw, String prefix) {
1366             pw.print(prefix);
1367             pw.println("BACKGROUND RESTRICTION POLICY SETTINGS:");
1368             final String indent = "  ";
1369             prefix = indent + prefix;
1370             pw.print(prefix);
1371             pw.print(KEY_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION);
1372             pw.print('=');
1373             pw.println(mBgAutoRestrictedBucket);
1374             pw.print(prefix);
1375             pw.print(KEY_BG_AUTO_RESTRICT_ABUSIVE_APPS);
1376             pw.print('=');
1377             pw.println(mBgAutoRestrictAbusiveApps);
1378             pw.print(prefix);
1379             pw.print(KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL);
1380             pw.print('=');
1381             pw.println(mBgAbusiveNotificationMinIntervalMs);
1382             pw.print(prefix);
1383             pw.print(KEY_BG_LONG_FGS_NOTIFICATION_MINIMAL_INTERVAL);
1384             pw.print('=');
1385             pw.println(mBgLongFgsNotificationMinIntervalMs);
1386             pw.print(prefix);
1387             pw.print(KEY_BG_PROMPT_FGS_ON_LONG_RUNNING);
1388             pw.print('=');
1389             pw.println(mBgPromptFgsOnLongRunning);
1390             pw.print(prefix);
1391             pw.print(KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING);
1392             pw.print('=');
1393             pw.println(mBgPromptFgsWithNotiOnLongRunning);
1394             pw.print(prefix);
1395             pw.print(KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED);
1396             pw.print('=');
1397             pw.println(mBgPromptFgsWithNotiToBgRestricted);
1398             pw.print(prefix);
1399             pw.print(KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED);
1400             pw.print('=');
1401             pw.println(mBgPromptAbusiveAppsToBgRestricted);
1402             pw.print(prefix);
1403             pw.print(KEY_BG_RESTRICTION_EXEMPTED_PACKAGES);
1404             pw.print('=');
1405             pw.println(mBgRestrictionExemptedPackages.toString());
1406         }
1407     }
1408 
1409     /**
1410      * A helper object which holds an app state tracker's type and its relevant info used for
1411      * logging atoms to statsd.
1412      */
1413     private class TrackerInfo {
1414         final int mType; // tracker type
1415         final byte[] mInfo; // tracker info proto object for statsd
1416 
TrackerInfo()1417         TrackerInfo() {
1418             mType = TRACKER_TYPE_UNKNOWN;
1419             mInfo = null;
1420         }
1421 
TrackerInfo(int type, byte[] info)1422         TrackerInfo(int type, byte[] info) {
1423             mType = type;
1424             mInfo = info;
1425         }
1426     }
1427 
1428     private final ConstantsObserver mConstantsObserver;
1429 
1430     private final AppStateTracker.BackgroundRestrictedAppListener mBackgroundRestrictionListener =
1431             new AppStateTracker.BackgroundRestrictedAppListener() {
1432                 @Override
1433                 public void updateBackgroundRestrictedForUidPackage(int uid, String packageName,
1434                         boolean restricted) {
1435                     mBgHandler.obtainMessage(BgHandler.MSG_BACKGROUND_RESTRICTION_CHANGED,
1436                             uid, restricted ? 1 : 0, packageName).sendToTarget();
1437                 }
1438             };
1439 
1440     private final AppIdleStateChangeListener mAppIdleStateChangeListener =
1441             new AppIdleStateChangeListener() {
1442                 @Override
1443                 public void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
1444                         boolean idle, int bucket, int reason) {
1445                     mBgHandler.obtainMessage(BgHandler.MSG_APP_STANDBY_BUCKET_CHANGED,
1446                             userId, bucket, packageName).sendToTarget();
1447                 }
1448 
1449                 @Override
1450                 public void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
1451                     mBgHandler.obtainMessage(BgHandler.MSG_USER_INTERACTION_STARTED,
1452                             userId, 0, packageName).sendToTarget();
1453                 }
1454             };
1455 
1456     private final IUidObserver mUidObserver =
1457             new UidObserver() {
1458                 @Override
1459                 public void onUidStateChanged(int uid, int procState, long procStateSeq,
1460                         int capability) {
1461                     mBgHandler.obtainMessage(BgHandler.MSG_UID_PROC_STATE_CHANGED, uid, procState)
1462                             .sendToTarget();
1463                 }
1464 
1465                 @Override
1466                 public void onUidIdle(int uid, boolean disabled) {
1467                     mBgHandler.obtainMessage(BgHandler.MSG_UID_IDLE, uid, disabled ? 1 : 0)
1468                             .sendToTarget();
1469                 }
1470 
1471                 @Override
1472                 public void onUidGone(int uid, boolean disabled) {
1473                     mBgHandler.obtainMessage(BgHandler.MSG_UID_GONE, uid, disabled ? 1 : 0)
1474                             .sendToTarget();
1475                 }
1476 
1477                 @Override
1478                 public void onUidActive(int uid) {
1479                     mBgHandler.obtainMessage(BgHandler.MSG_UID_ACTIVE, uid, 0).sendToTarget();
1480                 }
1481             };
1482 
1483     /**
1484      * Register the background restriction listener callback.
1485      */
addAppBackgroundRestrictionListener( @onNull AppBackgroundRestrictionListener listener)1486     public void addAppBackgroundRestrictionListener(
1487             @NonNull AppBackgroundRestrictionListener listener) {
1488         mRestrictionListeners.add(listener);
1489     }
1490 
AppRestrictionController(final Context context, final ActivityManagerService service)1491     AppRestrictionController(final Context context, final ActivityManagerService service) {
1492         this(new Injector(context), service);
1493     }
1494 
AppRestrictionController(final Injector injector, final ActivityManagerService service)1495     AppRestrictionController(final Injector injector, final ActivityManagerService service) {
1496         mInjector = injector;
1497         mContext = injector.getContext();
1498         mActivityManagerService = service;
1499         mBgHandlerThread = new HandlerThread("bgres-controller", THREAD_PRIORITY_BACKGROUND);
1500         mBgHandlerThread.start();
1501         mBgHandler = new BgHandler(mBgHandlerThread.getLooper(), injector);
1502         mBgExecutor = new HandlerExecutor(mBgHandler);
1503         mConstantsObserver = new ConstantsObserver(mBgHandler, mContext);
1504         mNotificationHelper = new NotificationHelper(this);
1505         injector.initAppStateTrackers(this);
1506         mExecutor = new HandlerExecutor(injector.getDefaultHandler());
1507     }
1508 
onSystemReady()1509     void onSystemReady() {
1510         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
1511                 mBgExecutor, mConstantsObserver);
1512         mConstantsObserver.start();
1513         initBgRestrictionExemptioFromSysConfig();
1514         initRestrictionStates();
1515         initSystemModuleNames();
1516         initRolesInInterest();
1517         registerForUidObservers();
1518         registerForSystemBroadcasts();
1519         registerCarrierPrivilegesCallbacks();
1520         mNotificationHelper.onSystemReady();
1521         mInjector.getAppStateTracker().addBackgroundRestrictedAppListener(
1522                 mBackgroundRestrictionListener);
1523         mInjector.getAppStandbyInternal().addListener(mAppIdleStateChangeListener);
1524         mInjector.getRoleManager().addOnRoleHoldersChangedListenerAsUser(mExecutor,
1525                 mRoleHolderChangedListener, UserHandle.ALL);
1526         mInjector.scheduleInitTrackers(mBgHandler, () -> {
1527             for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
1528                 mAppStateTrackers.get(i).onSystemReady();
1529             }
1530         });
1531     }
1532 
1533     @VisibleForTesting
resetRestrictionSettings()1534     void resetRestrictionSettings() {
1535         synchronized (mSettingsLock) {
1536             mRestrictionSettings.reset();
1537         }
1538         initRestrictionStates();
1539     }
1540 
1541     @VisibleForTesting
tearDown()1542     void tearDown() {
1543         DeviceConfig.removeOnPropertiesChangedListener(mConstantsObserver);
1544         unregisterForUidObservers();
1545         unregisterForSystemBroadcasts();
1546         mRestrictionSettings.removeXml();
1547     }
1548 
initBgRestrictionExemptioFromSysConfig()1549     private void initBgRestrictionExemptioFromSysConfig() {
1550         final SystemConfig sysConfig = SystemConfig.getInstance();
1551         mBgRestrictionExemptioFromSysConfig = sysConfig.getBgRestrictionExemption();
1552         if (DEBUG_BG_RESTRICTION_CONTROLLER) {
1553             final ArraySet<String> exemptedPkgs = mBgRestrictionExemptioFromSysConfig;
1554             for (int i = exemptedPkgs.size() - 1; i >= 0; i--) {
1555                 Slog.i(TAG, "bg-restriction-exemption: " + exemptedPkgs.valueAt(i));
1556             }
1557         }
1558         loadAppIdsFromPackageList(sysConfig.getAllowInPowerSaveExceptIdle(),
1559                 mSystemDeviceIdleExceptIdleAllowlist);
1560         loadAppIdsFromPackageList(sysConfig.getAllowInPowerSave(), mSystemDeviceIdleAllowlist);
1561     }
1562 
loadAppIdsFromPackageList(ArraySet<String> packages, ArraySet<Integer> apps)1563     private void loadAppIdsFromPackageList(ArraySet<String> packages, ArraySet<Integer> apps) {
1564         final PackageManager pm = mInjector.getPackageManager();
1565         for (int i = packages.size() - 1; i >= 0; i--) {
1566             final String pkg = packages.valueAt(i);
1567             try {
1568                 final ApplicationInfo ai = pm.getApplicationInfo(pkg,
1569                         PackageManager.MATCH_SYSTEM_ONLY);
1570                 if (ai == null) {
1571                     continue;
1572                 }
1573                 apps.add(UserHandle.getAppId(ai.uid));
1574             } catch (PackageManager.NameNotFoundException e) {
1575             }
1576         }
1577     }
1578 
isExemptedFromSysConfig(String packageName)1579     private boolean isExemptedFromSysConfig(String packageName) {
1580         return mBgRestrictionExemptioFromSysConfig != null
1581                 && mBgRestrictionExemptioFromSysConfig.contains(packageName);
1582     }
1583 
initRestrictionStates()1584     private void initRestrictionStates() {
1585         final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
1586         for (int userId : allUsers) {
1587             refreshAppRestrictionLevelForUser(userId, REASON_MAIN_FORCED_BY_USER,
1588                     REASON_SUB_FORCED_USER_FLAG_INTERACTION);
1589         }
1590         if (!mInjector.isTest()) {
1591             // Load the previously saved levels and update the current levels if needed.
1592             mRestrictionSettings.scheduleLoadFromXml();
1593             // Also save the current levels right away.
1594             for (int userId : allUsers) {
1595                 mRestrictionSettings.schedulePersistToXml(userId);
1596             }
1597         }
1598     }
1599 
initSystemModuleNames()1600     private void initSystemModuleNames() {
1601         final PackageManager pm = mInjector.getPackageManager();
1602         final List<ModuleInfo> moduleInfos = pm.getInstalledModules(0 /* flags */);
1603         if (moduleInfos == null) {
1604             return;
1605         }
1606         synchronized (mLock) {
1607             for (ModuleInfo info : moduleInfos) {
1608                 mSystemModulesCache.put(info.getPackageName(), Boolean.TRUE);
1609             }
1610         }
1611     }
1612 
isSystemModule(String packageName)1613     private boolean isSystemModule(String packageName) {
1614         synchronized (mLock) {
1615             final Boolean val = mSystemModulesCache.get(packageName);
1616             if (val != null) {
1617                 return val.booleanValue();
1618             }
1619         }
1620 
1621         // Slow path: check if the package is listed among the system modules.
1622         final PackageManager pm = mInjector.getPackageManager();
1623         boolean isSystemModule = false;
1624         try {
1625             isSystemModule = pm.getModuleInfo(packageName, 0 /* flags */) != null;
1626         } catch (PackageManager.NameNotFoundException e) {
1627         }
1628 
1629         if (!isSystemModule) {
1630             try {
1631                 final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
1632                 // Check if the package is contained in an APEX. There is no public API to properly
1633                 // check whether a given APK package comes from an APEX registered as module.
1634                 // Therefore we conservatively assume that any package scanned from an /apex path is
1635                 // a system package.
1636                 isSystemModule = pkg != null && pkg.applicationInfo.sourceDir.startsWith(
1637                         Environment.getApexDirectory().getAbsolutePath());
1638             } catch (PackageManager.NameNotFoundException e) {
1639             }
1640         }
1641         // Update the cache.
1642         synchronized (mLock) {
1643             mSystemModulesCache.put(packageName, isSystemModule);
1644         }
1645         return isSystemModule;
1646     }
1647 
registerForUidObservers()1648     private void registerForUidObservers() {
1649         try {
1650             mInjector.getIActivityManager().registerUidObserver(mUidObserver,
1651                     UID_OBSERVER_ACTIVE | UID_OBSERVER_GONE | UID_OBSERVER_IDLE
1652                     | UID_OBSERVER_PROCSTATE, PROCESS_STATE_FOREGROUND_SERVICE, "android");
1653         } catch (RemoteException e) {
1654             // Intra-process call, it won't happen.
1655         }
1656     }
1657 
unregisterForUidObservers()1658     private void unregisterForUidObservers() {
1659         try {
1660             mInjector.getIActivityManager().unregisterUidObserver(mUidObserver);
1661         } catch (RemoteException e) {
1662             // Intra-process call, it won't happen.
1663         }
1664     }
1665 
1666     /**
1667      * Called when initializing a user.
1668      */
refreshAppRestrictionLevelForUser(@serIdInt int userId, int reason, int subReason)1669     private void refreshAppRestrictionLevelForUser(@UserIdInt int userId, int reason,
1670             int subReason) {
1671         final List<AppStandbyInfo> appStandbyInfos = mInjector.getAppStandbyInternal()
1672                 .getAppStandbyBuckets(userId);
1673         if (ArrayUtils.isEmpty(appStandbyInfos)) {
1674             return;
1675         }
1676 
1677         if (DEBUG_BG_RESTRICTION_CONTROLLER) {
1678             Slog.i(TAG, "Refreshing restriction levels of user " + userId);
1679         }
1680         final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
1681         for (AppStandbyInfo info: appStandbyInfos) {
1682             final int uid = pm.getPackageUid(info.mPackageName, STOCK_PM_FLAGS, userId);
1683             if (uid < 0) {
1684                 // Shouldn't happen.
1685                 Slog.e(TAG, "Unable to find " + info.mPackageName + "/u" + userId);
1686                 continue;
1687             }
1688             final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(
1689                     userId, uid, info.mPackageName, info.mStandbyBucket, false, false);
1690             if (DEBUG_BG_RESTRICTION_CONTROLLER) {
1691                 Slog.i(TAG, "Proposed restriction level of " + info.mPackageName + "/"
1692                         + UserHandle.formatUid(uid) + ": "
1693                         + ActivityManager.restrictionLevelToName(levelTypePair.first)
1694                         + " " + info.mStandbyBucket);
1695             }
1696             applyRestrictionLevel(info.mPackageName, uid, levelTypePair.first, levelTypePair.second,
1697                     info.mStandbyBucket, true, reason, subReason);
1698         }
1699     }
1700 
refreshAppRestrictionLevelForUid(int uid, int reason, int subReason, boolean allowRequestBgRestricted)1701     void refreshAppRestrictionLevelForUid(int uid, int reason, int subReason,
1702             boolean allowRequestBgRestricted) {
1703         final String[] packages = mInjector.getPackageManager().getPackagesForUid(uid);
1704         if (ArrayUtils.isEmpty(packages)) {
1705             return;
1706         }
1707         final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
1708         final int userId = UserHandle.getUserId(uid);
1709         final long now = SystemClock.elapsedRealtime();
1710         for (String pkg: packages) {
1711             final int curBucket = appStandbyInternal.getAppStandbyBucket(pkg, userId, now, false);
1712             final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(userId, uid,
1713                     pkg, curBucket, allowRequestBgRestricted, true);
1714             if (DEBUG_BG_RESTRICTION_CONTROLLER) {
1715                 Slog.i(TAG, "Proposed restriction level of " + pkg + "/"
1716                         + UserHandle.formatUid(uid) + ": "
1717                         + ActivityManager.restrictionLevelToName(levelTypePair.first));
1718             }
1719             applyRestrictionLevel(pkg, uid, levelTypePair.first, levelTypePair.second,
1720                     curBucket, true, reason, subReason);
1721         }
1722     }
1723 
calcAppRestrictionLevel(@serIdInt int userId, int uid, String packageName, @UsageStatsManager.StandbyBuckets int standbyBucket, boolean allowRequestBgRestricted, boolean calcTrackers)1724     private Pair<Integer, TrackerInfo> calcAppRestrictionLevel(@UserIdInt int userId, int uid,
1725             String packageName, @UsageStatsManager.StandbyBuckets int standbyBucket,
1726             boolean allowRequestBgRestricted, boolean calcTrackers) {
1727         if (mInjector.getAppHibernationInternal().isHibernatingForUser(packageName, userId)) {
1728             return new Pair<>(RESTRICTION_LEVEL_FORCE_STOPPED, mEmptyTrackerInfo);
1729         }
1730         @RestrictionLevel int level;
1731         TrackerInfo trackerInfo = null;
1732         switch (standbyBucket) {
1733             case STANDBY_BUCKET_EXEMPTED:
1734                 level = RESTRICTION_LEVEL_EXEMPTED;
1735                 break;
1736             case STANDBY_BUCKET_NEVER:
1737                 if (!android.app.Flags.appRestrictionsApi()) {
1738                     level = RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
1739                     break;
1740                 }
1741             case STANDBY_BUCKET_ACTIVE:
1742             case STANDBY_BUCKET_WORKING_SET:
1743             case STANDBY_BUCKET_FREQUENT:
1744             case STANDBY_BUCKET_RARE:
1745             case STANDBY_BUCKET_RESTRICTED:
1746             default:
1747                 if (mInjector.getAppStateTracker()
1748                         .isAppBackgroundRestricted(uid, packageName)) {
1749                     return new Pair<>(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED, mEmptyTrackerInfo);
1750                 }
1751                 level = standbyBucket == STANDBY_BUCKET_RESTRICTED
1752                         ? RESTRICTION_LEVEL_RESTRICTED_BUCKET
1753                         : RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
1754                 if (calcTrackers) {
1755                     Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevelFromTackers(
1756                             uid, packageName, RESTRICTION_LEVEL_MAX);
1757                     @RestrictionLevel int l = levelTypePair.first;
1758                     if (l == RESTRICTION_LEVEL_EXEMPTED) {
1759                         return new Pair<>(RESTRICTION_LEVEL_EXEMPTED, levelTypePair.second);
1760                     }
1761                     if (l > level) {
1762                         level = l;
1763                         trackerInfo = levelTypePair.second;
1764                     }
1765                     if (level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
1766                         // This level can't be entered without user consent
1767                         if (allowRequestBgRestricted) {
1768                             mBgHandler.obtainMessage(BgHandler.MSG_REQUEST_BG_RESTRICTED,
1769                                     uid, 0, packageName).sendToTarget();
1770                         }
1771                         // Lower the level.
1772                         levelTypePair = calcAppRestrictionLevelFromTackers(uid, packageName,
1773                                 RESTRICTION_LEVEL_BACKGROUND_RESTRICTED);
1774                         level = levelTypePair.first;
1775                         trackerInfo = levelTypePair.second;
1776                     }
1777                 }
1778                 break;
1779         }
1780         return new Pair<>(level, trackerInfo);
1781     }
1782 
1783     /**
1784      * Ask each of the trackers for their proposed restriction levels for the given uid/package,
1785      * and return the most restrictive level along with the type of tracker and its relevant info
1786      * which applied this restriction level as a {@code Pair<@RestrictionLevel, TrackerInfo>}.
1787      *
1788      * <p>Note, it's different from the {@link #getRestrictionLevel} where it returns the least
1789      * restrictive level. We're returning the most restrictive level here because each tracker
1790      * monitors certain dimensions of the app, the abusive behaviors could be detected in one or
1791      * more of these dimensions, but not necessarily all of them. </p>
1792      */
calcAppRestrictionLevelFromTackers(int uid, String packageName, @RestrictionLevel int maxLevel)1793     private Pair<Integer, TrackerInfo> calcAppRestrictionLevelFromTackers(int uid,
1794             String packageName, @RestrictionLevel int maxLevel) {
1795         @RestrictionLevel int level = RESTRICTION_LEVEL_UNKNOWN;
1796         @RestrictionLevel int prevLevel = level;
1797         BaseAppStateTracker resultTracker = null;
1798         for (int i = mAppStateTrackers.size() - 1; i >= 0; i--) {
1799             @RestrictionLevel int l = mAppStateTrackers.get(i).getPolicy()
1800                     .getProposedRestrictionLevel(packageName, uid, maxLevel);
1801             level = Math.max(level, l);
1802             if (level != prevLevel) {
1803                 resultTracker = mAppStateTrackers.get(i);
1804                 prevLevel = level;
1805             }
1806         }
1807         final TrackerInfo trackerInfo = resultTracker == null
1808                                             ? mEmptyTrackerInfo
1809                                             : new TrackerInfo(resultTracker.getType(),
1810                                                     resultTracker.getTrackerInfoForStatsd(uid));
1811         return new Pair<>(level, trackerInfo);
1812     }
1813 
standbyBucketToRestrictionLevel( @sageStatsManager.StandbyBuckets int standbyBucket)1814     private static @RestrictionLevel int standbyBucketToRestrictionLevel(
1815             @UsageStatsManager.StandbyBuckets int standbyBucket) {
1816         switch (standbyBucket) {
1817             case STANDBY_BUCKET_EXEMPTED:
1818                 return RESTRICTION_LEVEL_EXEMPTED;
1819             case STANDBY_BUCKET_NEVER:
1820                 if (!android.app.Flags.appRestrictionsApi()) {
1821                     return RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
1822                 }
1823             case STANDBY_BUCKET_ACTIVE:
1824             case STANDBY_BUCKET_WORKING_SET:
1825             case STANDBY_BUCKET_FREQUENT:
1826             case STANDBY_BUCKET_RARE:
1827                 return RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
1828             case STANDBY_BUCKET_RESTRICTED:
1829                 return RESTRICTION_LEVEL_RESTRICTED_BUCKET;
1830             default:
1831                 return RESTRICTION_LEVEL_UNKNOWN;
1832         }
1833     }
1834 
1835     /**
1836      * Get the restriction level of the given UID, if it hosts multiple packages,
1837      * return least restricted one (or if any of them is exempted).
1838      */
getRestrictionLevel(int uid)1839     @RestrictionLevel int getRestrictionLevel(int uid) {
1840         return mRestrictionSettings.getRestrictionLevel(uid);
1841     }
1842 
1843     /**
1844      * Get the restriction level of the given UID and package.
1845      */
getRestrictionLevel(int uid, String packageName)1846     @RestrictionLevel int getRestrictionLevel(int uid, String packageName) {
1847         return mRestrictionSettings.getRestrictionLevel(uid, packageName);
1848     }
1849 
1850     /**
1851      * Get the restriction level of the given package in given user id.
1852      */
getRestrictionLevel(String packageName, @UserIdInt int userId)1853     @RestrictionLevel int getRestrictionLevel(String packageName, @UserIdInt int userId) {
1854         return mRestrictionSettings.getRestrictionLevel(packageName, userId);
1855     }
1856 
1857     /**
1858      * @return Whether or not to move the app to restricted level automatically
1859      * when system detects it's abusive.
1860      */
isAutoRestrictAbusiveAppEnabled()1861     boolean isAutoRestrictAbusiveAppEnabled() {
1862         return mConstantsObserver.mBgAutoRestrictAbusiveApps;
1863     }
1864 
1865     /**
1866      * @return The total foreground service durations for the given package/uid with given
1867      * foreground service type, or the total durations regardless the type if the given type is 0.
1868      */
getForegroundServiceTotalDurations(String packageName, int uid, long now, @ForegroundServiceType int serviceType)1869     long getForegroundServiceTotalDurations(String packageName, int uid, long now,
1870             @ForegroundServiceType int serviceType) {
1871         return mInjector.getAppFGSTracker().getTotalDurations(packageName, uid, now,
1872                 foregroundServiceTypeToIndex(serviceType));
1873     }
1874 
1875     /**
1876      * @return The total foreground service durations for the given uid with given
1877      * foreground service type, or the total durations regardless the type if the given type is 0.
1878      */
getForegroundServiceTotalDurations(int uid, long now, @ForegroundServiceType int serviceType)1879     long getForegroundServiceTotalDurations(int uid, long now,
1880             @ForegroundServiceType int serviceType) {
1881         return mInjector.getAppFGSTracker().getTotalDurations(uid, now,
1882                 foregroundServiceTypeToIndex(serviceType));
1883     }
1884 
1885     /**
1886      * @return The foreground service durations since given timestamp for the given package/uid
1887      * with given foreground service type, or the total durations regardless the type if the given
1888      * type is 0.
1889      */
getForegroundServiceTotalDurationsSince(String packageName, int uid, long since, long now, @ForegroundServiceType int serviceType)1890     long getForegroundServiceTotalDurationsSince(String packageName, int uid, long since, long now,
1891             @ForegroundServiceType int serviceType) {
1892         return mInjector.getAppFGSTracker().getTotalDurationsSince(packageName, uid, since, now,
1893                 foregroundServiceTypeToIndex(serviceType));
1894     }
1895 
1896     /**
1897      * @return The foreground service durations since given timestamp for the given uid with given
1898      * foreground service type, or the total durations regardless the type if the given type is 0.
1899      */
getForegroundServiceTotalDurationsSince(int uid, long since, long now, @ForegroundServiceType int serviceType)1900     long getForegroundServiceTotalDurationsSince(int uid, long since, long now,
1901             @ForegroundServiceType int serviceType) {
1902         return mInjector.getAppFGSTracker().getTotalDurationsSince(uid, since, now,
1903                 foregroundServiceTypeToIndex(serviceType));
1904     }
1905 
1906     /**
1907      * @return The total durations for the given package/uid with active media session.
1908      */
getMediaSessionTotalDurations(String packageName, int uid, long now)1909     long getMediaSessionTotalDurations(String packageName, int uid, long now) {
1910         return mInjector.getAppMediaSessionTracker().getTotalDurations(packageName, uid, now);
1911     }
1912 
1913     /**
1914      * @return The total durations for the given uid with active media session.
1915      */
getMediaSessionTotalDurations(int uid, long now)1916     long getMediaSessionTotalDurations(int uid, long now) {
1917         return mInjector.getAppMediaSessionTracker().getTotalDurations(uid, now);
1918     }
1919 
1920     /**
1921      * @return The durations since given timestamp for the given package/uid with
1922      * active media session.
1923      */
getMediaSessionTotalDurationsSince(String packageName, int uid, long since, long now)1924     long getMediaSessionTotalDurationsSince(String packageName, int uid, long since, long now) {
1925         return mInjector.getAppMediaSessionTracker().getTotalDurationsSince(packageName, uid, since,
1926                 now);
1927     }
1928 
1929     /**
1930      * @return The durations since given timestamp for the given uid with active media session.
1931      */
getMediaSessionTotalDurationsSince(int uid, long since, long now)1932     long getMediaSessionTotalDurationsSince(int uid, long since, long now) {
1933         return mInjector.getAppMediaSessionTracker().getTotalDurationsSince(uid, since, now);
1934     }
1935 
1936     /**
1937      * @return The durations over the given window, where the given package/uid has either
1938      * foreground services with type "mediaPlayback" running, or active media session running.
1939      */
getCompositeMediaPlaybackDurations(String packageName, int uid, long now, long window)1940     long getCompositeMediaPlaybackDurations(String packageName, int uid, long now, long window) {
1941         final long since = Math.max(0, now - window);
1942         final long mediaPlaybackDuration = Math.max(
1943                 getMediaSessionTotalDurationsSince(packageName, uid, since, now),
1944                 getForegroundServiceTotalDurationsSince(packageName, uid, since, now,
1945                         ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK));
1946         return mediaPlaybackDuration;
1947     }
1948 
1949     /**
1950      * @return The durations over the given window, where the given uid has either foreground
1951      * services with type "mediaPlayback" running, or active media session running.
1952      */
getCompositeMediaPlaybackDurations(int uid, long now, long window)1953     long getCompositeMediaPlaybackDurations(int uid, long now, long window) {
1954         final long since = Math.max(0, now - window);
1955         final long mediaPlaybackDuration = Math.max(
1956                 getMediaSessionTotalDurationsSince(uid, since, now),
1957                 getForegroundServiceTotalDurationsSince(uid, since, now,
1958                         ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK));
1959         return mediaPlaybackDuration;
1960     }
1961 
1962     /**
1963      * @return If the given package/uid has an active foreground service running.
1964      */
hasForegroundServices(String packageName, int uid)1965     boolean hasForegroundServices(String packageName, int uid) {
1966         return mInjector.getAppFGSTracker().hasForegroundServices(packageName, uid);
1967     }
1968 
1969     /**
1970      * @return If the given uid has an active foreground service running.
1971      */
hasForegroundServices(int uid)1972     boolean hasForegroundServices(int uid) {
1973         return mInjector.getAppFGSTracker().hasForegroundServices(uid);
1974     }
1975 
1976     /**
1977      * @return If the given package/uid has a foreground service notification or not.
1978      */
hasForegroundServiceNotifications(String packageName, int uid)1979     boolean hasForegroundServiceNotifications(String packageName, int uid) {
1980         return mInjector.getAppFGSTracker().hasForegroundServiceNotifications(packageName, uid);
1981     }
1982 
1983     /**
1984      * @return If the given uid has a foreground service notification or not.
1985      */
hasForegroundServiceNotifications(int uid)1986     boolean hasForegroundServiceNotifications(int uid) {
1987         return mInjector.getAppFGSTracker().hasForegroundServiceNotifications(uid);
1988     }
1989 
1990     /**
1991      * @return The to-be-exempted battery usage of the given UID in the given duration; it could
1992      *         be considered as "exempted" due to various use cases, i.e. media playback.
1993      */
getUidBatteryExemptedUsageSince(int uid, long since, long now, int types)1994     ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now,
1995             int types) {
1996         return mInjector.getAppBatteryExemptionTracker()
1997                 .getUidBatteryExemptedUsageSince(uid, since, now, types);
1998     }
1999 
2000     /**
2001      * @return The total battery usage of the given UID since the system boots.
2002      */
getUidBatteryUsage(int uid)2003     @NonNull ImmutableBatteryUsage getUidBatteryUsage(int uid) {
2004         return mInjector.getUidBatteryUsageProvider().getUidBatteryUsage(uid);
2005     }
2006 
2007     interface UidBatteryUsageProvider {
2008         /**
2009          * @return The total battery usage of the given UID since the system boots.
2010          */
getUidBatteryUsage(int uid)2011         @NonNull ImmutableBatteryUsage getUidBatteryUsage(int uid);
2012     }
2013 
dump(PrintWriter pw, String prefix)2014     void dump(PrintWriter pw, String prefix) {
2015         pw.print(prefix);
2016         pw.println("APP BACKGROUND RESTRICTIONS");
2017         prefix = "  " + prefix;
2018         pw.print(prefix);
2019         pw.println("BACKGROUND RESTRICTION LEVEL SETTINGS");
2020         mRestrictionSettings.dump(pw, "  " + prefix);
2021         mConstantsObserver.dump(pw, "  " + prefix);
2022         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
2023             pw.println();
2024             mAppStateTrackers.get(i).dump(pw, prefix);
2025         }
2026     }
2027 
dumpAsProto(ProtoOutputStream proto, int uid)2028     void dumpAsProto(ProtoOutputStream proto, int uid) {
2029         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
2030             mAppStateTrackers.get(i).dumpAsProto(proto, uid);
2031         }
2032     }
2033 
getRestrictionLevelStatsd(@estrictionLevel int level)2034     private int getRestrictionLevelStatsd(@RestrictionLevel int level) {
2035         switch (level) {
2036             case RESTRICTION_LEVEL_UNKNOWN:
2037                 return AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN;
2038             case RESTRICTION_LEVEL_UNRESTRICTED:
2039                 return AppBackgroundRestrictionsInfo.LEVEL_UNRESTRICTED;
2040             case RESTRICTION_LEVEL_EXEMPTED:
2041                 return AppBackgroundRestrictionsInfo.LEVEL_EXEMPTED;
2042             case RESTRICTION_LEVEL_ADAPTIVE_BUCKET:
2043                 return AppBackgroundRestrictionsInfo.LEVEL_ADAPTIVE_BUCKET;
2044             case RESTRICTION_LEVEL_RESTRICTED_BUCKET:
2045                 return AppBackgroundRestrictionsInfo.LEVEL_RESTRICTED_BUCKET;
2046             case RESTRICTION_LEVEL_BACKGROUND_RESTRICTED:
2047                 return AppBackgroundRestrictionsInfo.LEVEL_BACKGROUND_RESTRICTED;
2048             case RESTRICTION_LEVEL_FORCE_STOPPED:
2049                 return AppBackgroundRestrictionsInfo.LEVEL_HIBERNATION;
2050             default:
2051                 return AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN;
2052         }
2053     }
2054 
getThresholdStatsd(int reason)2055     private int getThresholdStatsd(int reason) {
2056         switch (reason) {
2057             case REASON_MAIN_FORCED_BY_SYSTEM:
2058                 return AppBackgroundRestrictionsInfo.THRESHOLD_RESTRICTED;
2059             case REASON_MAIN_FORCED_BY_USER:
2060                 return AppBackgroundRestrictionsInfo.THRESHOLD_USER;
2061             default:
2062                 return AppBackgroundRestrictionsInfo.THRESHOLD_UNKNOWN;
2063         }
2064     }
2065 
getTrackerTypeStatsd(@rackerType int type)2066     private int getTrackerTypeStatsd(@TrackerType int type) {
2067         switch (type) {
2068             case TRACKER_TYPE_BATTERY:
2069                 return AppBackgroundRestrictionsInfo.BATTERY_TRACKER;
2070             case TRACKER_TYPE_BATTERY_EXEMPTION:
2071                 return AppBackgroundRestrictionsInfo.BATTERY_EXEMPTION_TRACKER;
2072             case TRACKER_TYPE_FGS:
2073                 return AppBackgroundRestrictionsInfo.FGS_TRACKER;
2074             case TRACKER_TYPE_MEDIA_SESSION:
2075                 return AppBackgroundRestrictionsInfo.MEDIA_SESSION_TRACKER;
2076             case TRACKER_TYPE_PERMISSION:
2077                 return AppBackgroundRestrictionsInfo.PERMISSION_TRACKER;
2078             case TRACKER_TYPE_BROADCAST_EVENTS:
2079                 return AppBackgroundRestrictionsInfo.BROADCAST_EVENTS_TRACKER;
2080             case TRACKER_TYPE_BIND_SERVICE_EVENTS:
2081                 return AppBackgroundRestrictionsInfo.BIND_SERVICE_EVENTS_TRACKER;
2082             default:
2083                 return AppBackgroundRestrictionsInfo.UNKNOWN_TRACKER;
2084         }
2085     }
2086 
getExemptionReasonStatsd(int uid, @RestrictionLevel int level)2087     private int getExemptionReasonStatsd(int uid, @RestrictionLevel int level) {
2088         if (level != RESTRICTION_LEVEL_EXEMPTED) {
2089             return AppBackgroundRestrictionsInfo.REASON_DENIED;
2090         }
2091 
2092         @ReasonCode final int reasonCode = getBackgroundRestrictionExemptionReason(uid);
2093         return getExemptionReasonForStatsd(reasonCode);
2094     }
2095 
getOptimizationLevelStatsd(@estrictionLevel int level)2096     private int getOptimizationLevelStatsd(@RestrictionLevel int level) {
2097         switch (level) {
2098             case RESTRICTION_LEVEL_UNKNOWN:
2099                 return AppBackgroundRestrictionsInfo.UNKNOWN;
2100             case RESTRICTION_LEVEL_UNRESTRICTED:
2101                 return AppBackgroundRestrictionsInfo.NOT_OPTIMIZED;
2102             case RESTRICTION_LEVEL_ADAPTIVE_BUCKET:
2103                 return AppBackgroundRestrictionsInfo.OPTIMIZED;
2104             case RESTRICTION_LEVEL_BACKGROUND_RESTRICTED:
2105                 return AppBackgroundRestrictionsInfo.BACKGROUND_RESTRICTED;
2106             default:
2107                 return AppBackgroundRestrictionsInfo.UNKNOWN;
2108         }
2109     }
2110 
2111     @SuppressWarnings("AndroidFrameworkCompatChange")
getTargetSdkStatsd(String packageName)2112     private int getTargetSdkStatsd(String packageName) {
2113         final PackageManager pm = mInjector.getPackageManager();
2114         if (pm == null) {
2115             return AppBackgroundRestrictionsInfo.SDK_UNKNOWN;
2116         }
2117         try {
2118             final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
2119             if (pkg == null || pkg.applicationInfo == null) {
2120                 return AppBackgroundRestrictionsInfo.SDK_UNKNOWN;
2121             }
2122             final int targetSdk = pkg.applicationInfo.targetSdkVersion;
2123             if (targetSdk < Build.VERSION_CODES.S) {
2124                 return AppBackgroundRestrictionsInfo.SDK_PRE_S;
2125             } else if (targetSdk < Build.VERSION_CODES.TIRAMISU) {
2126                 return AppBackgroundRestrictionsInfo.SDK_S;
2127             } else if (targetSdk == Build.VERSION_CODES.TIRAMISU) {
2128                 return AppBackgroundRestrictionsInfo.SDK_T;
2129             } else {
2130                 return AppBackgroundRestrictionsInfo.SDK_UNKNOWN;
2131             }
2132         } catch (PackageManager.NameNotFoundException ignored) {
2133         }
2134         return AppBackgroundRestrictionsInfo.SDK_UNKNOWN;
2135     }
2136 
applyRestrictionLevel(String pkgName, int uid, @RestrictionLevel int level, TrackerInfo trackerInfo, int curBucket, boolean allowUpdateBucket, int reason, int subReason)2137     void applyRestrictionLevel(String pkgName, int uid,
2138             @RestrictionLevel int level, TrackerInfo trackerInfo,
2139             int curBucket, boolean allowUpdateBucket, int reason, int subReason) {
2140         int curLevel;
2141         int prevReason;
2142         final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
2143         if (trackerInfo == null) {
2144             trackerInfo = mEmptyTrackerInfo;
2145         }
2146         synchronized (mSettingsLock) {
2147             curLevel = getRestrictionLevel(uid, pkgName);
2148             if (curLevel == level) {
2149                 // Nothing to do.
2150                 return;
2151             }
2152             final int levelOfBucket = standbyBucketToRestrictionLevel(curBucket);
2153             if (levelOfBucket == level) {
2154                 // If the standby bucket yield the same level, use the reason from standby bucket.
2155                 final int bucketReason = appStandbyInternal.getAppStandbyBucketReason(
2156                         pkgName, UserHandle.getUserId(uid), SystemClock.elapsedRealtime());
2157                 if (bucketReason != 0) {
2158                     reason = bucketReason & REASON_MAIN_MASK;
2159                     subReason = bucketReason & REASON_SUB_MASK;
2160                 }
2161             }
2162             if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2163                 Slog.i(TAG, "Updating the restriction level of " + pkgName + "/"
2164                         + UserHandle.formatUid(uid) + " from "
2165                         + ActivityManager.restrictionLevelToName(curLevel) + " to "
2166                         + ActivityManager.restrictionLevelToName(level)
2167                         + " reason=" + reason + ", subReason=" + subReason);
2168             }
2169             prevReason = mRestrictionSettings.getReason(pkgName, uid);
2170             mRestrictionSettings.update(pkgName, uid, level, reason, subReason);
2171         }
2172 
2173         if (!android.app.Flags.appRestrictionsApi()
2174                 && (!allowUpdateBucket || curBucket == STANDBY_BUCKET_EXEMPTED)) {
2175             return;
2176         }
2177 
2178         boolean doItNow = true;
2179         if (level >= RESTRICTION_LEVEL_RESTRICTED_BUCKET
2180                 && curLevel < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
2181             // Moving the app standby bucket to restricted in the meanwhile.
2182             if (DEBUG_BG_RESTRICTION_CONTROLLER
2183                     && level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
2184                 Slog.i(TAG, pkgName + "/" + UserHandle.formatUid(uid)
2185                         + " is bg-restricted, moving to restricted standby bucket");
2186             }
2187             if (curBucket != STANDBY_BUCKET_RESTRICTED
2188                     && (mConstantsObserver.mBgAutoRestrictedBucket
2189                     || level == RESTRICTION_LEVEL_RESTRICTED_BUCKET)) {
2190                 // restrict the app if it hasn't done so.
2191                 synchronized (mSettingsLock) {
2192                     final int index = mActiveUids.indexOfKey(uid, pkgName);
2193                     if (index >= 0) {
2194                         // It's currently active, enqueue it.
2195                         final int localReason = reason;
2196                         final int localSubReason = subReason;
2197                         final TrackerInfo localTrackerInfo = trackerInfo;
2198                         mActiveUids.add(uid, pkgName, () -> {
2199                             appStandbyInternal.restrictApp(pkgName, UserHandle.getUserId(uid),
2200                                     localReason, localSubReason);
2201                             logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level,
2202                                     localTrackerInfo, localReason);
2203                         });
2204                         doItNow = false;
2205                     }
2206                 }
2207                 if (doItNow) {
2208                     appStandbyInternal.restrictApp(pkgName, UserHandle.getUserId(uid),
2209                             reason, subReason);
2210                     if (!android.app.Flags.appRestrictionsApi()) {
2211                         logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo,
2212                                 reason);
2213                     }
2214                 }
2215             }
2216         } else if (curLevel >= RESTRICTION_LEVEL_RESTRICTED_BUCKET
2217                 && level < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
2218             // Moved out of the background-restricted state.
2219             synchronized (mSettingsLock) {
2220                 final int index = mActiveUids.indexOfKey(uid, pkgName);
2221                 if (index >= 0) {
2222                     mActiveUids.add(uid, pkgName, null);
2223                 }
2224             }
2225             appStandbyInternal.maybeUnrestrictApp(pkgName, UserHandle.getUserId(uid),
2226                     prevReason & REASON_MAIN_MASK, prevReason & REASON_SUB_MASK,
2227                     reason, subReason);
2228             if (!android.app.Flags.appRestrictionsApi()) {
2229                 logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo,
2230                         reason);
2231             }
2232         }
2233 
2234         if (doItNow && android.app.Flags.appRestrictionsApi()
2235                 && curLevel != RESTRICTION_LEVEL_UNKNOWN) {
2236             logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo,
2237                     reason);
2238         }
2239     }
2240 
logAppBackgroundRestrictionInfo(String pkgName, int uid, @RestrictionLevel int prevLevel, @RestrictionLevel int level, @NonNull TrackerInfo trackerInfo, int reason)2241     private void logAppBackgroundRestrictionInfo(String pkgName, int uid,
2242             @RestrictionLevel int prevLevel, @RestrictionLevel int level,
2243             @NonNull TrackerInfo trackerInfo, int reason) {
2244         FrameworkStatsLog.write(FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO, uid,
2245                 getRestrictionLevelStatsd(level),
2246                 getThresholdStatsd(reason),
2247                 getTrackerTypeStatsd(trackerInfo.mType),
2248                 trackerInfo.mType == TRACKER_TYPE_FGS ? trackerInfo.mInfo : null,
2249                 trackerInfo.mType == TRACKER_TYPE_BATTERY ? trackerInfo.mInfo : null,
2250                 trackerInfo.mType == TRACKER_TYPE_BROADCAST_EVENTS ? trackerInfo.mInfo : null,
2251                 trackerInfo.mType == TRACKER_TYPE_BIND_SERVICE_EVENTS ? trackerInfo.mInfo : null,
2252                 getExemptionReasonStatsd(uid, level),
2253                 getOptimizationLevelStatsd(level),
2254                 getTargetSdkStatsd(pkgName),
2255                 ActivityManager.isLowRamDeviceStatic(),
2256                 getRestrictionLevelStatsd(prevLevel));
2257     }
2258 
handleBackgroundRestrictionChanged(int uid, String pkgName, boolean restricted)2259     private void handleBackgroundRestrictionChanged(int uid, String pkgName, boolean restricted) {
2260         // Firstly, notify the trackers.
2261         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
2262             mAppStateTrackers.get(i)
2263                     .onBackgroundRestrictionChanged(uid, pkgName, restricted);
2264         }
2265 
2266         final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
2267         final int userId = UserHandle.getUserId(uid);
2268         final long now = SystemClock.elapsedRealtime();
2269         final int curBucket = appStandbyInternal.getAppStandbyBucket(pkgName, userId, now, false);
2270         if (restricted) {
2271             // The app could fall into the background restricted with user consent only,
2272             // so set the reason to it.
2273             applyRestrictionLevel(pkgName, uid, RESTRICTION_LEVEL_BACKGROUND_RESTRICTED,
2274                     mEmptyTrackerInfo, curBucket, true, REASON_MAIN_FORCED_BY_USER,
2275                     REASON_SUB_FORCED_USER_FLAG_INTERACTION);
2276             mBgHandler.obtainMessage(BgHandler.MSG_CANCEL_REQUEST_BG_RESTRICTED, uid, 0, pkgName)
2277                     .sendToTarget();
2278         } else {
2279             // Moved out of the background-restricted state, we'd need to check if it should
2280             // stay in the restricted standby bucket.
2281             final @RestrictionLevel int lastLevel =
2282                     mRestrictionSettings.getLastRestrictionLevel(uid, pkgName);
2283             final int tentativeBucket = curBucket == STANDBY_BUCKET_EXEMPTED
2284                     ? STANDBY_BUCKET_EXEMPTED
2285                     : (lastLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET
2286                             ? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE);
2287             final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(
2288                     UserHandle.getUserId(uid), uid, pkgName, tentativeBucket, false, true);
2289 
2290             applyRestrictionLevel(pkgName, uid, levelTypePair.first, levelTypePair.second,
2291                     curBucket, true, REASON_MAIN_USAGE, REASON_SUB_USAGE_USER_INTERACTION);
2292         }
2293     }
2294 
dispatchAppRestrictionLevelChanges(int uid, String pkgName, @RestrictionLevel int newLevel)2295     private void dispatchAppRestrictionLevelChanges(int uid, String pkgName,
2296             @RestrictionLevel int newLevel) {
2297         mRestrictionListeners.forEach(
2298                 l -> l.onRestrictionLevelChanged(uid, pkgName, newLevel));
2299     }
2300 
dispatchAutoRestrictedBucketFeatureFlagChanged(boolean newValue)2301     private void dispatchAutoRestrictedBucketFeatureFlagChanged(boolean newValue) {
2302         final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
2303         final ArrayList<Runnable> pendingTasks = new ArrayList<>();
2304         synchronized (mSettingsLock) {
2305             mRestrictionSettings.forEachUidLocked(uid -> {
2306                 mRestrictionSettings.forEachPackageInUidLocked(uid, (pkgName, level, reason) -> {
2307                     if (level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
2308                         pendingTasks.add(newValue
2309                                 ? () -> appStandbyInternal.restrictApp(pkgName,
2310                                 UserHandle.getUserId(uid), reason & REASON_MAIN_MASK,
2311                                 reason & REASON_SUB_MASK)
2312                                 : () -> appStandbyInternal.maybeUnrestrictApp(pkgName,
2313                                 UserHandle.getUserId(uid), reason & REASON_MAIN_MASK,
2314                                 reason & REASON_SUB_MASK, REASON_MAIN_USAGE,
2315                                 REASON_SUB_USAGE_SYSTEM_UPDATE));
2316                     }
2317                 });
2318             });
2319         }
2320         for (int i = 0; i < pendingTasks.size(); i++) {
2321             pendingTasks.get(i).run();
2322         }
2323         mRestrictionListeners.forEach(
2324                 l -> l.onAutoRestrictedBucketFeatureFlagChanged(newValue));
2325     }
2326 
handleAppStandbyBucketChanged(int bucket, String packageName, @UserIdInt int userId)2327     private void handleAppStandbyBucketChanged(int bucket, String packageName,
2328             @UserIdInt int userId) {
2329         // Ignore spurious changes to standby bucket during early boot
2330         if (android.app.Flags.appRestrictionsApi() && !mLockedBootCompleted) return;
2331 
2332         final int uid = mInjector.getPackageManagerInternal().getPackageUid(
2333                 packageName, STOCK_PM_FLAGS, userId);
2334         final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(
2335                 userId, uid, packageName, bucket, false, false);
2336         applyRestrictionLevel(packageName, uid, levelTypePair.first, levelTypePair.second,
2337                 bucket, false, REASON_MAIN_DEFAULT, REASON_SUB_DEFAULT_UNDEFINED);
2338     }
2339 
handleRequestBgRestricted(String packageName, int uid)2340     void handleRequestBgRestricted(String packageName, int uid) {
2341         if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2342             Slog.i(TAG, "Requesting background restricted " + packageName + " "
2343                     + UserHandle.formatUid(uid));
2344         }
2345         mNotificationHelper.postRequestBgRestrictedIfNecessary(packageName, uid);
2346     }
2347 
handleCancelRequestBgRestricted(String packageName, int uid)2348     void handleCancelRequestBgRestricted(String packageName, int uid) {
2349         if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2350             Slog.i(TAG, "Cancelling requesting background restricted " + packageName + " "
2351                     + UserHandle.formatUid(uid));
2352         }
2353         mNotificationHelper.cancelRequestBgRestrictedIfNecessary(packageName, uid);
2354     }
2355 
handleUidProcStateChanged(int uid, int procState)2356     void handleUidProcStateChanged(int uid, int procState) {
2357         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
2358             mAppStateTrackers.get(i).onUidProcStateChanged(uid, procState);
2359         }
2360     }
2361 
handleUidGone(int uid)2362     void handleUidGone(int uid) {
2363         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
2364             mAppStateTrackers.get(i).onUidGone(uid);
2365         }
2366     }
2367 
2368     /**
2369      * Log a change in restriction state with a reason and threshold.
2370      * @param packageName
2371      * @param uid
2372      * @param restrictionType
2373      * @param enabled
2374      * @param reason
2375      * @param subReason Eg: settings, cli, long_wakelock, crash, binder_spam, cpu, threads
2376      *                  Length should not exceed RESTRICTON_SUBREASON_MAX_LENGTH
2377      * @param threshold
2378      */
noteAppRestrictionEnabled(String packageName, int uid, @RestrictionLevel int restrictionType, boolean enabled, @RestrictionReason int reason, String subReason, @RestrictionSource int source, long threshold)2379     public void noteAppRestrictionEnabled(String packageName, int uid,
2380             @RestrictionLevel int restrictionType, boolean enabled,
2381             @RestrictionReason int reason, String subReason, @RestrictionSource int source,
2382             long threshold) {
2383         if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2384             Slog.i(TAG, (enabled ? "restricted " : "unrestricted ") + packageName + " to "
2385                     + restrictionType + " reason=" + reason + ", subReason=" + subReason
2386                     + ", threshold=" + threshold);
2387         }
2388 
2389         // Limit the length of the free-form subReason string
2390         if (subReason != null && subReason.length() > RESTRICTION_SUBREASON_MAX_LENGTH) {
2391             Slog.e(TAG, "subReason is too long, truncating " + subReason);
2392             subReason = subReason.substring(0, RESTRICTION_SUBREASON_MAX_LENGTH);
2393         }
2394 
2395         // Log the restriction reason
2396         FrameworkStatsLog.write(FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED, uid,
2397                 getRestrictionTypeStatsd(restrictionType),
2398                 enabled,
2399                 getRestrictionChangeReasonStatsd(reason, subReason),
2400                 subReason,
2401                 threshold,
2402                 source);
2403     }
2404 
getRestrictionTypeStatsd(@estrictionLevel int level)2405     private int getRestrictionTypeStatsd(@RestrictionLevel int level) {
2406         return switch (level) {
2407             case RESTRICTION_LEVEL_UNKNOWN ->
2408                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_UNKNOWN;
2409             case RESTRICTION_LEVEL_UNRESTRICTED ->
2410                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_UNRESTRICTED;
2411             case RESTRICTION_LEVEL_EXEMPTED ->
2412                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_EXEMPTED;
2413             case RESTRICTION_LEVEL_ADAPTIVE_BUCKET ->
2414                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_ADAPTIVE;
2415             case RESTRICTION_LEVEL_RESTRICTED_BUCKET ->
2416                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_RESTRICTED_BUCKET;
2417             case RESTRICTION_LEVEL_BACKGROUND_RESTRICTED ->
2418                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_BACKGROUND_RESTRICTED;
2419             case RESTRICTION_LEVEL_FORCE_STOPPED ->
2420                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_FORCE_STOPPED;
2421             case RESTRICTION_LEVEL_USER_LAUNCH_ONLY ->
2422                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_USER_LAUNCH_ONLY;
2423             default ->
2424                 FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__RESTRICTION_TYPE__TYPE_CUSTOM;
2425         };
2426     }
2427 
getRestrictionChangeReasonStatsd(int reason, String subReason)2428     private int getRestrictionChangeReasonStatsd(int reason, String subReason) {
2429         return switch (reason) {
2430             case RESTRICTION_REASON_DEFAULT ->
2431                 FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__MAIN_REASON__REASON_DEFAULT;
2432             case RESTRICTION_REASON_DORMANT ->
2433                 FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__MAIN_REASON__REASON_DORMANT;
2434             case RESTRICTION_REASON_USAGE ->
2435                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__MAIN_REASON__REASON_USAGE;
2436             case RESTRICTION_REASON_USER ->
2437                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__MAIN_REASON__REASON_USER;
2438             case RESTRICTION_REASON_SYSTEM_HEALTH ->
2439                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__MAIN_REASON__REASON_SYSTEM_HEALTH;
2440             case RESTRICTION_REASON_POLICY ->
2441                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__MAIN_REASON__REASON_POLICY;
2442             default ->
2443                     FrameworkStatsLog.APP_RESTRICTION_STATE_CHANGED__MAIN_REASON__REASON_OTHER;
2444         };
2445     }
2446 
2447     static class NotificationHelper {
2448         static final String PACKAGE_SCHEME = "package";
2449         static final String GROUP_KEY = "com.android.app.abusive_bg_apps";
2450 
2451         static final int SUMMARY_NOTIFICATION_ID = SystemMessage.NOTE_ABUSIVE_BG_APPS_BASE;
2452 
2453         static final int NOTIFICATION_TYPE_ABUSIVE_CURRENT_DRAIN = 0;
2454         static final int NOTIFICATION_TYPE_LONG_RUNNING_FGS = 1;
2455         static final int NOTIFICATION_TYPE_LAST = 2;
2456 
2457         static final String ATTR_LAST_BATTERY_NOTIFICATION_TIME = "last_batt_noti_ts";
2458         static final String ATTR_LAST_LONG_FGS_NOTIFICATION_TIME = "last_long_fgs_noti_ts";
2459 
2460         @IntDef(prefix = { "NOTIFICATION_TYPE_"}, value = {
2461             NOTIFICATION_TYPE_ABUSIVE_CURRENT_DRAIN,
2462             NOTIFICATION_TYPE_LONG_RUNNING_FGS,
2463         })
2464         @Retention(RetentionPolicy.SOURCE)
2465         static @interface NotificationType{}
2466 
2467         static final String[] NOTIFICATION_TYPE_STRINGS = {
2468             "Abusive current drain",
2469             "Long-running FGS",
2470         };
2471 
2472         static final String[] NOTIFICATION_TIME_ATTRS = {
2473             ATTR_LAST_BATTERY_NOTIFICATION_TIME,
2474             ATTR_LAST_LONG_FGS_NOTIFICATION_TIME,
2475         };
2476 
2477         static @NotificationType int notificationTimeAttrToType(@NonNull String attr) {
2478             switch (attr) {
2479                 case ATTR_LAST_BATTERY_NOTIFICATION_TIME:
2480                     return NOTIFICATION_TYPE_ABUSIVE_CURRENT_DRAIN;
2481                 case ATTR_LAST_LONG_FGS_NOTIFICATION_TIME:
2482                     return NOTIFICATION_TYPE_LONG_RUNNING_FGS;
2483             }
2484             throw new IllegalArgumentException();
2485         }
2486 
2487         static @NonNull String notificationTypeToTimeAttr(@NotificationType int type) {
2488             return NOTIFICATION_TIME_ATTRS[type];
2489         }
2490 
2491         static final String ACTION_FGS_MANAGER_TRAMPOLINE =
2492                 "com.android.server.am.ACTION_FGS_MANAGER_TRAMPOLINE";
2493 
2494         static String notificationTypeToString(@NotificationType int notificationType) {
2495             return NOTIFICATION_TYPE_STRINGS[notificationType];
2496         }
2497 
2498         private final AppRestrictionController mBgController;
2499         private final NotificationManager mNotificationManager;
2500         private final Injector mInjector;
2501         private final Object mLock;
2502         private final Object mSettingsLock;
2503         private final Context mContext;
2504 
2505         private final BroadcastReceiver mActionButtonReceiver = new BroadcastReceiver() {
2506             @Override
2507             public void onReceive(Context context, Intent intent) {
2508                 final String action = intent.getAction();
2509                 switch (intent.getAction()) {
2510                     case ACTION_FGS_MANAGER_TRAMPOLINE:
2511                         final String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
2512                         final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
2513                         cancelRequestBgRestrictedIfNecessary(packageName, uid);
2514                         final Intent newIntent = new Intent(ACTION_SHOW_FOREGROUND_SERVICE_MANAGER);
2515                         newIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
2516                         // Task manager runs in SystemUI, which is SYSTEM user only.
2517                         mContext.sendBroadcastAsUser(newIntent, UserHandle.SYSTEM);
2518                         break;
2519                 }
2520             }
2521         };
2522 
2523         @GuardedBy("mSettingsLock")
2524         private int mNotificationIDStepper = SUMMARY_NOTIFICATION_ID + 1;
2525 
2526         NotificationHelper(AppRestrictionController controller) {
2527             mBgController = controller;
2528             mInjector = controller.mInjector;
2529             mNotificationManager = mInjector.getNotificationManager();
2530             mLock = controller.mLock;
2531             mSettingsLock = controller.mSettingsLock;
2532             mContext = mInjector.getContext();
2533         }
2534 
2535         void onSystemReady() {
2536             mContext.registerReceiverForAllUsers(mActionButtonReceiver,
2537                     new IntentFilter(ACTION_FGS_MANAGER_TRAMPOLINE),
2538                     MANAGE_ACTIVITY_TASKS, mBgController.mBgHandler, Context.RECEIVER_NOT_EXPORTED);
2539         }
2540 
2541         void postRequestBgRestrictedIfNecessary(String packageName, int uid) {
2542             if (!mBgController.mConstantsObserver.mBgPromptAbusiveAppsToBgRestricted) {
2543                 if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2544                     Slog.i(TAG, "Not requesting bg-restriction due to config");
2545                 }
2546                 return;
2547             }
2548 
2549             final Intent intent = new Intent(Settings.ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL);
2550             intent.setData(Uri.fromParts(PACKAGE_SCHEME, packageName, null));
2551             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2552 
2553             final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(mContext, 0,
2554                     intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, null,
2555                     UserHandle.of(UserHandle.getUserId(uid)));
2556             Notification.Action[] actions = null;
2557             final boolean hasForegroundServices =
2558                     mBgController.hasForegroundServices(packageName, uid);
2559             final boolean hasForegroundServiceNotifications =
2560                     mBgController.hasForegroundServiceNotifications(packageName, uid);
2561             if (!mBgController.mConstantsObserver.mBgPromptFgsWithNotiToBgRestricted) {
2562                 // We're not going to prompt the user if the FGS is active and its notification
2563                 // is still showing (not dismissed/silenced/denied).
2564                 if (hasForegroundServices && hasForegroundServiceNotifications) {
2565                     if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2566                         Slog.i(TAG, "Not requesting bg-restriction due to FGS with notification");
2567                     }
2568                     return;
2569                 }
2570             }
2571             if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER
2572                     && ENABLE_SHOW_FGS_MANAGER_ACTION_ON_BG_RESTRICTION
2573                     && hasForegroundServices) {
2574                 final Intent trampoline = new Intent(ACTION_FGS_MANAGER_TRAMPOLINE);
2575                 trampoline.setPackage("android");
2576                 trampoline.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
2577                 trampoline.putExtra(Intent.EXTRA_UID, uid);
2578                 final PendingIntent fgsMgrTrampoline = PendingIntent.getBroadcastAsUser(
2579                         mContext, 0, trampoline,
2580                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE,
2581                         UserHandle.CURRENT);
2582                 actions = new Notification.Action[] {
2583                     new Notification.Action.Builder(null,
2584                             mContext.getString(
2585                             com.android.internal.R.string.notification_action_check_bg_apps),
2586                             fgsMgrTrampoline)
2587                             .build()
2588                 };
2589             }
2590             postNotificationIfNecessary(NOTIFICATION_TYPE_ABUSIVE_CURRENT_DRAIN,
2591                     com.android.internal.R.string.notification_title_abusive_bg_apps,
2592                     com.android.internal.R.string.notification_content_abusive_bg_apps,
2593                     pendingIntent, packageName, uid, actions);
2594         }
2595 
2596         void postLongRunningFgsIfNecessary(String packageName, int uid) {
2597             // Log the event in statsd.
2598             FrameworkStatsLog.write(FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO,
2599                     uid,
2600                     mBgController.getRestrictionLevel(uid),
2601                     AppBackgroundRestrictionsInfo.THRESHOLD_UNKNOWN,
2602                     AppBackgroundRestrictionsInfo.FGS_TRACKER,
2603                     mInjector.getAppFGSTracker().getTrackerInfoForStatsd(uid),
2604                     null, // BatteryTrackerInfo
2605                     null, // BroadcastEventsTrackerInfo
2606                     null, // BindServiceEventsTrackerInfo
2607                     getExemptionReasonForStatsd(
2608                             mBgController.getBackgroundRestrictionExemptionReason(uid)),
2609                     AppBackgroundRestrictionsInfo.UNKNOWN, // OptimizationLevel
2610                     AppBackgroundRestrictionsInfo.SDK_UNKNOWN, // TargetSdk
2611                     ActivityManager.isLowRamDeviceStatic(),
2612                     mBgController.getRestrictionLevel(uid));
2613             PendingIntent pendingIntent;
2614             if (!mBgController.mConstantsObserver.mBgPromptFgsOnLongRunning) {
2615                 if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2616                     Slog.i(TAG, "Long-running FGS prompt is disabled.");
2617                 }
2618                 return;
2619             }
2620             if (!mBgController.mConstantsObserver.mBgPromptFgsWithNotiOnLongRunning
2621                     && mBgController.hasForegroundServiceNotifications(packageName, uid)) {
2622                 if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2623                     Slog.i(TAG, "Not prompt long-running due to FGS with notification");
2624                 }
2625                 return;
2626             }
2627             if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER) {
2628                 final Intent intent = new Intent(ACTION_SHOW_FOREGROUND_SERVICE_MANAGER);
2629                 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
2630                 // Task manager runs in SystemUI, which is SYSTEM user only.
2631                 pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0,
2632                         intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE,
2633                         UserHandle.SYSTEM);
2634             } else {
2635                 final Intent intent = new Intent(Settings.ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL);
2636                 intent.setData(Uri.fromParts(PACKAGE_SCHEME, packageName, null));
2637                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2638                 pendingIntent = PendingIntent.getActivityAsUser(mContext, 0,
2639                         intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE,
2640                         null, UserHandle.of(UserHandle.getUserId(uid)));
2641             }
2642 
2643             postNotificationIfNecessary(NOTIFICATION_TYPE_LONG_RUNNING_FGS,
2644                     com.android.internal.R.string.notification_title_long_running_fgs,
2645                     com.android.internal.R.string.notification_content_long_running_fgs,
2646                     pendingIntent, packageName, uid, null);
2647         }
2648 
2649         long getNotificationMinInterval(@NotificationType int notificationType) {
2650             switch (notificationType) {
2651                 case NOTIFICATION_TYPE_ABUSIVE_CURRENT_DRAIN:
2652                     return mBgController.mConstantsObserver.mBgAbusiveNotificationMinIntervalMs;
2653                 case NOTIFICATION_TYPE_LONG_RUNNING_FGS:
2654                     return mBgController.mConstantsObserver.mBgLongFgsNotificationMinIntervalMs;
2655                 default:
2656                     return 0L;
2657             }
2658         }
2659 
2660         int getNotificationIdIfNecessary(@NotificationType int notificationType,
2661                 String packageName, int uid) {
2662             synchronized (mSettingsLock) {
2663                 final RestrictionSettings.PkgSettings settings = mBgController.mRestrictionSettings
2664                         .getRestrictionSettingsLocked(uid, packageName);
2665                 if (settings == null) {
2666                     return 0;
2667                 }
2668 
2669                 final long now = mInjector.currentTimeMillis();
2670                 final long lastNotificationShownTime =
2671                         settings.getLastNotificationTime(notificationType);
2672                 if (lastNotificationShownTime != 0 && (lastNotificationShownTime
2673                         + getNotificationMinInterval(notificationType) > now)) {
2674                     if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2675                         Slog.i(TAG, "Not showing notification as last notification was shown "
2676                                 + TimeUtils.formatDuration(now - lastNotificationShownTime)
2677                                 + " ago");
2678                     }
2679                     return 0;
2680                 }
2681                 settings.setLastNotificationTime(notificationType, now);
2682                 int notificationId = settings.getNotificationId(notificationType);
2683                 if (notificationId <= 0) {
2684                     notificationId = mNotificationIDStepper++;
2685                     settings.setNotificationId(notificationType, notificationId);
2686                 }
2687                 if (DEBUG_BG_RESTRICTION_CONTROLLER) {
2688                     Slog.i(TAG, "Showing notification for " + packageName
2689                             + "/" + UserHandle.formatUid(uid)
2690                             + ", id=" + notificationId
2691                             + ", now=" + now
2692                             + ", lastShown=" + lastNotificationShownTime);
2693                 }
2694                 return notificationId;
2695             }
2696         }
2697 
2698         void postNotificationIfNecessary(@NotificationType int notificationType, int titleRes,
2699                 int messageRes, PendingIntent pendingIntent, String packageName, int uid,
2700                 @Nullable Notification.Action[] actions) {
2701             int notificationId = getNotificationIdIfNecessary(notificationType, packageName, uid);
2702             if (notificationId <= 0) {
2703                 return;
2704             }
2705 
2706             final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
2707             final PackageManager pm = mInjector.getPackageManager();
2708             final ApplicationInfo ai = pmi.getApplicationInfo(packageName, STOCK_PM_FLAGS,
2709                     SYSTEM_UID, UserHandle.getUserId(uid));
2710             final String title = mContext.getString(titleRes);
2711             final String message = mContext.getString(messageRes,
2712                     ai != null ? ai.loadLabel(pm) : packageName);
2713             final Icon icon = ai != null ? Icon.createWithResource(packageName, ai.icon) : null;
2714 
2715             postNotification(notificationId, packageName, uid, title, message, icon, pendingIntent,
2716                     actions);
2717         }
2718 
2719         void postNotification(int notificationId, String packageName, int uid, String title,
2720                 String message, Icon icon, PendingIntent pendingIntent,
2721                 @Nullable Notification.Action[] actions) {
2722             final UserHandle targetUser = UserHandle.of(UserHandle.getUserId(uid));
2723             postSummaryNotification(targetUser);
2724 
2725             final Notification.Builder notificationBuilder = new Notification.Builder(mContext,
2726                     ABUSIVE_BACKGROUND_APPS)
2727                     .setAutoCancel(true)
2728                     .setGroup(GROUP_KEY)
2729                     .setWhen(mInjector.currentTimeMillis())
2730                     .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
2731                     .setColor(mContext.getColor(
2732                             com.android.internal.R.color.system_notification_accent_color))
2733                     .setContentTitle(title)
2734                     .setContentText(message)
2735                     .setContentIntent(pendingIntent);
2736             if (icon != null) {
2737                 notificationBuilder.setLargeIcon(icon);
2738             }
2739             if (actions != null) {
2740                 for (Notification.Action action : actions) {
2741                     notificationBuilder.addAction(action);
2742                 }
2743             }
2744 
2745             final Notification notification = notificationBuilder.build();
2746             // Remember the package name for testing.
2747             notification.extras.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
2748 
2749             mNotificationManager.notifyAsUser(null, notificationId, notification, targetUser);
2750         }
2751 
2752         private void postSummaryNotification(@NonNull UserHandle targetUser) {
2753             final Notification summary = new Notification.Builder(mContext,
2754                     ABUSIVE_BACKGROUND_APPS)
2755                     .setGroup(GROUP_KEY)
2756                     .setGroupSummary(true)
2757                     .setStyle(new Notification.BigTextStyle())
2758                     .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
2759                     .setColor(mContext.getColor(
2760                             com.android.internal.R.color.system_notification_accent_color))
2761                     .build();
2762             mNotificationManager.notifyAsUser(null, SUMMARY_NOTIFICATION_ID, summary, targetUser);
2763         }
2764 
2765         void cancelRequestBgRestrictedIfNecessary(String packageName, int uid) {
2766             synchronized (mSettingsLock) {
2767                 final RestrictionSettings.PkgSettings settings = mBgController.mRestrictionSettings
2768                         .getRestrictionSettingsLocked(uid, packageName);
2769                 if (settings != null) {
2770                     final int notificationId =
2771                             settings.getNotificationId(NOTIFICATION_TYPE_ABUSIVE_CURRENT_DRAIN);
2772                     if (notificationId > 0) {
2773                         mNotificationManager.cancel(notificationId);
2774                     }
2775                 }
2776             }
2777         }
2778 
2779         void cancelLongRunningFGSNotificationIfNecessary(String packageName, int uid) {
2780             synchronized (mSettingsLock) {
2781                 final RestrictionSettings.PkgSettings settings = mBgController.mRestrictionSettings
2782                         .getRestrictionSettingsLocked(uid, packageName);
2783                 if (settings != null) {
2784                     final int notificationId =
2785                             settings.getNotificationId(NOTIFICATION_TYPE_LONG_RUNNING_FGS);
2786                     if (notificationId > 0) {
2787                         mNotificationManager.cancel(notificationId);
2788                     }
2789                 }
2790             }
2791         }
2792     }
2793 
2794     void handleUidInactive(int uid, boolean disabled) {
2795         final ArrayList<Runnable> pendingTasks = mTmpRunnables;
2796         synchronized (mSettingsLock) {
2797             final int index = mActiveUids.indexOfKey(uid);
2798             if (index < 0) {
2799                 return;
2800             }
2801             final int numPackages = mActiveUids.numElementsForKeyAt(index);
2802             for (int i = 0; i < numPackages; i++) {
2803                 final Runnable pendingTask = mActiveUids.valueAt(index, i);
2804                 if (pendingTask != null) {
2805                     pendingTasks.add(pendingTask);
2806                 }
2807             }
2808             mActiveUids.deleteAt(index);
2809         }
2810         for (int i = 0, size = pendingTasks.size(); i < size; i++) {
2811             pendingTasks.get(i).run();
2812         }
2813         pendingTasks.clear();
2814     }
2815 
2816     void handleUidActive(int uid) {
2817         synchronized (mSettingsLock) {
2818             final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
2819             final int userId = UserHandle.getUserId(uid);
2820             mRestrictionSettings.forEachPackageInUidLocked(uid, (pkgName, level, reason) -> {
2821                 if (mConstantsObserver.mBgAutoRestrictedBucket
2822                         && level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
2823                     mActiveUids.add(uid, pkgName, () -> appStandbyInternal.restrictApp(pkgName,
2824                             userId, reason & REASON_MAIN_MASK, reason & REASON_SUB_MASK));
2825                 } else {
2826                     mActiveUids.add(uid, pkgName, null);
2827                 }
2828             });
2829         }
2830     }
2831 
2832     boolean isOnDeviceIdleAllowlist(int uid) {
2833         final int appId = UserHandle.getAppId(uid);
2834 
2835         return Arrays.binarySearch(mDeviceIdleAllowlist, appId) >= 0
2836                 || Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, appId) >= 0;
2837     }
2838 
2839     boolean isOnSystemDeviceIdleAllowlist(int uid) {
2840         final int appId = UserHandle.getAppId(uid);
2841 
2842         return mSystemDeviceIdleAllowlist.contains(appId)
2843                 || mSystemDeviceIdleExceptIdleAllowlist.contains(appId);
2844     }
2845 
2846     void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids) {
2847         mDeviceIdleAllowlist = allAppids;
2848         mDeviceIdleExceptIdleAllowlist = exceptIdleAppids;
2849     }
2850 
2851     /**
2852      * @return The reason code of whether or not the given UID should be exempted from background
2853      * restrictions here.
2854      *
2855      * <p>
2856      * Note: Call it with caution as it'll try to acquire locks in other services.
2857      * </p>
2858      */
2859     @ReasonCode
2860     int getBackgroundRestrictionExemptionReason(int uid) {
2861         @ReasonCode int reason = getPotentialSystemExemptionReason(uid);
2862         if (reason != REASON_DENIED) {
2863             return reason;
2864         }
2865         final String[] packages = mInjector.getPackageManager().getPackagesForUid(uid);
2866         if (packages != null) {
2867             // Check each packages to see if any of them is in the "fixed" exemption cases.
2868             for (String pkg : packages) {
2869                 reason = getPotentialSystemExemptionReason(uid, pkg);
2870                 if (reason != REASON_DENIED) {
2871                     return reason;
2872                 }
2873             }
2874             // Loop the packages again, and check the user-configurable exemptions.
2875             for (String pkg : packages) {
2876                 reason = getPotentialUserAllowedExemptionReason(uid, pkg);
2877                 if (reason != REASON_DENIED) {
2878                     return reason;
2879                 }
2880             }
2881         }
2882         return REASON_DENIED;
2883     }
2884 
2885     /**
2886      * @param uid The uid to check.
2887      * @return The potential exemption reason of the given uid. The caller must decide
2888      * whether or not it should be exempted.
2889      */
2890     @ReasonCode
2891     int getPotentialSystemExemptionReason(int uid) {
2892         if (UserHandle.isCore(uid)) {
2893             return REASON_SYSTEM_UID;
2894         }
2895         if (isOnSystemDeviceIdleAllowlist(uid)) {
2896             return REASON_SYSTEM_ALLOW_LISTED;
2897         }
2898         if (UserManager.isDeviceInDemoMode(mContext)) {
2899             return REASON_DEVICE_DEMO_MODE;
2900         }
2901         final int userId = UserHandle.getUserId(uid);
2902         if (mInjector.getUserManagerInternal()
2903                 .hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)) {
2904             return REASON_DISALLOW_APPS_CONTROL;
2905         }
2906         final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
2907         if (am.isDeviceOwner(uid)) {
2908             return REASON_DEVICE_OWNER;
2909         }
2910         if (am.isProfileOwner(uid)) {
2911             return REASON_PROFILE_OWNER;
2912         }
2913         final int uidProcState = am.getUidProcessState(uid);
2914         if (uidProcState <= PROCESS_STATE_PERSISTENT) {
2915             return REASON_PROC_STATE_PERSISTENT;
2916         } else if (uidProcState <= PROCESS_STATE_PERSISTENT_UI) {
2917             return REASON_PROC_STATE_PERSISTENT_UI;
2918         }
2919         return REASON_DENIED;
2920     }
2921 
2922     /**
2923      * @param uid The uid to check.
2924      * @param pkgName The package name to check.
2925      * @return The potential system-fixed exemption reason of the given uid/package. The caller
2926      * must decide whether or not it should be exempted.
2927      */
2928     @ReasonCode
2929     int getPotentialSystemExemptionReason(int uid, String pkg) {
2930         final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
2931         final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
2932         final AppOpsManager appOpsManager = mInjector.getAppOpsManager();
2933         final ActivityManagerService activityManagerService = mInjector.getActivityManagerService();
2934         final int userId = UserHandle.getUserId(uid);
2935         if (isSystemModule(pkg)) {
2936             return REASON_SYSTEM_MODULE;
2937         } else if (isCarrierApp(pkg)) {
2938             return REASON_CARRIER_PRIVILEGED_APP;
2939         } else if (isExemptedFromSysConfig(pkg)) {
2940             return REASON_SYSTEM_ALLOW_LISTED;
2941         } else if (mConstantsObserver.mBgRestrictionExemptedPackages.contains(pkg)) {
2942             return REASON_SYSTEM_ALLOW_LISTED;
2943         } else if (pm.isPackageStateProtected(pkg, userId)) {
2944             return REASON_DPO_PROTECTED_APP;
2945         } else if (appStandbyInternal.isActiveDeviceAdmin(pkg, userId)) {
2946             return REASON_ACTIVE_DEVICE_ADMIN;
2947         } else if (activityManagerService.mConstants.mFlagSystemExemptPowerRestrictionsEnabled
2948                 && appOpsManager.checkOpNoThrow(
2949                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, uid, pkg)
2950                 == AppOpsManager.MODE_ALLOWED) {
2951             return REASON_SYSTEM_EXEMPT_APP_OP;
2952         }
2953         return REASON_DENIED;
2954     }
2955 
2956     /**
2957      * @param uid The uid to check.
2958      * @param pkgName The package name to check.
2959      * @return The potential user-allowed exemption reason of the given uid/package. The caller
2960      * must decide whether or not it should be exempted.
2961      */
2962     @ReasonCode
2963     int getPotentialUserAllowedExemptionReason(int uid, String pkg) {
2964         final AppOpsManager appOpsManager = mInjector.getAppOpsManager();
2965         if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN,
2966                 uid, pkg) == AppOpsManager.MODE_ALLOWED) {
2967             return REASON_OP_ACTIVATE_VPN;
2968         } else if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN,
2969                 uid, pkg) == AppOpsManager.MODE_ALLOWED) {
2970             return REASON_OP_ACTIVATE_PLATFORM_VPN;
2971         }
2972         if (isRoleHeldByUid(RoleManager.ROLE_DIALER, uid)) {
2973             return REASON_ROLE_DIALER;
2974         }
2975         if (isRoleHeldByUid(RoleManager.ROLE_EMERGENCY, uid)) {
2976             return REASON_ROLE_EMERGENCY;
2977         }
2978         if (isOnDeviceIdleAllowlist(uid)) {
2979             return REASON_ALLOWLISTED_PACKAGE;
2980         }
2981         final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
2982         if (am.isAssociatedCompanionApp(UserHandle.getUserId(uid), uid)) {
2983             return REASON_COMPANION_DEVICE_MANAGER;
2984         }
2985         return REASON_DENIED;
2986     }
2987 
2988     private boolean isCarrierApp(String packageName) {
2989         synchronized (mCarrierPrivilegedLock) {
2990             if (mCarrierPrivilegedApps != null) {
2991                 for (int i = mCarrierPrivilegedApps.size() - 1; i >= 0; i--) {
2992                     if (mCarrierPrivilegedApps.valueAt(i).contains(packageName)) {
2993                         return true;
2994                     }
2995                 }
2996             }
2997             return false;
2998         }
2999     }
3000 
3001     private void registerCarrierPrivilegesCallbacks() {
3002         final TelephonyManager telephonyManager = mInjector.getTelephonyManager();
3003         if (telephonyManager == null) {
3004             return;
3005         }
3006 
3007         final int numPhones = telephonyManager.getActiveModemCount();
3008         final ArrayList<PhoneCarrierPrivilegesCallback> callbacks = new ArrayList<>();
3009         for (int i = 0; i < numPhones; i++) {
3010             final PhoneCarrierPrivilegesCallback callback = new PhoneCarrierPrivilegesCallback(i);
3011             callbacks.add(callback);
3012             telephonyManager.registerCarrierPrivilegesCallback(i, mExecutor, callback);
3013         }
3014         mCarrierPrivilegesCallbacks = callbacks;
3015     }
3016 
3017     private void unregisterCarrierPrivilegesCallbacks() {
3018         final TelephonyManager telephonyManager = mInjector.getTelephonyManager();
3019         if (telephonyManager == null) {
3020             return;
3021         }
3022         final ArrayList<PhoneCarrierPrivilegesCallback> callbacks = mCarrierPrivilegesCallbacks;
3023         if (callbacks != null) {
3024             for (int i = callbacks.size() - 1; i >= 0; i--) {
3025                 telephonyManager.unregisterCarrierPrivilegesCallback(callbacks.get(i));
3026             }
3027             mCarrierPrivilegesCallbacks = null;
3028         }
3029     }
3030 
3031     private class PhoneCarrierPrivilegesCallback implements CarrierPrivilegesCallback {
3032         private final int mPhoneId;
3033 
3034         PhoneCarrierPrivilegesCallback(int phoneId) {
3035             mPhoneId = phoneId;
3036         }
3037 
3038         @Override
3039         public void onCarrierPrivilegesChanged(@NonNull Set<String> privilegedPackageNames,
3040                 @NonNull Set<Integer> privilegedUids) {
3041             synchronized (mCarrierPrivilegedLock) {
3042                 mCarrierPrivilegedApps.put(mPhoneId,
3043                         Collections.unmodifiableSet(privilegedPackageNames));
3044             }
3045         }
3046     }
3047 
3048     private boolean isRoleHeldByUid(@NonNull String roleName, int uid) {
3049         synchronized (mLock) {
3050             final ArrayList<String> roles = mUidRolesMapping.get(uid);
3051             return roles != null && roles.indexOf(roleName) >= 0;
3052         }
3053     }
3054 
3055     private void initRolesInInterest() {
3056         final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
3057         for (String role : ROLES_IN_INTEREST) {
3058             if (mInjector.getRoleManager().isRoleAvailable(role)) {
3059                 for (int userId : allUsers) {
3060                     final UserHandle user = UserHandle.of(userId);
3061                     onRoleHoldersChanged(role, user);
3062                 }
3063             }
3064         }
3065     }
3066 
3067     private void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
3068         final List<String> rolePkgs = mInjector.getRoleManager().getRoleHoldersAsUser(
3069                 roleName, user);
3070         final ArraySet<Integer> roleUids = new ArraySet<>();
3071         final int userId = user.getIdentifier();
3072         if (rolePkgs != null) {
3073             final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
3074             for (String pkg: rolePkgs) {
3075                 roleUids.add(pm.getPackageUid(pkg, STOCK_PM_FLAGS, userId));
3076             }
3077         }
3078         synchronized (mLock) {
3079             for (int i = mUidRolesMapping.size() - 1; i >= 0; i--) {
3080                 final int uid = mUidRolesMapping.keyAt(i);
3081                 if (UserHandle.getUserId(uid) != userId) {
3082                     continue;
3083                 }
3084                 final ArrayList<String> roles = mUidRolesMapping.valueAt(i);
3085                 final int index = roles.indexOf(roleName);
3086                 final boolean isRole = roleUids.contains(uid);
3087                 if (index >= 0) {
3088                     if (!isRole) { // Not holding this role anymore, remove it.
3089                         roles.remove(index);
3090                         if (roles.isEmpty()) {
3091                             mUidRolesMapping.removeAt(i);
3092                         }
3093                     }
3094                 } else if (isRole) { // Got this new role, add it.
3095                     roles.add(roleName);
3096                     roleUids.remove(uid);
3097                 }
3098             }
3099             for (int i = roleUids.size() - 1; i >= 0; i--) { // Take care of the leftovers.
3100                 final ArrayList<String> roles = new ArrayList<>();
3101                 roles.add(roleName);
3102                 mUidRolesMapping.put(roleUids.valueAt(i), roles);
3103             }
3104         }
3105     }
3106 
3107     /**
3108      * @return The background handler of this controller.
3109      */
3110     Handler getBackgroundHandler() {
3111         return mBgHandler;
3112     }
3113 
3114     /**
3115      * @return The background handler thread of this controller.
3116      */
3117     @VisibleForTesting
3118     HandlerThread getBackgroundHandlerThread() {
3119         return mBgHandlerThread;
3120     }
3121 
3122     /**
3123      * @return The global lock of this controller.
3124      */
3125     Object getLock() {
3126         return mLock;
3127     }
3128 
3129     @VisibleForTesting
3130     void addAppStateTracker(@NonNull BaseAppStateTracker tracker) {
3131         mAppStateTrackers.add(tracker);
3132     }
3133 
3134     /**
3135      * @return The tracker instance of the given class.
3136      */
3137     <T extends BaseAppStateTracker> T getAppStateTracker(Class<T> trackerClass) {
3138         for (BaseAppStateTracker tracker : mAppStateTrackers) {
3139             if (trackerClass.isAssignableFrom(tracker.getClass())) {
3140                 return (T) tracker;
3141             }
3142         }
3143         return null;
3144     }
3145 
3146     void postLongRunningFgsIfNecessary(String packageName, int uid) {
3147         mNotificationHelper.postLongRunningFgsIfNecessary(packageName, uid);
3148     }
3149 
3150     void cancelLongRunningFGSNotificationIfNecessary(String packageName, int uid) {
3151         mNotificationHelper.cancelLongRunningFGSNotificationIfNecessary(packageName, uid);
3152     }
3153 
3154     String getPackageName(int pid) {
3155         return mInjector.getPackageName(pid);
3156     }
3157 
3158     static class BgHandler extends Handler {
3159         static final int MSG_BACKGROUND_RESTRICTION_CHANGED = 0;
3160         static final int MSG_APP_RESTRICTION_LEVEL_CHANGED = 1;
3161         static final int MSG_APP_STANDBY_BUCKET_CHANGED = 2;
3162         static final int MSG_USER_INTERACTION_STARTED = 3;
3163         static final int MSG_REQUEST_BG_RESTRICTED = 4;
3164         static final int MSG_UID_IDLE = 5;
3165         static final int MSG_UID_ACTIVE = 6;
3166         static final int MSG_UID_GONE = 7;
3167         static final int MSG_UID_PROC_STATE_CHANGED = 8;
3168         static final int MSG_CANCEL_REQUEST_BG_RESTRICTED = 9;
3169         static final int MSG_LOAD_RESTRICTION_SETTINGS = 10;
3170         static final int MSG_PERSIST_RESTRICTION_SETTINGS = 11;
3171 
3172         private final Injector mInjector;
3173 
3174         BgHandler(Looper looper, Injector injector) {
3175             super(looper);
3176             mInjector = injector;
3177         }
3178 
3179         @Override
3180         public void handleMessage(Message msg) {
3181             final AppRestrictionController c = mInjector
3182                     .getAppRestrictionController();
3183             switch (msg.what) {
3184                 case MSG_BACKGROUND_RESTRICTION_CHANGED: {
3185                     c.handleBackgroundRestrictionChanged(msg.arg1, (String) msg.obj, msg.arg2 == 1);
3186                 } break;
3187                 case MSG_APP_RESTRICTION_LEVEL_CHANGED: {
3188                     c.dispatchAppRestrictionLevelChanges(msg.arg1, (String) msg.obj, msg.arg2);
3189                 } break;
3190                 case MSG_APP_STANDBY_BUCKET_CHANGED: {
3191                     c.handleAppStandbyBucketChanged(msg.arg2, (String) msg.obj, msg.arg1);
3192                 } break;
3193                 case MSG_USER_INTERACTION_STARTED: {
3194                     c.onUserInteractionStarted((String) msg.obj, msg.arg1);
3195                 } break;
3196                 case MSG_REQUEST_BG_RESTRICTED: {
3197                     c.handleRequestBgRestricted((String) msg.obj, msg.arg1);
3198                 } break;
3199                 case MSG_UID_IDLE: {
3200                     c.handleUidInactive(msg.arg1, msg.arg2 == 1);
3201                 } break;
3202                 case MSG_UID_ACTIVE: {
3203                     c.handleUidActive(msg.arg1);
3204                 } break;
3205                 case MSG_CANCEL_REQUEST_BG_RESTRICTED: {
3206                     c.handleCancelRequestBgRestricted((String) msg.obj, msg.arg1);
3207                 } break;
3208                 case MSG_UID_PROC_STATE_CHANGED: {
3209                     c.handleUidProcStateChanged(msg.arg1, msg.arg2);
3210                 } break;
3211                 case MSG_UID_GONE: {
3212                     // It also means this UID is inactive now.
3213                     c.handleUidInactive(msg.arg1, msg.arg2 == 1);
3214                     c.handleUidGone(msg.arg1);
3215                 } break;
3216                 case MSG_LOAD_RESTRICTION_SETTINGS: {
3217                     c.mRestrictionSettings.loadFromXml(true);
3218                 } break;
3219                 case MSG_PERSIST_RESTRICTION_SETTINGS: {
3220                     c.mRestrictionSettings.persistToXml(msg.arg1);
3221                 } break;
3222             }
3223         }
3224     }
3225 
3226     static class Injector {
3227         private final Context mContext;
3228         private ActivityManagerInternal mActivityManagerInternal;
3229         private AppRestrictionController mAppRestrictionController;
3230         private AppOpsManager mAppOpsManager;
3231         private AppStandbyInternal mAppStandbyInternal;
3232         private AppStateTracker mAppStateTracker;
3233         private AppHibernationManagerInternal mAppHibernationInternal;
3234         private IActivityManager mIActivityManager;
3235         private UserManagerInternal mUserManagerInternal;
3236         private PackageManagerInternal mPackageManagerInternal;
3237         private NotificationManager mNotificationManager;
3238         private RoleManager mRoleManager;
3239         private AppBatteryTracker mAppBatteryTracker;
3240         private AppBatteryExemptionTracker mAppBatteryExemptionTracker;
3241         private AppFGSTracker mAppFGSTracker;
3242         private AppMediaSessionTracker mAppMediaSessionTracker;
3243         private AppPermissionTracker mAppPermissionTracker;
3244         private TelephonyManager mTelephonyManager;
3245 
3246         Injector(Context context) {
3247             mContext = context;
3248         }
3249 
3250         Context getContext() {
3251             return mContext;
3252         }
3253 
3254         void initAppStateTrackers(AppRestrictionController controller) {
3255             mAppRestrictionController = controller;
3256             mAppBatteryTracker = new AppBatteryTracker(mContext, controller);
3257             mAppBatteryExemptionTracker = new AppBatteryExemptionTracker(mContext, controller);
3258             mAppFGSTracker = new AppFGSTracker(mContext, controller);
3259             mAppMediaSessionTracker = new AppMediaSessionTracker(mContext, controller);
3260             mAppPermissionTracker = new AppPermissionTracker(mContext, controller);
3261             controller.mAppStateTrackers.add(mAppBatteryTracker);
3262             controller.mAppStateTrackers.add(mAppBatteryExemptionTracker);
3263             controller.mAppStateTrackers.add(mAppFGSTracker);
3264             controller.mAppStateTrackers.add(mAppMediaSessionTracker);
3265             controller.mAppStateTrackers.add(mAppPermissionTracker);
3266             controller.mAppStateTrackers.add(new AppBroadcastEventsTracker(mContext, controller));
3267             controller.mAppStateTrackers.add(new AppBindServiceEventsTracker(mContext, controller));
3268         }
3269 
3270         ActivityManagerInternal getActivityManagerInternal() {
3271             if (mActivityManagerInternal == null) {
3272                 mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
3273             }
3274             return mActivityManagerInternal;
3275         }
3276 
3277         AppRestrictionController getAppRestrictionController() {
3278             return mAppRestrictionController;
3279         }
3280 
3281         AppOpsManager getAppOpsManager() {
3282             if (mAppOpsManager == null) {
3283                 mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
3284             }
3285             return mAppOpsManager;
3286         }
3287 
3288         AppStandbyInternal getAppStandbyInternal() {
3289             if (mAppStandbyInternal == null) {
3290                 mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
3291             }
3292             return mAppStandbyInternal;
3293         }
3294 
3295         AppHibernationManagerInternal getAppHibernationInternal() {
3296             if (mAppHibernationInternal == null) {
3297                 mAppHibernationInternal = LocalServices.getService(
3298                         AppHibernationManagerInternal.class);
3299             }
3300             return mAppHibernationInternal;
3301         }
3302 
3303         AppStateTracker getAppStateTracker() {
3304             if (mAppStateTracker == null) {
3305                 mAppStateTracker = LocalServices.getService(AppStateTracker.class);
3306             }
3307             return mAppStateTracker;
3308         }
3309 
3310         IActivityManager getIActivityManager() {
3311             return ActivityManager.getService();
3312         }
3313 
3314         UserManagerInternal getUserManagerInternal() {
3315             if (mUserManagerInternal == null) {
3316                 mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
3317             }
3318             return mUserManagerInternal;
3319         }
3320 
3321         PackageManagerInternal getPackageManagerInternal() {
3322             if (mPackageManagerInternal == null) {
3323                 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
3324             }
3325             return mPackageManagerInternal;
3326         }
3327 
3328         PackageManager getPackageManager() {
3329             return getContext().getPackageManager();
3330         }
3331 
3332         NotificationManager getNotificationManager() {
3333             if (mNotificationManager == null) {
3334                 mNotificationManager = getContext().getSystemService(NotificationManager.class);
3335             }
3336             return mNotificationManager;
3337         }
3338 
3339         RoleManager getRoleManager() {
3340             if (mRoleManager == null) {
3341                 mRoleManager = getContext().getSystemService(RoleManager.class);
3342             }
3343             return mRoleManager;
3344         }
3345 
3346         TelephonyManager getTelephonyManager() {
3347             if (mTelephonyManager == null) {
3348                 mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
3349             }
3350             return mTelephonyManager;
3351         }
3352 
3353         AppFGSTracker getAppFGSTracker() {
3354             return mAppFGSTracker;
3355         }
3356 
3357         AppMediaSessionTracker getAppMediaSessionTracker() {
3358             return mAppMediaSessionTracker;
3359         }
3360 
3361         ActivityManagerService getActivityManagerService() {
3362             return mAppRestrictionController.mActivityManagerService;
3363         }
3364 
3365         UidBatteryUsageProvider getUidBatteryUsageProvider() {
3366             return mAppBatteryTracker;
3367         }
3368 
3369         AppBatteryExemptionTracker getAppBatteryExemptionTracker() {
3370             return mAppBatteryExemptionTracker;
3371         }
3372 
3373         AppPermissionTracker getAppPermissionTracker() {
3374             return mAppPermissionTracker;
3375         }
3376 
3377         String getPackageName(int pid) {
3378             final ActivityManagerService am = getActivityManagerService();
3379             final ProcessRecord app;
3380             synchronized (am.mPidsSelfLocked) {
3381                 app = am.mPidsSelfLocked.get(pid);
3382                 if (app != null) {
3383                     final ApplicationInfo ai = app.info;
3384                     if (ai != null) {
3385                         return ai.packageName;
3386                     }
3387                 }
3388             }
3389             return null;
3390         }
3391 
3392         void scheduleInitTrackers(Handler handler, Runnable initializers) {
3393             handler.post(initializers);
3394         }
3395 
3396         File getDataSystemDeDirectory(@UserIdInt int userId) {
3397             return Environment.getDataSystemDeDirectory(userId);
3398         }
3399 
3400         @CurrentTimeMillisLong long currentTimeMillis() {
3401             return System.currentTimeMillis();
3402         }
3403 
3404         Handler getDefaultHandler() {
3405             return mAppRestrictionController.mActivityManagerService.mHandler;
3406         }
3407 
3408         boolean isTest() {
3409             return false;
3410         }
3411     }
3412 
3413     private void registerForSystemBroadcasts() {
3414         final IntentFilter packageFilter = new IntentFilter();
3415         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
3416         packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
3417         packageFilter.addDataScheme("package");
3418         mContext.registerReceiverForAllUsers(mBroadcastReceiver, packageFilter, null, mBgHandler);
3419         final IntentFilter userFilter = new IntentFilter();
3420         userFilter.addAction(Intent.ACTION_USER_ADDED);
3421         userFilter.addAction(Intent.ACTION_USER_REMOVED);
3422         userFilter.addAction(Intent.ACTION_UID_REMOVED);
3423         mContext.registerReceiverForAllUsers(mBroadcastReceiver, userFilter, null, mBgHandler);
3424         final IntentFilter bootFilter = new IntentFilter();
3425         bootFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
3426         mContext.registerReceiverAsUser(mBootReceiver, UserHandle.SYSTEM,
3427                 bootFilter, null, mBgHandler);
3428         final IntentFilter telFilter = new IntentFilter(
3429                 TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED);
3430         mContext.registerReceiverForAllUsers(mBroadcastReceiver, telFilter, null, mBgHandler);
3431     }
3432 
3433     private void unregisterForSystemBroadcasts() {
3434         mContext.unregisterReceiver(mBroadcastReceiver);
3435         mContext.unregisterReceiver(mBootReceiver);
3436     }
3437 
3438     void forEachTracker(Consumer<BaseAppStateTracker> sink) {
3439         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3440             sink.accept(mAppStateTrackers.get(i));
3441         }
3442     }
3443 
3444     private void onUserAdded(@UserIdInt int userId) {
3445         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3446             mAppStateTrackers.get(i).onUserAdded(userId);
3447         }
3448     }
3449 
3450     private void onUserStarted(@UserIdInt int userId) {
3451         refreshAppRestrictionLevelForUser(userId, REASON_MAIN_FORCED_BY_USER,
3452                 REASON_SUB_FORCED_USER_FLAG_INTERACTION);
3453         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3454             mAppStateTrackers.get(i).onUserStarted(userId);
3455         }
3456     }
3457 
3458     private void onUserStopped(@UserIdInt int userId) {
3459         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3460             mAppStateTrackers.get(i).onUserStopped(userId);
3461         }
3462     }
3463 
3464     private void onUserRemoved(@UserIdInt int userId) {
3465         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3466             mAppStateTrackers.get(i).onUserRemoved(userId);
3467         }
3468         mRestrictionSettings.removeUser(userId);
3469     }
3470 
3471     private void onUidAdded(int uid) {
3472         refreshAppRestrictionLevelForUid(uid, REASON_MAIN_FORCED_BY_SYSTEM,
3473                 REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED, false);
3474         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3475             mAppStateTrackers.get(i).onUidAdded(uid);
3476         }
3477     }
3478 
3479     private void onPackageRemoved(String pkgName, int uid) {
3480         mRestrictionSettings.removePackage(pkgName, uid);
3481     }
3482 
3483     private void onUidRemoved(int uid) {
3484         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3485             mAppStateTrackers.get(i).onUidRemoved(uid);
3486         }
3487         mRestrictionSettings.removeUid(uid);
3488     }
3489 
3490     private void onLockedBootCompleted() {
3491         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3492             mAppStateTrackers.get(i).onLockedBootCompleted();
3493         }
3494         mLockedBootCompleted = true;
3495     }
3496 
3497     boolean isBgAutoRestrictedBucketFeatureFlagEnabled() {
3498         return mConstantsObserver.mBgAutoRestrictedBucket;
3499     }
3500 
3501     private void onPropertiesChanged(String name) {
3502         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3503             mAppStateTrackers.get(i).onPropertiesChanged(name);
3504         }
3505     }
3506 
3507     private void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
3508         final int uid = mInjector.getPackageManagerInternal()
3509                 .getPackageUid(packageName, STOCK_PM_FLAGS, userId);
3510         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
3511             mAppStateTrackers.get(i).onUserInteractionStarted(packageName, uid);
3512         }
3513     }
3514 }
3515