• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.car.watchdog;
18 
19 import static android.app.StatsManager.PULL_SKIP;
20 import static android.app.StatsManager.PULL_SUCCESS;
21 import static android.car.builtin.os.UserManagerHelper.USER_NULL;
22 import static android.car.settings.CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE;
23 import static android.car.watchdog.CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO;
24 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_CURRENT_DAY;
25 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_15_DAYS;
26 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_30_DAYS;
27 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_3_DAYS;
28 import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS;
29 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NEVER;
30 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NO;
31 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_YES;
32 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
33 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
34 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
35 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
36 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
37 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
38 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
39 import static android.os.Process.INVALID_UID;
40 import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
41 
42 import static com.android.car.CarServiceUtils.getContentResolverForUser;
43 import static com.android.car.CarServiceUtils.getHandlerThread;
44 import static com.android.car.CarStatsLog.CAR_WATCHDOG_IO_OVERUSE_STATS_REPORTED;
45 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED;
46 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__KILL_REASON__KILLED_ON_IO_OVERUSE;
47 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__GARAGE_MODE;
48 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_INTERACTION_MODE;
49 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE;
50 import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__UID_STATE__UNKNOWN_UID_STATE;
51 import static com.android.car.CarStatsLog.CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY;
52 import static com.android.car.CarStatsLog.CAR_WATCHDOG_UID_IO_USAGE_SUMMARY;
53 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
54 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION;
55 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS;
56 import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP;
57 import static com.android.car.watchdog.CarWatchdogService.DEBUG;
58 import static com.android.car.watchdog.CarWatchdogService.TAG;
59 import static com.android.car.watchdog.PackageInfoHandler.SHARED_PACKAGE_PREFIX;
60 import static com.android.car.watchdog.TimeSource.ZONE_OFFSET;
61 import static com.android.car.watchdog.WatchdogStorage.RETENTION_PERIOD;
62 
63 import android.annotation.IntDef;
64 import android.annotation.NonNull;
65 import android.annotation.Nullable;
66 import android.annotation.UserIdInt;
67 import android.app.ActivityManager;
68 import android.app.StatsManager;
69 import android.app.StatsManager.PullAtomMetadata;
70 import android.automotive.watchdog.internal.ApplicationCategoryType;
71 import android.automotive.watchdog.internal.ComponentType;
72 import android.automotive.watchdog.internal.GarageMode;
73 import android.automotive.watchdog.internal.IoUsageStats;
74 import android.automotive.watchdog.internal.PackageIoOveruseStats;
75 import android.automotive.watchdog.internal.PackageMetadata;
76 import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
77 import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
78 import android.automotive.watchdog.internal.UserPackageIoUsageStats;
79 import android.car.builtin.content.pm.PackageManagerHelper;
80 import android.car.builtin.util.Slogf;
81 import android.car.drivingstate.CarUxRestrictions;
82 import android.car.drivingstate.ICarUxRestrictionsChangeListener;
83 import android.car.watchdog.CarWatchdogManager;
84 import android.car.watchdog.IResourceOveruseListener;
85 import android.car.watchdog.IoOveruseAlertThreshold;
86 import android.car.watchdog.IoOveruseConfiguration;
87 import android.car.watchdog.IoOveruseStats;
88 import android.car.watchdog.PackageKillableState;
89 import android.car.watchdog.PackageKillableState.KillableState;
90 import android.car.watchdog.PerStateBytes;
91 import android.car.watchdog.ResourceOveruseConfiguration;
92 import android.car.watchdog.ResourceOveruseStats;
93 import android.car.watchdoglib.CarWatchdogDaemonHelper;
94 import android.content.ContentResolver;
95 import android.content.Context;
96 import android.content.Intent;
97 import android.content.pm.ApplicationInfo;
98 import android.content.pm.PackageInfo;
99 import android.content.pm.PackageManager;
100 import android.content.res.Resources;
101 import android.net.Uri;
102 import android.os.Binder;
103 import android.os.Handler;
104 import android.os.IBinder;
105 import android.os.Looper;
106 import android.os.RemoteException;
107 import android.os.SystemClock;
108 import android.os.TransactionTooLargeException;
109 import android.os.UserHandle;
110 import android.os.UserManager;
111 import android.provider.Settings;
112 import android.text.TextUtils;
113 import android.util.ArrayMap;
114 import android.util.ArraySet;
115 import android.util.AtomicFile;
116 import android.util.JsonReader;
117 import android.util.JsonWriter;
118 import android.util.Pair;
119 import android.util.SparseArray;
120 import android.util.StatsEvent;
121 import android.view.Display;
122 
123 import com.android.car.BuiltinPackageDependency;
124 import com.android.car.CarLocalServices;
125 import com.android.car.CarStatsLog;
126 import com.android.car.CarUxRestrictionsManagerService;
127 import com.android.car.R;
128 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
129 import com.android.car.internal.NotificationHelperBase;
130 import com.android.car.internal.util.ConcurrentUtils;
131 import com.android.car.internal.util.IndentingPrintWriter;
132 import com.android.internal.annotations.GuardedBy;
133 import com.android.internal.annotations.VisibleForTesting;
134 import com.android.internal.util.Preconditions;
135 
136 import java.io.File;
137 import java.io.FileInputStream;
138 import java.io.FileOutputStream;
139 import java.io.IOException;
140 import java.io.InputStreamReader;
141 import java.io.OutputStreamWriter;
142 import java.lang.annotation.Retention;
143 import java.lang.annotation.RetentionPolicy;
144 import java.nio.charset.StandardCharsets;
145 import java.time.Instant;
146 import java.time.ZonedDateTime;
147 import java.time.format.DateTimeFormatter;
148 import java.time.format.DateTimeParseException;
149 import java.time.temporal.ChronoField;
150 import java.time.temporal.ChronoUnit;
151 import java.util.ArrayList;
152 import java.util.Arrays;
153 import java.util.Collections;
154 import java.util.List;
155 import java.util.Map;
156 import java.util.Objects;
157 import java.util.Set;
158 import java.util.concurrent.TimeUnit;
159 import java.util.function.BiConsumer;
160 import java.util.function.BiFunction;
161 import java.util.function.Consumer;
162 
163 /**
164  * Handles system resource performance monitoring module.
165  */
166 public final class WatchdogPerfHandler {
167     public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
168     public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
169     public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
170 
171     static final String INTENT_EXTRA_NOTIFICATION_ID = "notification_id";
172     static final String USER_PACKAGE_SEPARATOR = ":";
173     static final String PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR = ";";
174 
175     private static final String METADATA_FILENAME = "metadata.json";
176     private static final String SYSTEM_IO_USAGE_SUMMARY_REPORTED_DATE =
177             "systemIoUsageSummaryReportedDate";
178     private static final String UID_IO_USAGE_SUMMARY_REPORTED_DATE =
179             "uidIoUsageSummaryReportedDate";
180     private static final long OVERUSE_HANDLING_DELAY_MILLS = 10_000;
181     private static final long MAX_WAIT_TIME_MILLS = 3_000;
182 
183     private static final PullAtomMetadata PULL_ATOM_METADATA =
184             new PullAtomMetadata.Builder()
185                     // Summary atoms are populated only once a week, so a longer duration is
186                     // tolerable. However, the cool down duration should be smaller than a short
187                     // drive, so summary atoms can be pulled with short drives.
188                     .setCoolDownMillis(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.MINUTES))
189                     // When summary atoms are populated once a week, watchdog needs additional time
190                     // for reading from disk/DB.
191                     .setTimeoutMillis(10_000)
192                     .build();
193 
194     /**
195      * Don't distract the user by sending user notifications/dialogs, killing foreground
196      * applications, repeatedly killing persistent background services, or disabling any
197      * application.
198      */
199     private static final int UX_STATE_NO_DISTRACTION = 1;
200     /** The user can safely receive user notifications or dialogs. */
201     private static final int UX_STATE_USER_NOTIFICATION = 2;
202     /**
203      * Any application or service can be safely killed/disabled. User notifications can be sent
204      * only to the notification center.
205      */
206     private static final int UX_STATE_NO_INTERACTION = 3;
207 
208     @Retention(RetentionPolicy.SOURCE)
209     @IntDef(prefix = {"UX_STATE_"}, value = {
210             UX_STATE_NO_DISTRACTION,
211             UX_STATE_USER_NOTIFICATION,
212             UX_STATE_NO_INTERACTION
213     })
214     private @interface UxStateType{}
215 
216     private final Context mContext;
217     /**
218      * Context of the builtin car service that hosts the permissions, resources, and external
219      * facing services required for showing notifications.
220      */
221     private final Context mBuiltinPackageContext;
222     private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
223     private final PackageInfoHandler mPackageInfoHandler;
224     private final Handler mMainHandler;
225     private final Handler mServiceHandler;
226     private final WatchdogStorage mWatchdogStorage;
227     private final OveruseConfigurationCache mOveruseConfigurationCache;
228     private final int mUidIoUsageSummaryTopCount;
229     private final int mIoUsageSummaryMinSystemTotalWrittenBytes;
230     private final int mPackageKillableStateResetDays;
231     private final int mRecurringOverusePeriodInDays;
232     private final int mRecurringOveruseTimes;
233     private final int mResourceOveruseNotificationBaseId;
234     private final int mResourceOveruseNotificationMaxOffset;
235     private final TimeSource mTimeSource;
236     private final Object mLock = new Object();
237     /**
238      * Tracks user packages' resource usage. When cache is updated, call
239      * {@link WatchdogStorage#markDirty} to notify database is out of sync.
240      */
241     @GuardedBy("mLock")
242     private final ArrayMap<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
243     @GuardedBy("mLock")
244     private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid =
245             new SparseArray<>();
246     @GuardedBy("mLock")
247     private final SparseArray<ArrayList<ResourceOveruseListenerInfo>>
248             mOveruseSystemListenerInfosByUid = new SparseArray<>();
249     /**
250      * Default killable state for packages. Updated only for {@link UserHandle#ALL} user handle.
251      * When cache is updated, call {@link WatchdogStorage#markDirty} to notify database is out of
252      * sync.
253      */
254     // TODO(b/235615155): Update database when a default not killable package is set to killable
255     //  Also, changes to mDefaultNotKillableGenericPackages should be tracked by the last modified
256     //  date. This date should be copied to any new user package settings that take the default
257     //  value. When this date is beyond reset days, the settings here should be reset.
258     @GuardedBy("mLock")
259     private final ArraySet<String> mDefaultNotKillableGenericPackages = new ArraySet<>();
260     /** Keys in {@link mUsageByUserPackage} for user notification on resource overuse. */
261     @GuardedBy("mLock")
262     private final ArraySet<String> mUserNotifiablePackages = new ArraySet<>();
263     /** Values are the unique ids generated by {@code getUserPackageUniqueId}. */
264     @GuardedBy("mLock")
265     private final SparseArray<String> mActiveUserNotificationsByNotificationId =
266             new SparseArray<>();
267     /** Keys are the unique ids generated by {@code getUserPackageUniqueId}. */
268     @GuardedBy("mLock")
269     private final ArraySet<String> mActiveUserNotifications = new ArraySet<>();
270     /**
271      * Keys in {@link mUsageByUserPackage} that should be killed/disabled due to resource overuse.
272      */
273     @GuardedBy("mLock")
274     private final ArraySet<String> mActionableUserPackages = new ArraySet<>();
275     /**
276      * Tracks user packages disabled due to resource overuse.
277      */
278     @GuardedBy("mLock")
279     private final SparseArray<ArraySet<String>> mDisabledUserPackagesByUserId = new SparseArray<>();
280     @GuardedBy("mLock")
281     private ZonedDateTime mLatestStatsReportDate;
282     @GuardedBy("mLock")
283     private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
284             mPendingSetResourceOveruseConfigurationsRequest = null;
285     @GuardedBy("mLock")
286     private boolean mIsConnectedToDaemon;
287     @GuardedBy("mLock")
288     private @UxStateType int mCurrentUxState = UX_STATE_NO_DISTRACTION;
289     @GuardedBy("mLock")
290     private CarUxRestrictions mCurrentUxRestrictions;
291     @GuardedBy("mLock")
292     private boolean mIsHeadsUpNotificationSent;
293     @GuardedBy("mLock")
294     private int mCurrentOveruseNotificationIdOffset;
295     @GuardedBy("mLock")
296     private @GarageMode int mCurrentGarageMode = GarageMode.GARAGE_MODE_OFF;
297     @GuardedBy("mLock")
298     private long mOveruseHandlingDelayMills = OVERUSE_HANDLING_DELAY_MILLS;
299     @GuardedBy("mLock")
300     private ZonedDateTime mLastSystemIoUsageSummaryReportedDate;
301     @GuardedBy("mLock")
302     private ZonedDateTime mLastUidIoUsageSummaryReportedDate;
303 
304     private final ICarUxRestrictionsChangeListener mCarUxRestrictionsChangeListener =
305             new ICarUxRestrictionsChangeListener.Stub() {
306                 @Override
307                 public void onUxRestrictionsChanged(CarUxRestrictions restrictions) {
308                     synchronized (mLock) {
309                         mCurrentUxRestrictions = new CarUxRestrictions(restrictions);
310                         applyCurrentUxRestrictionsLocked();
311                     }
312                 }
313             };
314 
WatchdogPerfHandler(Context context, Context builtinPackageContext, CarWatchdogDaemonHelper daemonHelper, PackageInfoHandler packageInfoHandler, WatchdogStorage watchdogStorage, TimeSource timeSource)315     public WatchdogPerfHandler(Context context, Context builtinPackageContext,
316             CarWatchdogDaemonHelper daemonHelper, PackageInfoHandler packageInfoHandler,
317             WatchdogStorage watchdogStorage, TimeSource timeSource) {
318         mContext = context;
319         mBuiltinPackageContext = builtinPackageContext;
320         mCarWatchdogDaemonHelper = daemonHelper;
321         mPackageInfoHandler = packageInfoHandler;
322         mMainHandler = new Handler(Looper.getMainLooper());
323         mServiceHandler = new Handler(getHandlerThread(
324                 CarWatchdogService.class.getSimpleName()).getLooper());
325         mWatchdogStorage = watchdogStorage;
326         mOveruseConfigurationCache = new OveruseConfigurationCache();
327         mTimeSource = timeSource;
328         Resources resources = mContext.getResources();
329         mUidIoUsageSummaryTopCount = resources.getInteger(R.integer.uidIoUsageSummaryTopCount);
330         mIoUsageSummaryMinSystemTotalWrittenBytes =
331                 resources.getInteger(R.integer.ioUsageSummaryMinSystemTotalWrittenBytes);
332         mPackageKillableStateResetDays =
333                 resources.getInteger(R.integer.watchdogUserPackageSettingsResetDays);
334         mRecurringOverusePeriodInDays =
335                 resources.getInteger(R.integer.recurringResourceOverusePeriodInDays);
336         mRecurringOveruseTimes = resources.getInteger(R.integer.recurringResourceOveruseTimes);
337         mResourceOveruseNotificationBaseId =
338                 NotificationHelperBase.RESOURCE_OVERUSE_NOTIFICATION_BASE_ID;
339         mResourceOveruseNotificationMaxOffset =
340                 NotificationHelperBase.RESOURCE_OVERUSE_NOTIFICATION_MAX_OFFSET;
341     }
342 
343     /** Initializes the handler. */
init()344     public void init() {
345         // First database read is expensive, so post it on a separate handler thread.
346         mServiceHandler.post(() -> {
347             readFromDatabase();
348             // Set atom pull callbacks only after the internal datastructures are updated. When the
349             // pull happens, the service is already initialized and ready to populate the pulled
350             // atoms.
351             StatsManager statsManager = mContext.getSystemService(StatsManager.class);
352             statsManager.setPullAtomCallback(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY,
353                     PULL_ATOM_METADATA, ConcurrentUtils.DIRECT_EXECUTOR, this::onPullAtom);
354             statsManager.setPullAtomCallback(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY,
355                     PULL_ATOM_METADATA, ConcurrentUtils.DIRECT_EXECUTOR, this::onPullAtom);
356         });
357 
358         CarUxRestrictionsManagerService carUxRestrictionsManagerService =
359                 CarLocalServices.getService(CarUxRestrictionsManagerService.class);
360         CarUxRestrictions uxRestrictions =
361                 carUxRestrictionsManagerService.getCurrentUxRestrictions();
362         synchronized (mLock) {
363             mCurrentUxRestrictions = uxRestrictions;
364             applyCurrentUxRestrictionsLocked();
365             syncDisabledUserPackagesLocked();
366         }
367         carUxRestrictionsManagerService.registerUxRestrictionsChangeListener(
368                 mCarUxRestrictionsChangeListener, Display.DEFAULT_DISPLAY);
369 
370         if (DEBUG) {
371             Slogf.d(TAG, "WatchdogPerfHandler is initialized");
372         }
373     }
374 
375     /** Releases resources. */
release()376     public void release() {
377         CarLocalServices.getService(CarUxRestrictionsManagerService.class)
378                 .unregisterUxRestrictionsChangeListener(mCarUxRestrictionsChangeListener);
379     }
380 
381     /** Dumps its state. */
382     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)383     public void dump(IndentingPrintWriter writer) {
384         /*
385          * TODO(b/183436216): Implement this method.
386          */
387         synchronized (mLock) {
388             writer.println("Current UX state: " + toUxStateString(mCurrentUxState));
389             writer.println("List of disabled packages per user due to resource overuse: "
390                     + mDisabledUserPackagesByUserId);
391         }
392         mOveruseConfigurationCache.dump(writer);
393     }
394 
395     /** Retries any pending requests on re-connecting to the daemon */
onDaemonConnectionChange(boolean isConnected)396     public void onDaemonConnectionChange(boolean isConnected) {
397         boolean hasPendingRequest;
398         synchronized (mLock) {
399             mIsConnectedToDaemon = isConnected;
400             hasPendingRequest = mPendingSetResourceOveruseConfigurationsRequest != null;
401         }
402         if (isConnected) {
403             if (hasPendingRequest) {
404                 /*
405                  * Retry pending set resource overuse configuration request before processing any
406                  * new set/get requests. Thus notify the waiting requests only after the retry
407                  * completes.
408                  */
409                 retryPendingSetResourceOveruseConfigurations();
410             } else {
411                 /* Start fetch/sync configs only when there are no pending set requests because the
412                  * above retry starts fetch/sync configs on success. If the retry fails, the daemon
413                  * has crashed and shouldn't start fetchAndSyncResourceOveruseConfigurations.
414                  */
415                 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
416             }
417         }
418         synchronized (mLock) {
419             mLock.notifyAll();
420         }
421     }
422 
423     /** Updates the current UX state based on the display state. */
onDisplayStateChanged(boolean isEnabled)424     public void onDisplayStateChanged(boolean isEnabled) {
425         synchronized (mLock) {
426             if (isEnabled) {
427                 mCurrentUxState = UX_STATE_NO_DISTRACTION;
428                 applyCurrentUxRestrictionsLocked();
429             } else {
430                 mCurrentUxState = UX_STATE_NO_INTERACTION;
431                 performOveruseHandlingLocked();
432             }
433         }
434     }
435 
436     /** Handles garage mode change. */
onGarageModeChange(@arageMode int garageMode)437     public void onGarageModeChange(@GarageMode int garageMode) {
438         synchronized (mLock) {
439             mCurrentGarageMode = garageMode;
440             if (mCurrentGarageMode == GarageMode.GARAGE_MODE_ON) {
441                 mCurrentUxState = UX_STATE_NO_INTERACTION;
442                 performOveruseHandlingLocked();
443             }
444         }
445     }
446 
447     /** Returns resource overuse stats for the calling package. */
448     @NonNull
getResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)449     public ResourceOveruseStats getResourceOveruseStats(
450             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
451             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
452         Preconditions.checkArgument((resourceOveruseFlag > 0),
453                 "Must provide valid resource overuse flag");
454         Preconditions.checkArgument((maxStatsPeriod > 0),
455                 "Must provide valid maximum stats period");
456         // When more resource stats are added, make this as optional.
457         Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
458                 "Must provide resource I/O overuse flag");
459         int callingUid = Binder.getCallingUid();
460         UserHandle callingUserHandle = Binder.getCallingUserHandle();
461         int callingUserId = callingUserHandle.getIdentifier();
462         String genericPackageName =
463                 mPackageInfoHandler.getNamesForUids(new int[]{callingUid})
464                         .get(callingUid, null);
465         if (genericPackageName == null) {
466             Slogf.w(TAG, "Failed to fetch package info for uid %d", callingUid);
467             return new ResourceOveruseStats.Builder("", callingUserHandle).build();
468         }
469         ResourceOveruseStats.Builder statsBuilder =
470                 new ResourceOveruseStats.Builder(genericPackageName, callingUserHandle);
471         statsBuilder.setIoOveruseStats(
472                 getIoOveruseStatsForPeriod(callingUserId, genericPackageName, maxStatsPeriod));
473         if (DEBUG) {
474             Slogf.d(TAG, "Returning all resource overuse stats for calling uid %d [user %d and "
475                             + "package '%s']", callingUid, callingUserId, genericPackageName);
476         }
477         return statsBuilder.build();
478     }
479 
480     /** Returns resource overuse stats for all packages. */
481     @NonNull
getAllResourceOveruseStats( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)482     public List<ResourceOveruseStats> getAllResourceOveruseStats(
483             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
484             @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
485             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
486         Preconditions.checkArgument((resourceOveruseFlag > 0),
487                 "Must provide valid resource overuse flag");
488         Preconditions.checkArgument((maxStatsPeriod > 0),
489                 "Must provide valid maximum stats period");
490         // When more resource types are added, make this as optional.
491         Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
492                 "Must provide resource I/O overuse flag");
493         long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag);
494         List<ResourceOveruseStats> allStats = new ArrayList<>();
495         synchronized (mLock) {
496             for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
497                 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
498                 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
499                 IoOveruseStats ioOveruseStats =
500                         getIoOveruseStatsLocked(usage, minimumBytesWritten, maxStatsPeriod);
501                 if (ioOveruseStats == null) {
502                     continue;
503                 }
504                 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
505             }
506         }
507         if (DEBUG) {
508             Slogf.d(TAG, "Returning all resource overuse stats");
509         }
510         return allStats;
511     }
512 
513     /** Returns resource overuse stats for the specified user package. */
514     @NonNull
getResourceOveruseStatsForUserPackage( @onNull String packageName, @NonNull UserHandle userHandle, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)515     public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
516             @NonNull String packageName, @NonNull UserHandle userHandle,
517             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
518             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
519         Objects.requireNonNull(packageName, "Package name must be non-null");
520         Objects.requireNonNull(userHandle, "User handle must be non-null");
521         Preconditions.checkArgument(!userHandle.equals(UserHandle.ALL),
522                 "Must provide the user handle for a specific user");
523         Preconditions.checkArgument((resourceOveruseFlag > 0),
524                 "Must provide valid resource overuse flag");
525         Preconditions.checkArgument((maxStatsPeriod > 0),
526                 "Must provide valid maximum stats period");
527         // When more resource types are added, make this as optional.
528         Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
529                 "Must provide resource I/O overuse flag");
530         String genericPackageName =
531                 mPackageInfoHandler.getNameForUserPackage(packageName, userHandle.getIdentifier());
532         if (genericPackageName == null) {
533             throw new IllegalArgumentException("Package '" + packageName + "' not found");
534         }
535         ResourceOveruseStats.Builder statsBuilder =
536                 new ResourceOveruseStats.Builder(genericPackageName, userHandle);
537         statsBuilder.setIoOveruseStats(getIoOveruseStatsForPeriod(userHandle.getIdentifier(),
538                 genericPackageName, maxStatsPeriod));
539         if (DEBUG) {
540             Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s', "
541                     + "generic package '%s'", userHandle.getIdentifier(), packageName,
542                     genericPackageName);
543         }
544         return statsBuilder.build();
545     }
546 
547     /** Adds the resource overuse listener. */
addResourceOveruseListener( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)548     public void addResourceOveruseListener(
549             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
550             @NonNull IResourceOveruseListener listener) {
551         Objects.requireNonNull(listener, "Listener must be non-null");
552         Preconditions.checkArgument((resourceOveruseFlag > 0),
553                 "Must provide valid resource overuse flag");
554         synchronized (mLock) {
555             addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
556                     mOveruseListenerInfosByUid);
557         }
558     }
559 
560     /** Removes the previously added resource overuse listener. */
removeResourceOveruseListener(@onNull IResourceOveruseListener listener)561     public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
562         Objects.requireNonNull(listener, "Listener must be non-null");
563         synchronized (mLock) {
564             removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
565         }
566     }
567 
568     /** Adds the resource overuse system listener. */
addResourceOveruseListenerForSystem( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener)569     public void addResourceOveruseListenerForSystem(
570             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
571             @NonNull IResourceOveruseListener listener) {
572         Objects.requireNonNull(listener, "Listener must be non-null");
573         Preconditions.checkArgument((resourceOveruseFlag > 0),
574                 "Must provide valid resource overuse flag");
575         synchronized (mLock) {
576             addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
577                     mOveruseSystemListenerInfosByUid);
578         }
579     }
580 
581     /** Removes the previously added resource overuse system listener. */
removeResourceOveruseListenerForSystem(@onNull IResourceOveruseListener listener)582     public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
583         Objects.requireNonNull(listener, "Listener must be non-null");
584         synchronized (mLock) {
585             removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
586         }
587     }
588 
589     /** Sets whether or not a package is killable on resource overuse. */
setKillablePackageAsUser(String packageName, UserHandle userHandle, boolean isKillable)590     public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
591             boolean isKillable) {
592         Objects.requireNonNull(packageName, "Package name must be non-null");
593         Objects.requireNonNull(userHandle, "User handle must be non-null");
594 
595         if (userHandle.equals(UserHandle.ALL)) {
596             setPackageKillableStateForAllUsers(packageName, isKillable);
597             return;
598         }
599         int userId = userHandle.getIdentifier();
600         String genericPackageName = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
601         if (genericPackageName == null) {
602             throw new IllegalArgumentException("Package '" + packageName + "' not found");
603         }
604         String key = getUserPackageUniqueId(userId, genericPackageName);
605         PackageResourceUsage usage;
606         synchronized (mLock) {
607             // When the queried package is not cached in {@link mUsageByUserPackage}, the set API
608             // will update the killable state even when the package should never be killed.
609             // But the get API will return the correct killable state. This behavior is tolerable
610             // because in production the set API should be called only after the get API.
611             // For instance, when this case happens by mistake and the package overuses resource
612             // between the set and the get API calls, the daemon will provide correct killable
613             // state when pushing the latest stats. Ergo, the invalid killable state doesn't have
614             // any effect.
615             usage = mUsageByUserPackage.get(key);
616             if (usage == null) {
617                 usage = new PackageResourceUsage(userId, genericPackageName,
618                         getDefaultKillableStateLocked(genericPackageName));
619             }
620             if (!usage.verifyAndSetKillableState(isKillable, mTimeSource.getCurrentDate())) {
621                 Slogf.e(TAG, "User %d cannot set killable state for package '%s'",
622                         userHandle.getIdentifier(), genericPackageName);
623                 throw new IllegalArgumentException("Package killable state is not updatable");
624             }
625             mUsageByUserPackage.put(key, usage);
626         }
627         if (!isKillable) {
628             int uid = getOrFetchUid(usage, packageName);
629             enablePackageForUser(uid, usage.genericPackageName);
630         }
631         mWatchdogStorage.markDirty();
632         if (DEBUG) {
633             Slogf.d(TAG, "Successfully set killable package state for user %d", userId);
634         }
635     }
636 
setPackageKillableStateForAllUsers(String packageName, boolean isKillable)637     private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
638         int[] userIds = getAliveUserIds();
639         String genericPackageName = null;
640         List<PackageResourceUsage> updatedUsages = new ArrayList<>(userIds.length);
641         synchronized (mLock) {
642             for (int i = 0; i < userIds.length; i++) {
643                 int userId = userIds[i];
644                 String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
645                 if (name == null) {
646                     continue;
647                 }
648                 genericPackageName = name;
649                 String key = getUserPackageUniqueId(userId, genericPackageName);
650                 PackageResourceUsage usage = mUsageByUserPackage.get(key);
651                 if (usage == null) {
652                     continue;
653                 }
654                 if (!usage.verifyAndSetKillableState(isKillable, mTimeSource.getCurrentDate())) {
655                     Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName);
656                     throw new IllegalArgumentException(
657                             "Package killable state is not updatable");
658                 }
659                 updatedUsages.add(usage);
660             }
661             if (genericPackageName != null) {
662                 if (!isKillable) {
663                     mDefaultNotKillableGenericPackages.add(genericPackageName);
664                 } else {
665                     mDefaultNotKillableGenericPackages.remove(genericPackageName);
666                 }
667                 mWatchdogStorage.markDirty();
668             }
669         }
670         // Enabling user packages requires accessing package manager which requires making binder
671         // calls. Binder calls should not be made while holding a lock, given it might lead to
672         // deadlock. Hence, enabling packages after the synchronized block.
673         if (!isKillable) {
674             for (int i = 0; i < updatedUsages.size(); i++) {
675                 PackageResourceUsage usage = updatedUsages.get(i);
676                 int uid = getOrFetchUid(usage, packageName);
677                 enablePackageForUser(uid, usage.genericPackageName);
678             }
679         }
680         if (DEBUG) {
681             Slogf.d(TAG, "Successfully set killable package state for all users");
682         }
683     }
684 
685     /** Returns the list of package killable states on resource overuse for the user. */
686     @NonNull
getPackageKillableStatesAsUser(UserHandle userHandle)687     public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
688         Objects.requireNonNull(userHandle, "User handle must be non-null");
689         PackageManager pm = mContext.getPackageManager();
690         if (!userHandle.equals(UserHandle.ALL)) {
691             if (DEBUG) {
692                 Slogf.d(TAG, "Returning all package killable states for user %d",
693                         userHandle.getIdentifier());
694             }
695             return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
696         }
697         List<PackageKillableState> packageKillableStates = new ArrayList<>();
698         int[] userIds = getAliveUserIds();
699         for (int i = 0; i < userIds.length; ++i) {
700             packageKillableStates.addAll(
701                     getPackageKillableStatesForUserId(userIds[i], pm));
702         }
703         if (DEBUG) {
704             Slogf.d(TAG, "Returning all package killable states for all users");
705         }
706         return packageKillableStates;
707     }
708 
getPackageKillableStatesForUserId(int userId, PackageManager pm)709     private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
710             PackageManager pm) {
711         List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */ 0, userId);
712         List<PackageKillableState> states = new ArrayList<>();
713         synchronized (mLock) {
714             ArrayMap<String, List<ApplicationInfo>> applicationInfosBySharedPackage =
715                     new ArrayMap<>();
716             for (int i = 0; i < packageInfos.size(); ++i) {
717                 PackageInfo packageInfo = packageInfos.get(i);
718                 String genericPackageName = mPackageInfoHandler.getNameForPackage(packageInfo);
719                 if (packageInfo.sharedUserId == null) {
720                     int componentType = mPackageInfoHandler.getComponentType(
721                             packageInfo.applicationInfo);
722                     int killableState = getPackageKillableStateForUserPackageLocked(
723                             userId, genericPackageName, componentType,
724                             mOveruseConfigurationCache.isSafeToKill(
725                                     genericPackageName, componentType, /* sharedPackages= */null));
726                     states.add(new PackageKillableState(packageInfo.packageName, userId,
727                             killableState));
728                     continue;
729                 }
730                 List<ApplicationInfo> applicationInfos =
731                         applicationInfosBySharedPackage.get(genericPackageName);
732                 if (applicationInfos == null) {
733                     applicationInfos = new ArrayList<>();
734                 }
735                 applicationInfos.add(packageInfo.applicationInfo);
736                 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos);
737             }
738             for (Map.Entry<String, List<ApplicationInfo>> entry :
739                     applicationInfosBySharedPackage.entrySet()) {
740                 String genericPackageName = entry.getKey();
741                 List<ApplicationInfo> applicationInfos = entry.getValue();
742                 int componentType = mPackageInfoHandler.getSharedComponentType(
743                         applicationInfos, genericPackageName);
744                 List<String> packageNames = new ArrayList<>(applicationInfos.size());
745                 for (int i = 0; i < applicationInfos.size(); ++i) {
746                     packageNames.add(applicationInfos.get(i).packageName);
747                 }
748                 int killableState = getPackageKillableStateForUserPackageLocked(
749                         userId, genericPackageName, componentType,
750                         mOveruseConfigurationCache.isSafeToKill(
751                                 genericPackageName, componentType, packageNames));
752                 for (int i = 0; i < applicationInfos.size(); ++i) {
753                     states.add(new PackageKillableState(
754                             applicationInfos.get(i).packageName, userId, killableState));
755                 }
756             }
757         }
758         if (DEBUG) {
759             Slogf.d(TAG, "Returning the package killable states for user packages");
760         }
761         return states;
762     }
763 
764     /** Sets the given resource overuse configurations. */
765     @CarWatchdogManager.ReturnCode
setResourceOveruseConfigurations( List<ResourceOveruseConfiguration> configurations, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)766     public int setResourceOveruseConfigurations(
767             List<ResourceOveruseConfiguration> configurations,
768             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
769             throws RemoteException {
770         Objects.requireNonNull(configurations, "Configurations must be non-null");
771         Preconditions.checkArgument((configurations.size() > 0),
772                 "Must provide at least one configuration");
773         Preconditions.checkArgument((resourceOveruseFlag > 0),
774                 "Must provide valid resource overuse flag");
775         checkResourceOveruseConfigs(configurations, resourceOveruseFlag);
776         List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
777                 new ArrayList<>();
778         for (int i = 0; i < configurations.size(); ++i) {
779             internalConfigs.add(toInternalResourceOveruseConfiguration(configurations.get(i),
780                     resourceOveruseFlag));
781         }
782         synchronized (mLock) {
783             if (!mIsConnectedToDaemon) {
784                 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
785                 return CarWatchdogManager.RETURN_CODE_SUCCESS;
786             }
787             /* Verify no pending request in progress. */
788             setPendingSetResourceOveruseConfigurationsRequestLocked(null);
789         }
790         return setResourceOveruseConfigurationsInternal(internalConfigs,
791                 /* isPendingRequest= */ false);
792     }
793 
794     /** Returns the available resource overuse configurations. */
795     @NonNull
getResourceOveruseConfigurations( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)796     public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
797             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
798         Preconditions.checkArgument((resourceOveruseFlag > 0),
799                 "Must provide valid resource overuse flag");
800         if (!isConnectedToDaemon()) {
801             throw new IllegalStateException("Car watchdog daemon is not connected");
802         }
803         synchronized (mLock) {
804             /* Verify no pending request in progress. */
805             setPendingSetResourceOveruseConfigurationsRequestLocked(null);
806         }
807         List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
808                 new ArrayList<>();
809         try {
810             internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
811         } catch (RemoteException | RuntimeException e) {
812             Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
813             throw new IllegalStateException(e);
814         }
815         List<ResourceOveruseConfiguration> configs = new ArrayList<>();
816         for (int i = 0; i < internalConfigs.size(); ++i) {
817             configs.add(
818                     toResourceOveruseConfiguration(internalConfigs.get(i), resourceOveruseFlag));
819         }
820         if (DEBUG) {
821             Slogf.d(TAG, "Returning the resource overuse configuration");
822         }
823         return configs;
824     }
825 
826     /** Processes the latest I/O overuse stats */
latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats)827     public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
828         // Long running operation, such as DB operations, must not be performed on binder threads,
829         // even if they are one way binder call, because it may block other one way binder threads.
830         // Hence, we handle the latest I/O overuse stats on the service handler thread.
831         mServiceHandler.post(() -> latestIoOveruseStatsInternal(packageIoOveruseStats));
832     }
833 
latestIoOveruseStatsInternal(List<PackageIoOveruseStats> packageIoOveruseStats)834     private void latestIoOveruseStatsInternal(List<PackageIoOveruseStats> packageIoOveruseStats) {
835         int[] uids = new int[packageIoOveruseStats.size()];
836         for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
837             uids[i] = packageIoOveruseStats.get(i).uid;
838         }
839         SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids);
840         ArraySet<String> overusingUserPackageKeys = new ArraySet<>();
841         checkAndHandleDateChange();
842         if (genericPackageNamesByUid.size() > 0) {
843             mWatchdogStorage.markDirty();
844         }
845         synchronized (mLock) {
846             for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
847                 PackageIoOveruseStats stats = packageIoOveruseStats.get(i);
848                 String genericPackageName = genericPackageNamesByUid.get(stats.uid);
849                 if (genericPackageName == null) {
850                     continue;
851                 }
852                 PackageResourceUsage usage = cacheAndFetchUsageLocked(stats.uid, genericPackageName,
853                         stats.ioOveruseStats, stats.forgivenWriteBytes);
854                 if (stats.shouldNotify) {
855                     /*
856                      * Packages that exceed the warn threshold percentage should be notified as well
857                      * and only the daemon is aware of such packages. Thus the flag is used to
858                      * indicate which packages should be notified.
859                      */
860                     ResourceOveruseStats resourceOveruseStats =
861                             usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
862                                     usage.getIoOveruseStats()).build();
863                     notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
864                 }
865                 if (!usage.ioUsage.exceedsThreshold()) {
866                     continue;
867                 }
868                 overusingUserPackageKeys.add(usage.getUniqueId());
869                 if (usage.getKillableState() == KILLABLE_STATE_NEVER) {
870                     continue;
871                 }
872                 if (usage.ioUsage.getNotForgivenOveruses() > mRecurringOveruseTimes) {
873                     String id = usage.getUniqueId();
874                     mActionableUserPackages.add(id);
875                     mUserNotifiablePackages.add(id);
876                     usage.ioUsage.forgiveOveruses();
877                 }
878             }
879             if ((mCurrentUxState != UX_STATE_NO_DISTRACTION && !mUserNotifiablePackages.isEmpty())
880                     // TODO(b/200599130): When resource overusing background apps are killed
881                     //  immediately, update the below check to allow posting
882                     //  {@code performOveruseHandlingLocked} immediately.
883                     || (mCurrentUxState == UX_STATE_NO_INTERACTION
884                     && !mActionableUserPackages.isEmpty())) {
885                 mMainHandler.postDelayed(() -> {
886                     synchronized (mLock) {
887                         performOveruseHandlingLocked();
888                     }}, mOveruseHandlingDelayMills);
889             }
890         }
891         if (!overusingUserPackageKeys.isEmpty()) {
892             pushIoOveruseMetrics(overusingUserPackageKeys);
893         }
894         if (DEBUG) {
895             Slogf.d(TAG, "Processed latest I/O overuse stats");
896         }
897     }
898 
899     /** Resets the resource overuse settings and stats for the given generic package names. */
resetResourceOveruseStats(Set<String> genericPackageNames)900     public void resetResourceOveruseStats(Set<String> genericPackageNames) {
901         mServiceHandler.post(() -> {
902             synchronized (mLock) {
903                 mIsHeadsUpNotificationSent = false;
904                 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
905                     PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
906                     if (!genericPackageNames.contains(usage.genericPackageName)) {
907                         continue;
908                     }
909                     usage.resetStats();
910                     usage.verifyAndSetKillableState(/* isKillable= */ true,
911                             mTimeSource.getCurrentDate());
912                     mWatchdogStorage.deleteUserPackage(usage.userId, usage.genericPackageName);
913                     mActionableUserPackages.remove(usage.getUniqueId());
914                     Slogf.i(TAG,
915                             "Reset resource overuse settings and stats for user '%d' package '%s'",
916                             usage.userId, usage.genericPackageName);
917                     if (usage.isSharedPackage() && usage.getUid() == INVALID_UID) {
918                         // Only enable packages that were disabled by the watchdog service. Ergo, if
919                         // the usage doesn't have a valid UID, the package was not recently disabled
920                         // by the watchdog service (unless the service crashed) and can be safely
921                         // skipped.
922                         Slogf.e(TAG, "Skipping enabling user %d's package %s", usage.userId,
923                                 usage.genericPackageName);
924                         continue;
925                     }
926                     enablePackageForUser(usage.getUid(), usage.genericPackageName);
927                 }
928             }
929         });
930     }
931 
932     /**
933      * Asynchronously fetches today's I/O usage stats for all packages collected during the
934      * previous boot and sends them to the CarWatchdog daemon.
935      */
asyncFetchTodayIoUsageStats()936     public void asyncFetchTodayIoUsageStats() {
937         mServiceHandler.post(() -> {
938             List<UserPackageIoUsageStats> todayIoUsageStats = getTodayIoUsageStats();
939             try {
940                 mCarWatchdogDaemonHelper.onTodayIoUsageStatsFetched(todayIoUsageStats);
941             } catch (RemoteException e) {
942                 Slogf.w(TAG, e, "Failed to send today's I/O usage stats to daemon.");
943             }
944         });
945     }
946 
947     /** Returns today's I/O usage stats for all packages collected during the previous boot. */
getTodayIoUsageStats()948     public List<UserPackageIoUsageStats> getTodayIoUsageStats() {
949         List<UserPackageIoUsageStats> userPackageIoUsageStats = new ArrayList<>();
950         List<WatchdogStorage.IoUsageStatsEntry> entries = mWatchdogStorage.getTodayIoUsageStats();
951         for (int i = 0; i < entries.size(); ++i) {
952             WatchdogStorage.IoUsageStatsEntry entry = entries.get(i);
953             UserPackageIoUsageStats stats = new UserPackageIoUsageStats();
954             stats.userId = entry.userId;
955             stats.packageName = entry.packageName;
956             stats.ioUsageStats = new IoUsageStats();
957             android.automotive.watchdog.IoOveruseStats internalIoUsage =
958                     entry.ioUsage.getInternalIoOveruseStats();
959             stats.ioUsageStats.writtenBytes = internalIoUsage.writtenBytes;
960             stats.ioUsageStats.forgivenWriteBytes = entry.ioUsage.getForgivenWriteBytes();
961             stats.ioUsageStats.totalOveruses = internalIoUsage.totalOveruses;
962             userPackageIoUsageStats.add(stats);
963         }
964         return userPackageIoUsageStats;
965     }
966 
967     /** Deletes all data for specific user. */
deleteUser(@serIdInt int userId)968     public void deleteUser(@UserIdInt int userId) {
969         synchronized (mLock) {
970             for (int i = mUsageByUserPackage.size() - 1; i >= 0; --i) {
971                 if (userId == mUsageByUserPackage.valueAt(i).userId) {
972                     mUsageByUserPackage.removeAt(i);
973                 }
974             }
975             mWatchdogStorage.syncUsers(getAliveUserIds());
976         }
977         if (DEBUG) {
978             Slogf.d(TAG, "Resource usage for user id: %d was deleted.", userId);
979         }
980     }
981 
982     /** Handles intents from user notification actions. */
processUserNotificationIntent(Intent intent)983     public void processUserNotificationIntent(Intent intent) {
984         String action = intent.getAction();
985         String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
986         UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
987         int notificationId = intent.getIntExtra(INTENT_EXTRA_NOTIFICATION_ID, -1);
988         if (packageName == null || packageName.isEmpty() || userHandle == null
989                 || userHandle.getIdentifier() < 0) {
990             Slogf.w(TAG, "Invalid package '%s' or userHandle '%s' received in the intent",
991                     packageName, userHandle);
992             return;
993         }
994         switch (action) {
995             case CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP:
996                 disablePackageForUser(packageName, userHandle.getIdentifier());
997                 if (DEBUG) {
998                     Slogf.d(TAG,
999                             "Handled user notification action to disable package %s for user %s",
1000                             packageName, userHandle);
1001                 }
1002                 break;
1003             case CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS:
1004                 Intent settingsIntent = new Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
1005                         .setData(Uri.parse("package:" + packageName))
1006                         .setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
1007                 mBuiltinPackageContext.startActivityAsUser(settingsIntent, userHandle);
1008                 if (DEBUG) {
1009                     Slogf.d(TAG, "Handled user notification action to launch settings app for "
1010                             + "package %s and user %s", packageName, userHandle);
1011                 }
1012                 break;
1013             case CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION:
1014                 break;
1015             default:
1016                 Slogf.e(TAG, "Skipping invalid user notification intent action: %s", action);
1017                 return;
1018         }
1019 
1020         if (notificationId == -1) {
1021             Slogf.e(TAG, "Didn't received user notification id in action %s", action);
1022             return;
1023         }
1024 
1025         int maxNotificationId =
1026                 mResourceOveruseNotificationBaseId + mResourceOveruseNotificationMaxOffset - 1;
1027         if (notificationId < mResourceOveruseNotificationBaseId
1028                 || notificationId > maxNotificationId) {
1029             Slogf.e(TAG, "Notification id (%d) outside of reserved IDs (%d - %d) for car watchdog.",
1030                     notificationId, mResourceOveruseNotificationBaseId, maxNotificationId);
1031             return;
1032         }
1033 
1034         synchronized (mLock) {
1035             String uniqueUserPackageId = mActiveUserNotificationsByNotificationId.get(
1036                     notificationId);
1037             if (uniqueUserPackageId != null
1038                     && uniqueUserPackageId.equals(getUserPackageUniqueId(userHandle.getIdentifier(),
1039                     packageName))) {
1040                 mActiveUserNotificationsByNotificationId.remove(notificationId);
1041                 mActiveUserNotifications.remove(uniqueUserPackageId);
1042             }
1043         }
1044 
1045         cancelNotificationAsUser(notificationId, userHandle);
1046         if (DEBUG) {
1047             Slogf.d(TAG, "Successfully canceled notification id %d for user %s and package %s",
1048                     notificationId, userHandle, packageName);
1049         }
1050     }
1051 
1052     /** Handles when system broadcast package changed action */
processPackageChangedIntent(Intent intent)1053     public void processPackageChangedIntent(Intent intent) {
1054         int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
1055         if (userId == USER_NULL) {
1056             Slogf.w(TAG, "Skipping package changed action with USER_NULL user");
1057             return;
1058         }
1059         String packageName = intent.getData().getSchemeSpecificPart();
1060         try {
1061             if (PackageManagerHelper.getApplicationEnabledSettingForUser(packageName, userId)
1062                     != COMPONENT_ENABLED_STATE_ENABLED) {
1063                 return;
1064             }
1065         } catch (Exception e) {
1066             // Catch IllegalArgumentException thrown by PackageManager when the package
1067             // is not found. CarWatchdogService shouldn't crash when the package
1068             // no longer exists when the {@link ACTION_PACKAGE_CHANGED} broadcast is
1069             // handled.
1070             Slogf.e(TAG, e,
1071                     "Failed to verify enabled setting for user %d, package '%s'",
1072                     userId, packageName);
1073             return;
1074         }
1075         synchronized (mLock) {
1076             ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId);
1077             if (disabledPackages == null || !disabledPackages.contains(packageName)) {
1078                 return;
1079             }
1080             removeFromDisabledPackagesSettingsStringLocked(packageName, userId);
1081             disabledPackages.remove(packageName);
1082             if (disabledPackages.isEmpty()) {
1083                 mDisabledUserPackagesByUserId.remove(userId);
1084             }
1085         }
1086         if (DEBUG) {
1087             Slogf.d(TAG, "Successfully enabled package due to package changed action");
1088         }
1089     }
1090 
1091     /** Disables a package for specific user until used. */
disablePackageForUser(String packageName, @UserIdInt int userId)1092     public boolean disablePackageForUser(String packageName, @UserIdInt int userId) {
1093         synchronized (mLock) {
1094             ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId);
1095             if (disabledPackages != null && disabledPackages.contains(packageName)) {
1096                 return true;
1097             }
1098         }
1099         try {
1100             int currentEnabledState =
1101                     PackageManagerHelper.getApplicationEnabledSettingForUser(packageName, userId);
1102             switch (currentEnabledState) {
1103                 case COMPONENT_ENABLED_STATE_DISABLED:
1104                 case COMPONENT_ENABLED_STATE_DISABLED_USER:
1105                 case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
1106                     Slogf.w(TAG, "Unable to disable application for user %d, package '%s' as the "
1107                             + "current enabled state is %s", userId, packageName,
1108                             toEnabledStateString(currentEnabledState));
1109                     return false;
1110                 default:
1111                     // COMPONENT_ENABLED_STATE_DEFAULT or other non-disabled states.
1112                     break;
1113             }
1114             PackageManagerHelper.setApplicationEnabledSettingForUser(packageName,
1115                     COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
1116                     mContext.getPackageName());
1117             synchronized (mLock) {
1118                 ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId);
1119                 if (disabledPackages == null) {
1120                     disabledPackages = new ArraySet<>(1);
1121                 }
1122                 appendToDisabledPackagesSettingsString(packageName, userId);
1123                 disabledPackages.add(packageName);
1124                 mDisabledUserPackagesByUserId.put(userId, disabledPackages);
1125             }
1126             Slogf.i(TAG, "Disabled package '%s' on user %d until used due to resource overuse",
1127                     packageName, userId);
1128         } catch (Exception e) {
1129             Slogf.e(TAG, e, "Failed to disable application for user %d, package '%s'", userId,
1130                     packageName);
1131             return false;
1132         }
1133         return true;
1134     }
1135 
1136     /**
1137      * Sets the delay to handle resource overuse after the package is notified of resource overuse.
1138      */
setOveruseHandlingDelay(long millis)1139     public void setOveruseHandlingDelay(long millis) {
1140         synchronized (mLock) {
1141             mOveruseHandlingDelayMills = millis;
1142         }
1143     }
1144 
1145     /** Writes to watchdog metadata file. */
writeMetadataFile()1146     public void writeMetadataFile() {
1147         ZonedDateTime systemIoUsageSummaryReportDate;
1148         ZonedDateTime uidIoUsageSummaryReportDate;
1149         synchronized (mLock) {
1150             if (mLastSystemIoUsageSummaryReportedDate == null
1151                     && mLastUidIoUsageSummaryReportedDate == null) {
1152                 return;
1153             }
1154             systemIoUsageSummaryReportDate = mLastSystemIoUsageSummaryReportedDate;
1155             uidIoUsageSummaryReportDate = mLastUidIoUsageSummaryReportedDate;
1156         }
1157         File file = getWatchdogMetadataFile();
1158         AtomicFile atomicFile = new AtomicFile(file);
1159         FileOutputStream fos = null;
1160         try {
1161             fos = atomicFile.startWrite();
1162             try (JsonWriter jsonWriter =
1163                          new JsonWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
1164                 jsonWriter.beginObject();
1165                 if (systemIoUsageSummaryReportDate != null) {
1166                     jsonWriter.name(SYSTEM_IO_USAGE_SUMMARY_REPORTED_DATE)
1167                             .value(systemIoUsageSummaryReportDate
1168                                     .format(DateTimeFormatter.ISO_DATE_TIME));
1169                 }
1170                 if (uidIoUsageSummaryReportDate != null) {
1171                     jsonWriter.name(UID_IO_USAGE_SUMMARY_REPORTED_DATE)
1172                             .value(uidIoUsageSummaryReportDate
1173                                     .format(DateTimeFormatter.ISO_DATE_TIME));
1174                 }
1175                 jsonWriter.endObject();
1176             }
1177             atomicFile.finishWrite(fos);
1178             if (DEBUG) {
1179                 Slogf.e(TAG, "Successfully wrote watchdog metadata file '%s'",
1180                         file.getAbsoluteFile());
1181             }
1182         } catch (IOException e) {
1183             Slogf.e(TAG, e, "Failed to write watchdog metadata file '%s'", file.getAbsoluteFile());
1184             atomicFile.failWrite(fos);
1185         }
1186     }
1187 
1188     /** Fetches and syncs the resource overuse configurations from watchdog daemon. */
fetchAndSyncResourceOveruseConfigurations()1189     private void fetchAndSyncResourceOveruseConfigurations() {
1190         List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs;
1191         try {
1192             internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
1193         } catch (RemoteException | RuntimeException e) {
1194             Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
1195             return;
1196         }
1197         if (internalConfigs.isEmpty()) {
1198             Slogf.e(TAG, "Fetched resource overuse configurations are empty");
1199             return;
1200         }
1201         mOveruseConfigurationCache.set(internalConfigs);
1202         mPackageInfoHandler.setVendorPackagePrefixes(
1203                 mOveruseConfigurationCache.getVendorPackagePrefixes());
1204         if (DEBUG) {
1205             Slogf.d(TAG, "Fetched and synced resource overuse configs.");
1206         }
1207     }
1208 
readFromDatabase()1209     private void readFromDatabase() {
1210         mWatchdogStorage.syncUsers(getAliveUserIds());
1211         List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries =
1212                 mWatchdogStorage.getUserPackageSettings();
1213         Slogf.i(TAG, "Read %d user package settings from database", settingsEntries.size());
1214         // Get date before |WatchdogStorage.getTodayIoUsageStats| such that if date changes between
1215         // call to database and caching of the date, future calls to |latestIoOveruseStats| will
1216         // catch the change and sync the database with the in-memory cache.
1217         ZonedDateTime curReportDate = mTimeSource.getCurrentDate();
1218         Instant killableStateResetDate =
1219                 curReportDate.minusDays(mPackageKillableStateResetDays).toInstant();
1220         List<WatchdogStorage.IoUsageStatsEntry> ioStatsEntries =
1221                 mWatchdogStorage.getTodayIoUsageStats();
1222         Slogf.i(TAG, "Read %d I/O usage stats from database", ioStatsEntries.size());
1223         synchronized (mLock) {
1224             for (int i = 0; i < settingsEntries.size(); i++) {
1225                 WatchdogStorage.UserPackageSettingsEntry entry = settingsEntries.get(i);
1226                 if (entry.userId == UserHandle.ALL.getIdentifier()) {
1227                     if (entry.killableState != KILLABLE_STATE_YES) {
1228                         mDefaultNotKillableGenericPackages.add(entry.packageName);
1229                     }
1230                     continue;
1231                 }
1232                 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
1233                 PackageResourceUsage usage = mUsageByUserPackage.get(key);
1234                 if (usage == null) {
1235                     usage = new PackageResourceUsage(entry.userId, entry.packageName,
1236                             getDefaultKillableStateLocked(entry.packageName));
1237                 }
1238                 int killableState = entry.killableState;
1239                 Instant lastModifiedDate =
1240                         Instant.ofEpochSecond(entry.killableStateLastModifiedEpochSeconds);
1241                 ZonedDateTime usageModifiedDate = lastModifiedDate.atZone(ZONE_OFFSET);
1242                 if (killableState == KILLABLE_STATE_NO
1243                         && lastModifiedDate.compareTo(killableStateResetDate) <= 0) {
1244                     killableState = KILLABLE_STATE_YES;
1245                     usageModifiedDate = curReportDate;
1246                     mWatchdogStorage.markDirty();
1247                     Slogf.i(TAG, "Reset killable state for package %s for user %d",
1248                             entry.packageName, entry.userId);
1249                 }
1250                 usage.setKillableState(killableState, usageModifiedDate);
1251                 mUsageByUserPackage.put(key, usage);
1252             }
1253             for (int i = 0; i < ioStatsEntries.size(); ++i) {
1254                 WatchdogStorage.IoUsageStatsEntry entry = ioStatsEntries.get(i);
1255                 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
1256                 PackageResourceUsage usage = mUsageByUserPackage.get(key);
1257                 if (usage == null) {
1258                     usage = new PackageResourceUsage(entry.userId, entry.packageName,
1259                             getDefaultKillableStateLocked(entry.packageName));
1260                 }
1261                 /* Overwrite in memory cache as the stats will be merged on the daemon side and
1262                  * pushed on the next latestIoOveruseStats call. This is tolerable because the next
1263                  * push should happen soon.
1264                  */
1265                 usage.ioUsage.overwrite(entry.ioUsage);
1266                 mUsageByUserPackage.put(key, usage);
1267             }
1268             mLatestStatsReportDate = curReportDate;
1269         }
1270         syncHistoricalNotForgivenOveruses();
1271     }
1272 
1273     /** Fetches all historical not forgiven overuses and syncs them with package I/O usages. */
syncHistoricalNotForgivenOveruses()1274     private void syncHistoricalNotForgivenOveruses() {
1275         List<WatchdogStorage.NotForgivenOverusesEntry> notForgivenOverusesEntries =
1276                 mWatchdogStorage.getNotForgivenHistoricalIoOveruses(mRecurringOverusePeriodInDays);
1277         Slogf.i(TAG, "Read %d not forgiven overuse stats from database",
1278                 notForgivenOverusesEntries.size());
1279         synchronized (mLock) {
1280             for (int i = 0; i < notForgivenOverusesEntries.size(); i++) {
1281                 WatchdogStorage.NotForgivenOverusesEntry entry = notForgivenOverusesEntries.get(i);
1282                 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
1283                 PackageResourceUsage usage = mUsageByUserPackage.get(key);
1284                 if (usage == null) {
1285                     usage = new PackageResourceUsage(entry.userId, entry.packageName,
1286                             getDefaultKillableStateLocked(entry.packageName));
1287                 }
1288                 usage.ioUsage.setHistoricalNotForgivenOveruses(entry.notForgivenOveruses);
1289                 mUsageByUserPackage.put(key, usage);
1290             }
1291         }
1292     }
1293 
1294     /**
1295      * Writes user package settings and stats to database. If database is marked as clean,
1296      * no writing is executed.
1297      */
writeToDatabase()1298     public void writeToDatabase() {
1299         if (!mWatchdogStorage.startWrite()) {
1300             return;
1301         }
1302         try {
1303             List<WatchdogStorage.UserPackageSettingsEntry> userPackageSettingsEntries =
1304                     new ArrayList<>();
1305             List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = new ArrayList<>();
1306             SparseArray<List<String>> forgivePackagesByUserId = new SparseArray<>();
1307             synchronized (mLock) {
1308                 for (int i = 0; i < mUsageByUserPackage.size(); i++) {
1309                     PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
1310                     userPackageSettingsEntries.add(new WatchdogStorage.UserPackageSettingsEntry(
1311                             usage.userId, usage.genericPackageName, usage.getKillableState(),
1312                             usage.getKillableStateLastModifiedDate().toEpochSecond()));
1313                     if (!usage.ioUsage.hasUsage()) {
1314                         continue;
1315                     }
1316                     if (usage.ioUsage.shouldForgiveHistoricalOveruses()) {
1317                         List<String> packagesToForgive = forgivePackagesByUserId.get(usage.userId);
1318                         if (packagesToForgive == null) {
1319                             packagesToForgive = new ArrayList<>();
1320                         }
1321                         packagesToForgive.add(usage.genericPackageName);
1322                         forgivePackagesByUserId.put(usage.userId, packagesToForgive);
1323                     }
1324                     ioUsageStatsEntries.add(new WatchdogStorage.IoUsageStatsEntry(usage.userId,
1325                             usage.genericPackageName, usage.ioUsage));
1326                 }
1327                 for (String packageName : mDefaultNotKillableGenericPackages) {
1328                     // TODO(b/235615155): Update database when a default not killable package is
1329                     //  set to killable. Also, changes to mDefaultNotKillableGenericPackages should
1330                     //  be tracked by the last modified date and the date should be written to the
1331                     //  database.
1332                     userPackageSettingsEntries.add(new WatchdogStorage.UserPackageSettingsEntry(
1333                             UserHandle.ALL.getIdentifier(), packageName, KILLABLE_STATE_NO,
1334                             mTimeSource.getCurrentDate().toEpochSecond()));
1335                 }
1336             }
1337             boolean userPackageSettingResult =
1338                     mWatchdogStorage.saveUserPackageSettings(userPackageSettingsEntries);
1339             if (!userPackageSettingResult) {
1340                 Slogf.e(TAG, "Failed to write user package settings to database");
1341             } else {
1342                 Slogf.i(TAG, "Successfully saved %d user package settings to database",
1343                         userPackageSettingsEntries.size());
1344             }
1345             if (writeStats(ioUsageStatsEntries, forgivePackagesByUserId)
1346                     && userPackageSettingResult) {
1347                 mWatchdogStorage.markWriteSuccessful();
1348             }
1349         } finally {
1350             mWatchdogStorage.endWrite();
1351         }
1352     }
1353 
1354     @GuardedBy("mLock")
getDefaultKillableStateLocked(String genericPackageName)1355     private @KillableState int getDefaultKillableStateLocked(String genericPackageName) {
1356         return mDefaultNotKillableGenericPackages.contains(genericPackageName)
1357                 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1358     }
1359 
writeStats(List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries, SparseArray<List<String>> forgivePackagesByUserId)1360     private boolean writeStats(List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries,
1361             SparseArray<List<String>> forgivePackagesByUserId) {
1362         // Forgive historical overuses before writing the latest stats to disk to avoid forgiving
1363         // the latest stats when the write is triggered after date change.
1364         if (forgivePackagesByUserId.size() != 0) {
1365             mWatchdogStorage.forgiveHistoricalOveruses(forgivePackagesByUserId,
1366                     mRecurringOverusePeriodInDays);
1367             Slogf.i(TAG, "Attempted to forgive historical overuses for %d users.",
1368                     forgivePackagesByUserId.size());
1369         }
1370         if (ioUsageStatsEntries.isEmpty()) {
1371             return true;
1372         }
1373         int result = mWatchdogStorage.saveIoUsageStats(ioUsageStatsEntries);
1374         if (result == WatchdogStorage.FAILED_TRANSACTION) {
1375             Slogf.e(TAG, "Failed to write %d I/O overuse stats to database",
1376                     ioUsageStatsEntries.size());
1377         } else {
1378             Slogf.i(TAG, "Successfully saved %d/%d I/O overuse stats to database",
1379                     result, ioUsageStatsEntries.size());
1380         }
1381         return result != WatchdogStorage.FAILED_TRANSACTION;
1382     }
1383 
1384     @GuardedBy("mLock")
applyCurrentUxRestrictionsLocked()1385     private void applyCurrentUxRestrictionsLocked() {
1386         if (mCurrentUxRestrictions == null
1387                 || mCurrentUxRestrictions.isRequiresDistractionOptimization()) {
1388             mCurrentUxState = UX_STATE_NO_DISTRACTION;
1389             return;
1390         }
1391         if (mCurrentUxState == UX_STATE_NO_INTERACTION) {
1392             return;
1393         }
1394         mCurrentUxState = UX_STATE_USER_NOTIFICATION;
1395         performOveruseHandlingLocked();
1396     }
1397 
1398     @GuardedBy("mLock")
getPackageKillableStateForUserPackageLocked( int userId, String genericPackageName, int componentType, boolean isSafeToKill)1399     private int getPackageKillableStateForUserPackageLocked(
1400             int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
1401         String key = getUserPackageUniqueId(userId, genericPackageName);
1402         PackageResourceUsage usage = mUsageByUserPackage.get(key);
1403         int defaultKillableState = getDefaultKillableStateLocked(genericPackageName);
1404         if (usage == null) {
1405             usage = new PackageResourceUsage(userId, genericPackageName, defaultKillableState);
1406         }
1407         int killableState = usage.syncAndFetchKillableState(
1408                 componentType, isSafeToKill, defaultKillableState);
1409         mUsageByUserPackage.put(key, usage);
1410         mWatchdogStorage.markDirty();
1411         return killableState;
1412     }
1413 
1414     @GuardedBy("mLock")
checkAndResetUserPackageKillableStatesLocked()1415     private void checkAndResetUserPackageKillableStatesLocked() {
1416         ZonedDateTime currentDate = mTimeSource.getCurrentDate();
1417         Instant killableStateResetDate =
1418                 currentDate.minusDays(mPackageKillableStateResetDays).toInstant();
1419         for (int i = 0; i < mUsageByUserPackage.size(); i++) {
1420             PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
1421             Instant lastModifiedDate =
1422                     usage.getKillableStateLastModifiedDate().toInstant();
1423             if (usage.getKillableState() != KILLABLE_STATE_NO
1424                     || lastModifiedDate.compareTo(killableStateResetDate) > 0) {
1425                 continue;
1426             }
1427             usage.verifyAndSetKillableState(/* isKillable= */ true, currentDate);
1428             mWatchdogStorage.markDirty();
1429             Slogf.i(TAG, "Reset killable state for package %s for user %d",
1430                     usage.genericPackageName, usage.userId);
1431         }
1432     }
1433 
1434     @GuardedBy("mLock")
notifyResourceOveruseStatsLocked(int uid, ResourceOveruseStats resourceOveruseStats)1435     private void notifyResourceOveruseStatsLocked(int uid,
1436             ResourceOveruseStats resourceOveruseStats) {
1437         String genericPackageName = resourceOveruseStats.getPackageName();
1438         ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
1439         if (listenerInfos != null) {
1440             for (int i = 0; i < listenerInfos.size(); ++i) {
1441                 listenerInfos.get(i).notifyListener(
1442                         FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
1443             }
1444         }
1445         for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
1446             ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
1447                     mOveruseSystemListenerInfosByUid.valueAt(i);
1448             for (int j = 0; j < systemListenerInfos.size(); ++j) {
1449                 systemListenerInfos.get(j).notifyListener(
1450                         FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
1451             }
1452         }
1453         if (DEBUG) {
1454             Slogf.d(TAG, "Notified resource overuse stats to listening applications");
1455         }
1456     }
1457 
checkAndHandleDateChange()1458     private void checkAndHandleDateChange() {
1459         synchronized (mLock) {
1460             ZonedDateTime currentDate = mTimeSource.getCurrentDate();
1461             if (currentDate.equals(mLatestStatsReportDate)) {
1462                 return;
1463             }
1464             mLatestStatsReportDate = currentDate;
1465             checkAndResetUserPackageKillableStatesLocked();
1466         }
1467         writeToDatabase();
1468         synchronized (mLock) {
1469             for (int i = 0; i < mUsageByUserPackage.size(); i++) {
1470                 mUsageByUserPackage.valueAt(i).resetStats();
1471             }
1472         }
1473         syncHistoricalNotForgivenOveruses();
1474         if (DEBUG) {
1475             Slogf.d(TAG, "Handled date change successfully");
1476         }
1477     }
1478 
1479     @GuardedBy("mLock")
cacheAndFetchUsageLocked(int uid, String genericPackageName, android.automotive.watchdog.IoOveruseStats internalStats, android.automotive.watchdog.PerStateBytes forgivenWriteBytes)1480     private PackageResourceUsage cacheAndFetchUsageLocked(int uid, String genericPackageName,
1481             android.automotive.watchdog.IoOveruseStats internalStats,
1482             android.automotive.watchdog.PerStateBytes forgivenWriteBytes) {
1483         int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
1484         String key = getUserPackageUniqueId(userId, genericPackageName);
1485         int defaultKillableState = getDefaultKillableStateLocked(genericPackageName);
1486         PackageResourceUsage usage = mUsageByUserPackage.get(key);
1487         if (usage == null) {
1488             usage = new PackageResourceUsage(userId, genericPackageName, defaultKillableState);
1489         }
1490         usage.update(uid, internalStats, forgivenWriteBytes, defaultKillableState);
1491         mUsageByUserPackage.put(key, usage);
1492         return usage;
1493     }
1494 
getIoOveruseStatsForPeriod(int userId, String genericPackageName, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)1495     private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName,
1496             @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
1497         synchronized (mLock) {
1498             String key = getUserPackageUniqueId(userId, genericPackageName);
1499             PackageResourceUsage usage = mUsageByUserPackage.get(key);
1500             if (usage == null) {
1501                 return null;
1502             }
1503             return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod);
1504         }
1505     }
1506 
1507     @GuardedBy("mLock")
getIoOveruseStatsLocked(PackageResourceUsage usage, long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod)1508     private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage,
1509             long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
1510         if (!usage.ioUsage.hasUsage()) {
1511             /* Return I/O overuse stats only when the package has usage for the current day.
1512              * Without the current day usage, the returned stats will contain zero remaining
1513              * bytes, which is incorrect.
1514              */
1515             return null;
1516         }
1517         IoOveruseStats currentStats = usage.getIoOveruseStats();
1518         long totalBytesWritten = currentStats.getTotalBytesWritten();
1519         int numDays = toNumDays(maxStatsPeriod);
1520         IoOveruseStats historyStats = null;
1521         if (numDays > 0) {
1522             historyStats = mWatchdogStorage.getHistoricalIoOveruseStats(
1523                     usage.userId, usage.genericPackageName, numDays - 1);
1524             totalBytesWritten += historyStats != null ? historyStats.getTotalBytesWritten() : 0;
1525         }
1526         if (totalBytesWritten < minimumBytesWritten) {
1527             return null;
1528         }
1529         if (historyStats == null) {
1530             return currentStats;
1531         }
1532         IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
1533                 historyStats.getStartTime(),
1534                 historyStats.getDurationInSeconds() + currentStats.getDurationInSeconds());
1535         statsBuilder.setTotalTimesKilled(
1536                 historyStats.getTotalTimesKilled() + currentStats.getTotalTimesKilled());
1537         statsBuilder.setTotalOveruses(
1538                 historyStats.getTotalOveruses() + currentStats.getTotalOveruses());
1539         statsBuilder.setTotalBytesWritten(
1540                 historyStats.getTotalBytesWritten() + currentStats.getTotalBytesWritten());
1541         statsBuilder.setKillableOnOveruse(currentStats.isKillableOnOveruse());
1542         statsBuilder.setRemainingWriteBytes(currentStats.getRemainingWriteBytes());
1543         return statsBuilder.build();
1544     }
1545 
1546     @GuardedBy("mLock")
addResourceOveruseListenerLocked( @arWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag, @NonNull IResourceOveruseListener listener, SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid)1547     private void addResourceOveruseListenerLocked(
1548             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
1549             @NonNull IResourceOveruseListener listener,
1550             SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
1551         int callingPid = Binder.getCallingPid();
1552         int callingUid = Binder.getCallingUid();
1553         boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
1554         String listenerType = isListenerForSystem ? "resource overuse listener for system" :
1555                 "resource overuse listener";
1556 
1557         IBinder binder = listener.asBinder();
1558         ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1559         if (listenerInfos == null) {
1560             listenerInfos = new ArrayList<>();
1561             listenerInfosByUid.put(callingUid, listenerInfos);
1562         }
1563         for (int i = 0; i < listenerInfos.size(); ++i) {
1564             if (listenerInfos.get(i).listener.asBinder() == binder) {
1565                 throw new IllegalStateException(
1566                         "Cannot add " + listenerType + " as it is already added");
1567             }
1568         }
1569 
1570         ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
1571                 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
1572         try {
1573             listenerInfo.linkToDeath();
1574         } catch (RemoteException e) {
1575             Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
1576             return;
1577         }
1578         listenerInfos.add(listenerInfo);
1579         if (DEBUG) {
1580             Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
1581                     callingPid, callingUid);
1582         }
1583     }
1584 
1585     @GuardedBy("mLock")
removeResourceOveruseListenerLocked(@onNull IResourceOveruseListener listener, SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid)1586     private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
1587             SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
1588         int callingUid = Binder.getCallingUid();
1589         String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
1590                 ? "resource overuse system listener" : "resource overuse listener";
1591         ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1592         if (listenerInfos == null) {
1593             Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
1594             return;
1595         }
1596         IBinder binder = listener.asBinder();
1597         ResourceOveruseListenerInfo cachedListenerInfo = null;
1598         for (int i = 0; i < listenerInfos.size(); ++i) {
1599             if (listenerInfos.get(i).listener.asBinder() == binder) {
1600                 cachedListenerInfo = listenerInfos.get(i);
1601                 break;
1602             }
1603         }
1604         if (cachedListenerInfo == null) {
1605             Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
1606             return;
1607         }
1608         cachedListenerInfo.unlinkToDeath();
1609         listenerInfos.remove(cachedListenerInfo);
1610         if (listenerInfos.isEmpty()) {
1611             listenerInfosByUid.remove(callingUid);
1612         }
1613         if (DEBUG) {
1614             Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
1615                     cachedListenerInfo.pid, cachedListenerInfo.uid);
1616         }
1617     }
1618 
1619     @GuardedBy("mLock")
setPendingSetResourceOveruseConfigurationsRequestLocked( List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs)1620     private void setPendingSetResourceOveruseConfigurationsRequestLocked(
1621             List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
1622         if (mPendingSetResourceOveruseConfigurationsRequest != null) {
1623             if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
1624                 return;
1625             }
1626             throw new IllegalStateException(
1627                     "Pending setResourceOveruseConfigurations request in progress");
1628         }
1629         mPendingSetResourceOveruseConfigurationsRequest = configs;
1630     }
1631 
retryPendingSetResourceOveruseConfigurations()1632     private void retryPendingSetResourceOveruseConfigurations() {
1633         List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
1634         synchronized (mLock) {
1635             if (mPendingSetResourceOveruseConfigurationsRequest == null) {
1636                 return;
1637             }
1638             configs = mPendingSetResourceOveruseConfigurationsRequest;
1639         }
1640         try {
1641             int result = setResourceOveruseConfigurationsInternal(configs,
1642                     /* isPendingRequest= */ true);
1643             if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
1644                 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
1645                         + "%d", result);
1646             }
1647         } catch (Exception e) {
1648             Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
1649         }
1650     }
1651 
setResourceOveruseConfigurationsInternal( List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs, boolean isPendingRequest)1652     private int setResourceOveruseConfigurationsInternal(
1653             List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
1654             boolean isPendingRequest) throws RemoteException {
1655         boolean doClearPendingRequest = isPendingRequest;
1656         try {
1657             mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
1658             mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
1659         } catch (RemoteException e) {
1660             if (e instanceof TransactionTooLargeException) {
1661                 throw e;
1662             }
1663             Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
1664             synchronized (mLock) {
1665                 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
1666             }
1667             doClearPendingRequest = false;
1668             return CarWatchdogManager.RETURN_CODE_SUCCESS;
1669         } finally {
1670             if (doClearPendingRequest) {
1671                 synchronized (mLock) {
1672                     mPendingSetResourceOveruseConfigurationsRequest = null;
1673                 }
1674             }
1675         }
1676         if (DEBUG) {
1677             Slogf.d(TAG, "Set the resource overuse configuration successfully");
1678         }
1679         return CarWatchdogManager.RETURN_CODE_SUCCESS;
1680     }
1681 
isConnectedToDaemon()1682     private boolean isConnectedToDaemon() {
1683         synchronized (mLock) {
1684             long startTimeMillis = SystemClock.uptimeMillis();
1685             long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1686             while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
1687                 try {
1688                     mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
1689                 } catch (InterruptedException e) {
1690                     Thread.currentThread().interrupt();
1691                     continue;
1692                 } finally {
1693                     sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1694                 }
1695                 break;
1696             }
1697             return mIsConnectedToDaemon;
1698         }
1699     }
1700 
getAliveUserIds()1701     private int[] getAliveUserIds() {
1702         UserManager userManager = mContext.getSystemService(UserManager.class);
1703         List<UserHandle> aliveUsers = userManager.getUserHandles(/* excludeDying= */ true);
1704         int userSize = aliveUsers.size();
1705         int[] userIds = new int[userSize];
1706         for (int i = 0; i < userSize; ++i) {
1707             userIds[i] = aliveUsers.get(i).getIdentifier();
1708         }
1709         return userIds;
1710     }
1711 
1712     @GuardedBy("mLock")
performOveruseHandlingLocked()1713     private void performOveruseHandlingLocked() {
1714         if (mCurrentUxState == UX_STATE_NO_DISTRACTION) {
1715             return;
1716         }
1717         if (!mUserNotifiablePackages.isEmpty()) {
1718             // Notifications are presented asynchronously, therefore the delay added by posting
1719             // to the handler should not affect the system behavior.
1720             mServiceHandler.post(this::notifyUserOnOveruse);
1721         }
1722         if (mActionableUserPackages.isEmpty() || mCurrentUxState != UX_STATE_NO_INTERACTION) {
1723             return;
1724         }
1725         ArraySet<String> killedUserPackageKeys = new ArraySet<>();
1726         for (int i = 0; i < mActionableUserPackages.size(); ++i) {
1727             PackageResourceUsage usage =
1728                     mUsageByUserPackage.get(mActionableUserPackages.valueAt(i));
1729             if (usage == null) {
1730                 continue;
1731             }
1732             // Between detecting and handling the overuse, either the package killable state or
1733             // the resource overuse configuration was updated. So, verify the killable state
1734             // before proceeding.
1735             int killableState = usage.getKillableState();
1736             if (killableState != KILLABLE_STATE_YES) {
1737                 continue;
1738             }
1739             List<String> packages;
1740             if (usage.isSharedPackage()) {
1741                 packages = mPackageInfoHandler.getPackagesForUid(usage.getUid(),
1742                         usage.genericPackageName);
1743             } else {
1744                 packages = Collections.singletonList(usage.genericPackageName);
1745             }
1746             boolean isKilled = false;
1747             for (int pkgIdx = 0; pkgIdx < packages.size(); pkgIdx++) {
1748                 String packageName = packages.get(pkgIdx);
1749                 isKilled |= disablePackageForUser(packageName, usage.userId);
1750             }
1751             if (isKilled) {
1752                 usage.ioUsage.killed();
1753                 killedUserPackageKeys.add(usage.getUniqueId());
1754             }
1755         }
1756         pushIoOveruseKillMetrics(killedUserPackageKeys);
1757         mActionableUserPackages.clear();
1758     }
1759 
notifyUserOnOveruse()1760     private void notifyUserOnOveruse() {
1761         SparseArray<String> headsUpNotificationPackagesByNotificationId = new SparseArray<>();
1762         SparseArray<String> notificationCenterPackagesByNotificationId = new SparseArray<>();
1763         int currentUserId = ActivityManager.getCurrentUser();
1764         synchronized (mLock) {
1765             for (int i = mUserNotifiablePackages.size() - 1; i >= 0; i--) {
1766                 String uniqueId = mUserNotifiablePackages.valueAt(i);
1767                 PackageResourceUsage usage = mUsageByUserPackage.get(uniqueId);
1768                 if (usage == null || (usage.userId == currentUserId
1769                         && usage.getKillableState() != KILLABLE_STATE_YES)) {
1770                     mUserNotifiablePackages.removeAt(i);
1771                     continue;
1772                 }
1773                 if (usage.userId != currentUserId) {
1774                     Slogf.i(TAG, "Skipping notification for user %d and package %s because current"
1775                                     + " user %d is different", usage.userId,
1776                             usage.genericPackageName, currentUserId);
1777                     continue;
1778                 }
1779                 List<String> packages;
1780                 if (usage.isSharedPackage()) {
1781                     packages = mPackageInfoHandler.getPackagesForUid(usage.getUid(),
1782                             usage.genericPackageName);
1783                 } else {
1784                     packages = Collections.singletonList(usage.genericPackageName);
1785                 }
1786                 for (int pkgIdx = 0; pkgIdx < packages.size(); pkgIdx++) {
1787                     String packageName = packages.get(pkgIdx);
1788                     String userPackageUniqueId = getUserPackageUniqueId(currentUserId, packageName);
1789                     if (mActiveUserNotifications.contains(userPackageUniqueId)) {
1790                         Slogf.e(TAG, "Dropping notification for user %d and package %s as it has "
1791                                 + "an active notification", currentUserId, packageName);
1792                         continue;
1793                     }
1794                     int notificationId = mResourceOveruseNotificationBaseId
1795                             + mCurrentOveruseNotificationIdOffset;
1796                     if (mCurrentUxState == UX_STATE_NO_INTERACTION || mIsHeadsUpNotificationSent) {
1797                         notificationCenterPackagesByNotificationId.put(notificationId, packageName);
1798                     } else {
1799                         headsUpNotificationPackagesByNotificationId.put(notificationId,
1800                                 packageName);
1801                         mIsHeadsUpNotificationSent = true;
1802                     }
1803                     if (mActiveUserNotificationsByNotificationId.contains(notificationId)) {
1804                         mActiveUserNotifications.remove(
1805                                 mActiveUserNotificationsByNotificationId.get(notificationId));
1806                     }
1807                     mActiveUserNotifications.add(userPackageUniqueId);
1808                     mActiveUserNotificationsByNotificationId.put(notificationId,
1809                             userPackageUniqueId);
1810                     mCurrentOveruseNotificationIdOffset = ++mCurrentOveruseNotificationIdOffset
1811                             % mResourceOveruseNotificationMaxOffset;
1812                 }
1813                 mUserNotifiablePackages.removeAt(i);
1814             }
1815         }
1816         sendResourceOveruseNotificationsAsUser(currentUserId,
1817                 headsUpNotificationPackagesByNotificationId,
1818                 notificationCenterPackagesByNotificationId);
1819         if (DEBUG) {
1820             Slogf.d(TAG, "Sent %d resource overuse notifications successfully",
1821                     headsUpNotificationPackagesByNotificationId.size()
1822                             + notificationCenterPackagesByNotificationId.size());
1823         }
1824     }
1825 
enablePackageForUser(int uid, String genericPackageName)1826     private void enablePackageForUser(int uid, String genericPackageName) {
1827         int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
1828         synchronized (mLock) {
1829             ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId);
1830             if (disabledPackages == null) {
1831                 return;
1832             }
1833         }
1834         List<String> packages;
1835         if (isSharedPackage(genericPackageName)) {
1836             packages = mPackageInfoHandler.getPackagesForUid(uid, genericPackageName);
1837         } else {
1838             packages = Collections.singletonList(genericPackageName);
1839         }
1840         for (int i = 0; i < packages.size(); i++) {
1841             String packageName = packages.get(i);
1842             try {
1843                 if (PackageManagerHelper.getApplicationEnabledSettingForUser(packageName,
1844                         userId) != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
1845                     continue;
1846                 }
1847                 synchronized (mLock) {
1848                     ArraySet<String> disabledPackages = mDisabledUserPackagesByUserId.get(userId);
1849                     if (disabledPackages == null || !disabledPackages.contains(packageName)) {
1850                         continue;
1851                     }
1852                     removeFromDisabledPackagesSettingsStringLocked(packageName, userId);
1853                     disabledPackages.remove(packageName);
1854                     if (disabledPackages.isEmpty()) {
1855                         mDisabledUserPackagesByUserId.remove(userId);
1856                     }
1857                 }
1858                 PackageManagerHelper.setApplicationEnabledSettingForUser(
1859                         packageName, COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0, userId,
1860                         mContext.getPackageName());
1861                 Slogf.i(TAG, "Enabled user '%d' package '%s'", userId, packageName);
1862             } catch (RemoteException | IllegalArgumentException e) {
1863                 Slogf.e(TAG, e, "Failed to verify and enable user %d, package '%s'", userId,
1864                         packageName);
1865             }
1866         }
1867     }
1868 
sendResourceOveruseNotificationsAsUser(@serIdInt int userId, SparseArray<String> headsUpNotificationPackagesById, SparseArray<String> notificationCenterPackagesById)1869     private void sendResourceOveruseNotificationsAsUser(@UserIdInt int userId,
1870             SparseArray<String> headsUpNotificationPackagesById,
1871             SparseArray<String> notificationCenterPackagesById) {
1872         if (headsUpNotificationPackagesById.size() == 0
1873                 && notificationCenterPackagesById.size() == 0) {
1874             return;
1875         }
1876         BuiltinPackageDependency.createNotificationHelper(mBuiltinPackageContext)
1877                 .showResourceOveruseNotificationsAsUser(
1878                         UserHandle.of(userId),
1879                         headsUpNotificationPackagesById, notificationCenterPackagesById);
1880     }
1881 
cancelNotificationAsUser(int notificationId, UserHandle userHandle)1882     private void cancelNotificationAsUser(int notificationId, UserHandle userHandle) {
1883         BuiltinPackageDependency.createNotificationHelper(mBuiltinPackageContext)
1884                         .cancelNotificationAsUser(userHandle, notificationId);
1885     }
1886 
appendToDisabledPackagesSettingsString(String packageName, @UserIdInt int userId)1887     private void appendToDisabledPackagesSettingsString(String packageName, @UserIdInt int userId) {
1888         ContentResolver contentResolverForUser = getContentResolverForUser(mContext, userId);
1889         // Appending and removing package names to/from the settings string
1890         // KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE is done only by this class. So, synchronize
1891         // these operations using the class wide lock.
1892         synchronized (mLock) {
1893             ArraySet<String> packages = extractPackages(
1894                     Settings.Secure.getString(contentResolverForUser,
1895                             KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE));
1896             if (!packages.add(packageName)) {
1897                 return;
1898             }
1899             String settingsString = constructSettingsString(packages);
1900             Settings.Secure.putString(contentResolverForUser,
1901                     KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE, settingsString);
1902             if (DEBUG) {
1903                 Slogf.d(TAG, "Appended %s to %s. New value is '%s'", packageName,
1904                         KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE, settingsString);
1905             }
1906         }
1907     }
1908 
1909     /**
1910      * Removes {@code packageName} from {@link KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE}
1911      * {@code Settings} of the given user.
1912      *
1913      * <p> Appending and removing package names to/from the settings string
1914      *     KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE is done only by this class. So, synchronize
1915      *     these operations using the class wide lock.
1916      */
1917     @GuardedBy("mLock")
removeFromDisabledPackagesSettingsStringLocked(String packageName, @UserIdInt int userId)1918     private void removeFromDisabledPackagesSettingsStringLocked(String packageName,
1919             @UserIdInt int userId) {
1920         ContentResolver contentResolverForUser = getContentResolverForUser(mContext, userId);
1921         ArraySet<String> packages = extractPackages(
1922                 Settings.Secure.getString(contentResolverForUser,
1923                         KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE));
1924         if (!packages.remove(packageName)) {
1925             return;
1926         }
1927         String settingsString = constructSettingsString(packages);
1928         Settings.Secure.putString(contentResolverForUser,
1929                 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE, settingsString);
1930         if (DEBUG) {
1931             Slogf.d(TAG, "Removed %s from %s. New value is '%s'", packageName,
1932                     KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE, settingsString);
1933         }
1934     }
1935 
1936     /**
1937      * Syncs the {@link KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE} {@code Settings} of all users
1938      * with the internal cache.
1939      *
1940      * <p> Appending and removing package names to/from the settings string
1941      *     KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE is done only by this class. So, synchronize
1942      *     these operations using the class wide lock.
1943      */
1944     @GuardedBy("mLock")
syncDisabledUserPackagesLocked()1945     private void syncDisabledUserPackagesLocked() {
1946         int[] userIds = getAliveUserIds();
1947         for (int i = 0; i < userIds.length; i++) {
1948             int userId = userIds[i];
1949             ContentResolver contentResolverForUser = getContentResolverForUser(mContext, userId);
1950             ArraySet<String> packages = extractPackages(
1951                     Settings.Secure.getString(contentResolverForUser,
1952                             KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE));
1953             if (packages.isEmpty()) {
1954                 continue;
1955             }
1956             mDisabledUserPackagesByUserId.put(userId, packages);
1957         }
1958         if (DEBUG) {
1959             Slogf.d(TAG, "Synced the %s settings to the disabled user packages cache.",
1960                     KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE);
1961         }
1962     }
1963 
extractPackages(String settingsString)1964     private static ArraySet<String> extractPackages(String settingsString) {
1965         return TextUtils.isEmpty(settingsString) ? new ArraySet<>()
1966                 : new ArraySet<>(Arrays.asList(settingsString.split(
1967                         PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR)));
1968     }
1969 
1970     @Nullable
constructSettingsString(ArraySet<String> packages)1971     private static String constructSettingsString(ArraySet<String> packages) {
1972         return packages.isEmpty() ? null :
1973                 TextUtils.join(PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR, packages);
1974     }
1975 
pushIoOveruseMetrics(ArraySet<String> userPackageKeys)1976     private void pushIoOveruseMetrics(ArraySet<String> userPackageKeys) {
1977         SparseArray<AtomsProto.CarWatchdogIoOveruseStats> statsByUid = new SparseArray<>();
1978         synchronized (mLock) {
1979             for (int i = 0; i < userPackageKeys.size(); ++i) {
1980                 String key = userPackageKeys.valueAt(i);
1981                 PackageResourceUsage usage = mUsageByUserPackage.get(key);
1982                 if (usage == null) {
1983                     Slogf.w(TAG, "Missing usage stats for user package key %s", key);
1984                     continue;
1985                 }
1986                 statsByUid.put(usage.getUid(), constructCarWatchdogIoOveruseStatsLocked(usage));
1987             }
1988         }
1989         for (int i = 0; i < statsByUid.size(); ++i) {
1990             CarStatsLog.write(CAR_WATCHDOG_IO_OVERUSE_STATS_REPORTED, statsByUid.keyAt(i),
1991                     statsByUid.valueAt(i).toByteArray());
1992         }
1993     }
1994 
pushIoOveruseKillMetrics(ArraySet<String> userPackageKeys)1995     private void pushIoOveruseKillMetrics(ArraySet<String> userPackageKeys) {
1996         int systemState;
1997         SparseArray<AtomsProto.CarWatchdogIoOveruseStats> statsByUid = new SparseArray<>();
1998         synchronized (mLock) {
1999             systemState = inferSystemStateLocked();
2000             for (int i = 0; i < userPackageKeys.size(); ++i) {
2001                 String key = userPackageKeys.valueAt(i);
2002                 PackageResourceUsage usage = mUsageByUserPackage.get(key);
2003                 if (usage == null) {
2004                     Slogf.w(TAG, "Missing usage stats for user package key %s", key);
2005                     continue;
2006                 }
2007                 statsByUid.put(usage.getUid(), constructCarWatchdogIoOveruseStatsLocked(usage));
2008             }
2009         }
2010         for (int i = 0; i < statsByUid.size(); ++i) {
2011             // TODO(b/200598815): After watchdog can classify foreground vs background apps,
2012             //  report the correct uid state.
2013             CarStatsLog.write(CAR_WATCHDOG_KILL_STATS_REPORTED, statsByUid.keyAt(i),
2014                     CAR_WATCHDOG_KILL_STATS_REPORTED__UID_STATE__UNKNOWN_UID_STATE,
2015                     systemState,
2016                     CAR_WATCHDOG_KILL_STATS_REPORTED__KILL_REASON__KILLED_ON_IO_OVERUSE,
2017                     /* arg5= */ null, statsByUid.valueAt(i).toByteArray());
2018         }
2019     }
2020 
2021     @GuardedBy("mLock")
inferSystemStateLocked()2022     private int inferSystemStateLocked() {
2023         if (mCurrentGarageMode == GarageMode.GARAGE_MODE_ON) {
2024             return CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__GARAGE_MODE;
2025         }
2026         return mCurrentUxState == UX_STATE_NO_INTERACTION
2027                 ? CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE
2028                 : CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_INTERACTION_MODE;
2029     }
2030 
2031     @GuardedBy("mLock")
constructCarWatchdogIoOveruseStatsLocked( PackageResourceUsage usage)2032     private AtomsProto.CarWatchdogIoOveruseStats constructCarWatchdogIoOveruseStatsLocked(
2033             PackageResourceUsage usage) {
2034         @ComponentType int componentType = mPackageInfoHandler.getComponentType(
2035                 usage.getUid(), usage.genericPackageName);
2036         android.automotive.watchdog.PerStateBytes threshold =
2037                 mOveruseConfigurationCache.fetchThreshold(usage.genericPackageName, componentType);
2038         android.automotive.watchdog.PerStateBytes writtenBytes =
2039                 usage.ioUsage.getInternalIoOveruseStats().writtenBytes;
2040         return constructCarWatchdogIoOveruseStats(
2041                 AtomsProto.CarWatchdogIoOveruseStats.Period.DAILY,
2042                 constructCarWatchdogPerStateBytes(threshold.foregroundBytes,
2043                         threshold.backgroundBytes, threshold.garageModeBytes),
2044                 constructCarWatchdogPerStateBytes(writtenBytes.foregroundBytes,
2045                         writtenBytes.backgroundBytes, writtenBytes.garageModeBytes));
2046     }
2047 
onPullAtom(int atomTag, List<StatsEvent> data)2048     private int onPullAtom(int atomTag, List<StatsEvent> data) {
2049         if (atomTag != CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY
2050                 && atomTag != CAR_WATCHDOG_UID_IO_USAGE_SUMMARY) {
2051             Slogf.e(TAG, "Unexpected atom tag: %d", atomTag);
2052             return PULL_SKIP;
2053         }
2054         synchronized (mLock) {
2055             if (mLastSystemIoUsageSummaryReportedDate == null
2056                     || mLastUidIoUsageSummaryReportedDate == null) {
2057                 readMetadataFileLocked();
2058             }
2059         }
2060         ZonedDateTime reportDate;
2061         switch (atomTag) {
2062             case CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY:
2063                 synchronized (mLock) {
2064                     reportDate = mLastSystemIoUsageSummaryReportedDate;
2065                 }
2066                 pullAtomsForWeeklyPeriodsSinceReportedDate(reportDate, data,
2067                         this::pullSystemIoUsageSummaryStatsEvents);
2068                 synchronized (mLock) {
2069                     mLastSystemIoUsageSummaryReportedDate = mTimeSource.getCurrentDate();
2070                 }
2071                 break;
2072             case CAR_WATCHDOG_UID_IO_USAGE_SUMMARY:
2073                 synchronized (mLock) {
2074                     reportDate = mLastUidIoUsageSummaryReportedDate;
2075                 }
2076                 pullAtomsForWeeklyPeriodsSinceReportedDate(reportDate, data,
2077                         this::pullUidIoUsageSummaryStatsEvents);
2078                 synchronized (mLock) {
2079                     mLastUidIoUsageSummaryReportedDate = mTimeSource.getCurrentDate();
2080                 }
2081                 break;
2082             default:
2083                 Slogf.i(TAG, "Skipping pull atom request on invalid watchdog atom tag: %d",
2084                         atomTag);
2085         }
2086         return PULL_SUCCESS;
2087     }
2088 
2089     @GuardedBy("mLock")
readMetadataFileLocked()2090     private void readMetadataFileLocked() {
2091         mLastSystemIoUsageSummaryReportedDate = mLastUidIoUsageSummaryReportedDate =
2092                 mTimeSource.getCurrentDate().minus(RETENTION_PERIOD);
2093         File file = getWatchdogMetadataFile();
2094         if (!file.exists()) {
2095             Slogf.e(TAG, "Watchdog metadata file '%s' doesn't exist", file.getAbsoluteFile());
2096             return;
2097         }
2098         AtomicFile atomicFile = new AtomicFile(file);
2099         try (FileInputStream fis = atomicFile.openRead()) {
2100             JsonReader reader = new JsonReader(new InputStreamReader(fis, StandardCharsets.UTF_8));
2101             reader.beginObject();
2102             while (reader.hasNext()) {
2103                 String name = reader.nextName();
2104                 switch (name) {
2105                     case SYSTEM_IO_USAGE_SUMMARY_REPORTED_DATE:
2106                         mLastSystemIoUsageSummaryReportedDate =
2107                                 ZonedDateTime.parse(reader.nextString(),
2108                                         DateTimeFormatter.ISO_DATE_TIME.withZone(ZONE_OFFSET));
2109                         break;
2110                     case UID_IO_USAGE_SUMMARY_REPORTED_DATE:
2111                         mLastUidIoUsageSummaryReportedDate =
2112                                 ZonedDateTime.parse(reader.nextString(),
2113                                         DateTimeFormatter.ISO_DATE_TIME.withZone(ZONE_OFFSET));
2114                         break;
2115                     default:
2116                         Slogf.w(TAG, "Unrecognized key: %s", name);
2117                         reader.skipValue();
2118                 }
2119             }
2120             reader.endObject();
2121             if (DEBUG) {
2122                 Slogf.e(TAG, "Successfully read watchdog metadata file '%s'",
2123                         file.getAbsoluteFile());
2124             }
2125         } catch (IOException e) {
2126             Slogf.e(TAG, e, "Failed to read watchdog metadata file '%s'", file.getAbsoluteFile());
2127         } catch (NumberFormatException | IllegalStateException | DateTimeParseException e) {
2128             Slogf.e(TAG, e, "Unexpected format in watchdog metadata file '%s'",
2129                     file.getAbsoluteFile());
2130         }
2131     }
2132 
pullAtomsForWeeklyPeriodsSinceReportedDate(ZonedDateTime reportedDate, List<StatsEvent> data, BiConsumer<Pair<ZonedDateTime, ZonedDateTime>, List<StatsEvent>> pullAtomCallback)2133     private void pullAtomsForWeeklyPeriodsSinceReportedDate(ZonedDateTime reportedDate,
2134             List<StatsEvent> data, BiConsumer<Pair<ZonedDateTime, ZonedDateTime>,
2135             List<StatsEvent>> pullAtomCallback) {
2136         ZonedDateTime now = mTimeSource.getCurrentDate();
2137         ZonedDateTime nextReportWeekStartDate = reportedDate.with(ChronoField.DAY_OF_WEEK, 1)
2138                 .truncatedTo(ChronoUnit.DAYS);
2139         while (ChronoUnit.WEEKS.between(nextReportWeekStartDate, now) > 0) {
2140             pullAtomCallback.accept(
2141                     new Pair<>(nextReportWeekStartDate, nextReportWeekStartDate.plusWeeks(1)),
2142                     data);
2143             nextReportWeekStartDate = nextReportWeekStartDate.plusWeeks(1);
2144         }
2145     }
2146 
pullSystemIoUsageSummaryStatsEvents(Pair<ZonedDateTime, ZonedDateTime> period, List<StatsEvent> data)2147     private void pullSystemIoUsageSummaryStatsEvents(Pair<ZonedDateTime, ZonedDateTime> period,
2148             List<StatsEvent> data) {
2149         List<AtomsProto.CarWatchdogDailyIoUsageSummary> dailyIoUsageSummaries =
2150                 mWatchdogStorage.getDailySystemIoUsageSummaries(
2151                         mIoUsageSummaryMinSystemTotalWrittenBytes, period.first.toEpochSecond(),
2152                         period.second.toEpochSecond());
2153         if (dailyIoUsageSummaries == null) {
2154             Slogf.i(TAG, "No system I/O usage summary stats available to pull");
2155             return;
2156         }
2157 
2158         AtomsProto.CarWatchdogEventTimePeriod evenTimePeriod =
2159                 AtomsProto.CarWatchdogEventTimePeriod.newBuilder()
2160                         .setPeriod(AtomsProto.CarWatchdogEventTimePeriod.Period.WEEKLY).build();
2161         data.add(CarStatsLog.buildStatsEvent(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY,
2162                 AtomsProto.CarWatchdogIoUsageSummary.newBuilder()
2163                         .setEventTimePeriod(evenTimePeriod)
2164                         .addAllDailyIoUsageSummary(dailyIoUsageSummaries).build()
2165                         .toByteArray(),
2166                 period.first.toEpochSecond() * 1000));
2167 
2168         Slogf.i(TAG, "Successfully pulled system I/O usage summary stats");
2169     }
2170 
pullUidIoUsageSummaryStatsEvents(Pair<ZonedDateTime, ZonedDateTime> period, List<StatsEvent> data)2171     private void pullUidIoUsageSummaryStatsEvents(Pair<ZonedDateTime, ZonedDateTime> period,
2172             List<StatsEvent> data) {
2173         // Fetch summaries for twice the top N user packages because if the UID cannot be resolved
2174         // for some user packages, the fetched summaries will still contain enough entries to pull.
2175         List<WatchdogStorage.UserPackageDailySummaries> topUsersDailyIoUsageSummaries =
2176                 mWatchdogStorage.getTopUsersDailyIoUsageSummaries(mUidIoUsageSummaryTopCount * 2,
2177                         mIoUsageSummaryMinSystemTotalWrittenBytes,
2178                         period.first.toEpochSecond(), period.second.toEpochSecond());
2179         if (topUsersDailyIoUsageSummaries == null) {
2180             Slogf.i(TAG, "No top users' I/O usage summary stats available to pull");
2181             return;
2182         }
2183 
2184         SparseArray<List<String>> genericPackageNamesByUserId = new SparseArray<>();
2185         for (int i = 0; i < topUsersDailyIoUsageSummaries.size(); ++i) {
2186             WatchdogStorage.UserPackageDailySummaries entry =
2187                     topUsersDailyIoUsageSummaries.get(i);
2188             List<String> genericPackageNames = genericPackageNamesByUserId.get(entry.userId);
2189             if (genericPackageNames == null) {
2190                 genericPackageNames = new ArrayList<>();
2191             }
2192             genericPackageNames.add(entry.packageName);
2193             genericPackageNamesByUserId.put(entry.userId, genericPackageNames);
2194         }
2195 
2196         SparseArray<Map<String, Integer>> packageUidsByUserId =
2197                 getPackageUidsForUsers(genericPackageNamesByUserId);
2198 
2199         AtomsProto.CarWatchdogEventTimePeriod.Builder evenTimePeriodBuilder =
2200                 AtomsProto.CarWatchdogEventTimePeriod.newBuilder()
2201                         .setPeriod(AtomsProto.CarWatchdogEventTimePeriod.Period.WEEKLY);
2202 
2203         long startEpochMillis = period.first.toEpochSecond() * 1000;
2204         int numPulledUidSummaryStats = 0;
2205         for (int i = 0; i < topUsersDailyIoUsageSummaries.size()
2206                 && numPulledUidSummaryStats < mUidIoUsageSummaryTopCount; ++i) {
2207             WatchdogStorage.UserPackageDailySummaries entry = topUsersDailyIoUsageSummaries.get(i);
2208             Map<String, Integer> uidsByGenericPackageName = packageUidsByUserId.get(entry.userId);
2209             if (uidsByGenericPackageName == null
2210                     || !uidsByGenericPackageName.containsKey(entry.packageName)) {
2211                 Slogf.e(TAG, "Failed to fetch uid for package %s and user %d. So, skipping "
2212                         + "reporting stats for this user package", entry.packageName, entry.userId);
2213                 continue;
2214             }
2215             data.add(CarStatsLog.buildStatsEvent(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY,
2216                     uidsByGenericPackageName.get(entry.packageName),
2217                     AtomsProto.CarWatchdogIoUsageSummary.newBuilder()
2218                             .setEventTimePeriod(evenTimePeriodBuilder)
2219                             .addAllDailyIoUsageSummary(entry.dailyIoUsageSummaries).build()
2220                             .toByteArray(),
2221                     startEpochMillis));
2222             ++numPulledUidSummaryStats;
2223         }
2224 
2225         Slogf.e(TAG, "Successfully pulled top %d users' I/O usage summary stats",
2226                 numPulledUidSummaryStats);
2227     }
2228 
2229     /**
2230      * Gets the UID for the resource usage's user package.
2231      *
2232      * <p>If the package resource usage's UID is not valid, fetches the UID for the user package
2233      * from the package manager. Returns {@code INVALID_UID} if the package manager cannot find the
2234      * package. </p>
2235      */
getOrFetchUid(PackageResourceUsage usage, String packageName)2236     private int getOrFetchUid(PackageResourceUsage usage, String packageName) {
2237         int uid = usage.getUid();
2238         if (uid == INVALID_UID) {
2239             uid = getPackageUidAsUser(mContext.getPackageManager(), packageName,
2240                     usage.userId);
2241         }
2242         return uid;
2243     }
2244 
getPackageUidsForUsers( SparseArray<List<String>> genericPackageNamesByUserId)2245     private SparseArray<Map<String, Integer>> getPackageUidsForUsers(
2246             SparseArray<List<String>> genericPackageNamesByUserId) {
2247         PackageManager pm = mContext.getPackageManager();
2248         SparseArray<Map<String, Integer>> packageUidsByUserId = new SparseArray<>();
2249         for (int i = 0; i < genericPackageNamesByUserId.size(); ++i) {
2250             int userId = genericPackageNamesByUserId.keyAt(i);
2251             Map<String, Integer> uidsByGenericPackageName = getPackageUidsForUser(pm,
2252                     genericPackageNamesByUserId.valueAt(i), userId);
2253             if (!uidsByGenericPackageName.isEmpty()) {
2254                 packageUidsByUserId.put(userId, uidsByGenericPackageName);
2255             }
2256         }
2257         return packageUidsByUserId;
2258     }
2259 
2260     /**
2261      * Returns UIDs for the given generic package names belonging to the given user.
2262      *
2263      * <p>{@code pm.getInstalledPackagesAsUser} call is expensive as it fetches all installed
2264      * packages for the given user. Thus this method should be called for all packages that requires
2265      * the UIDs to be resolved in a single call.
2266      */
getPackageUidsForUser(PackageManager pm, List<String> genericPackageNames, int userId)2267     private Map<String, Integer> getPackageUidsForUser(PackageManager pm,
2268             List<String> genericPackageNames, int userId) {
2269         Map<String, Integer> uidsByGenericPackageNames = new ArrayMap<>();
2270         Set<String> resolveSharedUserIds = new ArraySet<>();
2271         for (int i = 0; i < genericPackageNames.size(); ++i) {
2272             String genericPackageName = genericPackageNames.get(i);
2273             PackageResourceUsage usage;
2274             synchronized (mLock) {
2275                 usage = mUsageByUserPackage.get(getUserPackageUniqueId(userId,
2276                         genericPackageName));
2277             }
2278             if (usage != null && usage.getUid() != INVALID_UID) {
2279                 uidsByGenericPackageNames.put(genericPackageName, usage.getUid());
2280                 continue;
2281             }
2282             if (isSharedPackage(genericPackageName)) {
2283                 resolveSharedUserIds.add(
2284                         genericPackageName.substring(SHARED_PACKAGE_PREFIX.length()));
2285                 continue;
2286             }
2287             int uid = getPackageUidAsUser(pm, genericPackageName, userId);
2288             if (uid != INVALID_UID) {
2289                 uidsByGenericPackageNames.put(genericPackageName, uid);
2290             }
2291         }
2292         if (resolveSharedUserIds.isEmpty()) {
2293             return uidsByGenericPackageNames;
2294         }
2295         List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */ 0, userId);
2296         for (int i = 0; i < packageInfos.size() && !resolveSharedUserIds.isEmpty(); ++i) {
2297             PackageInfo packageInfo = packageInfos.get(i);
2298             if (packageInfo.sharedUserId == null
2299                     || !resolveSharedUserIds.contains(packageInfo.sharedUserId)) {
2300                 continue;
2301             }
2302             int uid = getPackageUidAsUser(pm, packageInfo.packageName, userId);
2303             if (uid != INVALID_UID) {
2304                 uidsByGenericPackageNames.put(SHARED_PACKAGE_PREFIX + packageInfo.sharedUserId,
2305                         uid);
2306             }
2307             resolveSharedUserIds.remove(packageInfo.sharedUserId);
2308         }
2309         return uidsByGenericPackageNames;
2310     }
2311 
getPackageUidAsUser(PackageManager pm, String packageName, @UserIdInt int userId)2312     private int getPackageUidAsUser(PackageManager pm, String packageName, @UserIdInt int userId) {
2313         try {
2314             return PackageManagerHelper.getPackageUidAsUser(pm, packageName, userId);
2315         } catch (PackageManager.NameNotFoundException e) {
2316             Slogf.e(TAG, "Package %s for user %d is not found", packageName, userId);
2317             return INVALID_UID;
2318         }
2319     }
2320 
getWatchdogMetadataFile()2321     private static File getWatchdogMetadataFile() {
2322         return new File(CarWatchdogService.getWatchdogDirFile(), METADATA_FILENAME);
2323     }
2324 
getUserPackageUniqueId(@serIdInt int userId, String genericPackageName)2325     private static String getUserPackageUniqueId(@UserIdInt int userId, String genericPackageName) {
2326         return userId + USER_PACKAGE_SEPARATOR + genericPackageName;
2327     }
2328 
2329     @VisibleForTesting
toIoOveruseStatsBuilder( android.automotive.watchdog.IoOveruseStats internalStats, int totalTimesKilled, boolean isKillableOnOveruses)2330     static IoOveruseStats.Builder toIoOveruseStatsBuilder(
2331             android.automotive.watchdog.IoOveruseStats internalStats,
2332             int totalTimesKilled, boolean isKillableOnOveruses) {
2333         return new IoOveruseStats.Builder(internalStats.startTime, internalStats.durationInSeconds)
2334                 .setTotalOveruses(internalStats.totalOveruses)
2335                 .setTotalTimesKilled(totalTimesKilled)
2336                 .setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes))
2337                 .setKillableOnOveruse(isKillableOnOveruses)
2338                 .setRemainingWriteBytes(toPerStateBytes(internalStats.remainingWriteBytes));
2339     }
2340 
toPerStateBytes( android.automotive.watchdog.PerStateBytes internalPerStateBytes)2341     private static PerStateBytes toPerStateBytes(
2342             android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
2343         return new PerStateBytes(internalPerStateBytes.foregroundBytes,
2344                 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
2345     }
2346 
totalPerStateBytes( android.automotive.watchdog.PerStateBytes internalPerStateBytes)2347     private static long totalPerStateBytes(
2348             android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
2349         BiFunction<Long, Long, Long> sum = (l, r) -> {
2350             return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
2351         };
2352         return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
2353                 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
2354     }
2355 
getMinimumBytesWritten( @arWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag)2356     private static long getMinimumBytesWritten(
2357             @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
2358         switch (minimumStatsIoFlag) {
2359             case 0:
2360                 return 0;
2361             case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
2362                 return 1024 * 1024;
2363             case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
2364                 return 100 * 1024 * 1024;
2365             case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
2366                 return 1024 * 1024 * 1024;
2367             default:
2368                 throw new IllegalArgumentException(
2369                         "Must provide valid minimum stats flag for I/O resource");
2370         }
2371     }
2372 
2373     private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)2374             toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
2375             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
2376         android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
2377                 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
2378         internalConfig.componentType = config.getComponentType();
2379         internalConfig.safeToKillPackages = config.getSafeToKillPackages();
2380         internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
2381         internalConfig.packageMetadata = new ArrayList<>();
2382         for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
2383             if (entry.getKey().isEmpty()) {
2384                 continue;
2385             }
2386             PackageMetadata metadata = new PackageMetadata();
2387             metadata.packageName = entry.getKey();
2388             switch(entry.getValue()) {
2389                 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
2390                     metadata.appCategoryType = ApplicationCategoryType.MAPS;
2391                     break;
2392                 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
2393                     metadata.appCategoryType = ApplicationCategoryType.MEDIA;
2394                     break;
2395                 default:
2396                     Slogf.i(TAG, "Invalid application category type: %s skipping package: %s",
2397                             entry.getValue(), metadata.packageName);
2398                     continue;
2399             }
2400             internalConfig.packageMetadata.add(metadata);
2401         }
2402         internalConfig.resourceSpecificConfigurations = new ArrayList<>();
2403         if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
2404                 && config.getIoOveruseConfiguration() != null) {
2405             internalConfig.resourceSpecificConfigurations.add(
2406                     toResourceSpecificConfiguration(config.getComponentType(),
2407                             config.getIoOveruseConfiguration()));
2408         }
2409         return internalConfig;
2410     }
2411 
2412     private static ResourceSpecificConfiguration
toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config)2413             toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
2414         android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
2415                 new android.automotive.watchdog.internal.IoOveruseConfiguration();
2416         internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
2417                 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
2418         internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
2419                 config.getPackageSpecificThresholds());
2420         internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
2421                 config.getAppCategorySpecificThresholds());
2422         for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) {
2423             PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i);
2424             switch(threshold.name) {
2425                 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
2426                     threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
2427                     break;
2428                 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
2429                     threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
2430                     break;
2431                 default:
2432                     threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
2433             }
2434         }
2435         internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
2436                 config.getSystemWideThresholds());
2437 
2438         ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
2439         resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
2440         return resourceSpecificConfig;
2441     }
2442 
2443     @VisibleForTesting
toComponentTypeStr(int componentType)2444     static String toComponentTypeStr(int componentType) {
2445         switch(componentType) {
2446             case ComponentType.SYSTEM:
2447                 return "SYSTEM";
2448             case ComponentType.VENDOR:
2449                 return "VENDOR";
2450             case ComponentType.THIRD_PARTY:
2451                 return "THIRD_PARTY";
2452             default:
2453                 return "UNKNOWN";
2454         }
2455     }
2456 
toPerStateIoOveruseThresholds( Map<String, PerStateBytes> thresholds)2457     private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
2458             Map<String, PerStateBytes> thresholds) {
2459         List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
2460         for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
2461             if (!thresholds.isEmpty()) {
2462                 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
2463                         entry.getValue()));
2464             }
2465         }
2466         return internalThresholds;
2467     }
2468 
toPerStateIoOveruseThreshold(String name, PerStateBytes perStateBytes)2469     private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
2470             PerStateBytes perStateBytes) {
2471         PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
2472         threshold.name = name;
2473         threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
2474         threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
2475         threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
2476         threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
2477         return threshold;
2478     }
2479 
2480     private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds)2481             toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
2482         List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
2483                 new ArrayList<>();
2484         for (int i = 0; i < thresholds.size(); ++i) {
2485             if (thresholds.get(i).getDurationInSeconds() == 0
2486                     || thresholds.get(i).getWrittenBytesPerSecond() == 0) {
2487                 continue;
2488             }
2489             android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
2490                     new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
2491             internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds();
2492             internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond();
2493             internalThresholds.add(internalThreshold);
2494         }
2495         return internalThresholds;
2496     }
2497 
toResourceOveruseConfiguration( android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)2498     private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
2499             android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
2500             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
2501         ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
2502         for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) {
2503             String categoryTypeStr;
2504             switch (internalConfig.packageMetadata.get(i).appCategoryType) {
2505                 case ApplicationCategoryType.MAPS:
2506                     categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
2507                     break;
2508                 case ApplicationCategoryType.MEDIA:
2509                     categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
2510                     break;
2511                 default:
2512                     continue;
2513             }
2514             packagesToAppCategoryTypes.put(
2515                     internalConfig.packageMetadata.get(i).packageName, categoryTypeStr);
2516         }
2517         ResourceOveruseConfiguration.Builder configBuilder =
2518                 new ResourceOveruseConfiguration.Builder(
2519                 internalConfig.componentType,
2520                 internalConfig.safeToKillPackages,
2521                 internalConfig.vendorPackagePrefixes,
2522                 packagesToAppCategoryTypes);
2523         for (ResourceSpecificConfiguration resourceSpecificConfig :
2524                 internalConfig.resourceSpecificConfigurations) {
2525             if (resourceSpecificConfig.getTag()
2526                     == ResourceSpecificConfiguration.ioOveruseConfiguration
2527                     && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
2528                 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
2529                         resourceSpecificConfig.getIoOveruseConfiguration()));
2530             }
2531         }
2532         return configBuilder.build();
2533     }
2534 
toIoOveruseConfiguration( android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig)2535     private static IoOveruseConfiguration toIoOveruseConfiguration(
2536             android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
2537         PerStateBytes componentLevelThresholds =
2538                 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
2539         ArrayMap<String, PerStateBytes> packageSpecificThresholds =
2540                 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
2541         ArrayMap<String, PerStateBytes> appCategorySpecificThresholds =
2542                 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
2543         replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
2544                 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
2545         replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
2546                 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
2547         List<IoOveruseAlertThreshold> systemWideThresholds =
2548                 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
2549 
2550         IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
2551                 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
2552                 systemWideThresholds);
2553         return configBuilder.build();
2554     }
2555 
toPerStateBytesMap( List<PerStateIoOveruseThreshold> thresholds)2556     private static ArrayMap<String, PerStateBytes> toPerStateBytesMap(
2557             List<PerStateIoOveruseThreshold> thresholds) {
2558         ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
2559         for (int i = 0; i < thresholds.size(); ++i) {
2560             thresholdsMap.put(
2561                     thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes));
2562         }
2563         return thresholdsMap;
2564     }
2565 
toIoOveruseAlertThresholds( List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds)2566     private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
2567             List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
2568         List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
2569         for (int i = 0; i < internalThresholds.size(); ++i) {
2570             thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds,
2571                     internalThresholds.get(i).writtenBytesPerSecond));
2572         }
2573         return thresholds;
2574     }
2575 
checkResourceOveruseConfigs( List<ResourceOveruseConfiguration> configurations, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)2576     private static void checkResourceOveruseConfigs(
2577             List<ResourceOveruseConfiguration> configurations,
2578             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
2579         ArraySet<Integer> seenComponentTypes = new ArraySet<>();
2580         for (int i = 0; i < configurations.size(); ++i) {
2581             ResourceOveruseConfiguration config = configurations.get(i);
2582             if (seenComponentTypes.contains(config.getComponentType())) {
2583                 throw new IllegalArgumentException(
2584                         "Cannot provide duplicate configurations for the same component type");
2585             }
2586             checkResourceOveruseConfig(config, resourceOveruseFlag);
2587             seenComponentTypes.add(config.getComponentType());
2588         }
2589     }
2590 
checkResourceOveruseConfig(ResourceOveruseConfiguration config, @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)2591     private static void checkResourceOveruseConfig(ResourceOveruseConfiguration config,
2592             @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
2593         int componentType = config.getComponentType();
2594         if (Objects.equals(toComponentTypeStr(componentType), "UNKNOWN")) {
2595             throw new IllegalArgumentException(
2596                     "Invalid component type in the configuration: " + componentType);
2597         }
2598         if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
2599                 && config.getIoOveruseConfiguration() == null) {
2600             throw new IllegalArgumentException("Must provide I/O overuse configuration");
2601         }
2602         checkIoOveruseConfig(config.getIoOveruseConfiguration(), componentType);
2603     }
2604 
checkIoOveruseConfig(IoOveruseConfiguration config, int componentType)2605     private static void checkIoOveruseConfig(IoOveruseConfiguration config, int componentType) {
2606         if (config.getComponentLevelThresholds().getBackgroundModeBytes() <= 0
2607                 || config.getComponentLevelThresholds().getForegroundModeBytes() <= 0
2608                 || config.getComponentLevelThresholds().getGarageModeBytes() <= 0) {
2609             throw new IllegalArgumentException(
2610                     "For component: " + toComponentTypeStr(componentType)
2611                             + " some thresholds are zero for: "
2612                             + config.getComponentLevelThresholds().toString());
2613         }
2614         if (componentType == ComponentType.SYSTEM) {
2615             List<IoOveruseAlertThreshold> systemThresholds = config.getSystemWideThresholds();
2616             if (systemThresholds.isEmpty()) {
2617                 throw new IllegalArgumentException(
2618                         "Empty system-wide alert thresholds provided in "
2619                                 + toComponentTypeStr(componentType)
2620                                 + " config.");
2621             }
2622             for (int i = 0; i < systemThresholds.size(); i++) {
2623                 checkIoOveruseAlertThreshold(systemThresholds.get(i));
2624             }
2625         }
2626     }
2627 
checkIoOveruseAlertThreshold( IoOveruseAlertThreshold ioOveruseAlertThreshold)2628     private static void checkIoOveruseAlertThreshold(
2629             IoOveruseAlertThreshold ioOveruseAlertThreshold) {
2630         if (ioOveruseAlertThreshold.getDurationInSeconds() <= 0) {
2631             throw new IllegalArgumentException(
2632                     "System wide threshold duration must be greater than zero for: "
2633                             + ioOveruseAlertThreshold);
2634         }
2635         if (ioOveruseAlertThreshold.getWrittenBytesPerSecond() <= 0) {
2636             throw new IllegalArgumentException(
2637                     "System wide threshold written bytes per second must be greater than zero for: "
2638                             + ioOveruseAlertThreshold);
2639         }
2640     }
2641 
isSharedPackage(String genericPackageName)2642     private static boolean isSharedPackage(String genericPackageName) {
2643         return genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
2644     }
2645 
replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey)2646     private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
2647         PerStateBytes perStateBytes = map.get(oldKey);
2648         if (perStateBytes != null) {
2649             map.put(newKey, perStateBytes);
2650             map.remove(oldKey);
2651         }
2652     }
2653 
toNumDays(@arWatchdogManager.StatsPeriod int maxStatsPeriod)2654     private static int toNumDays(@CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
2655         switch(maxStatsPeriod) {
2656             case STATS_PERIOD_CURRENT_DAY:
2657                 return 0;
2658             case STATS_PERIOD_PAST_3_DAYS:
2659                 return 3;
2660             case STATS_PERIOD_PAST_7_DAYS:
2661                 return 7;
2662             case STATS_PERIOD_PAST_15_DAYS:
2663                 return 15;
2664             case STATS_PERIOD_PAST_30_DAYS:
2665                 return 30;
2666             default:
2667                 throw new IllegalArgumentException(
2668                         "Invalid max stats period provided: " + maxStatsPeriod);
2669         }
2670     }
2671 
2672     @VisibleForTesting
constructCarWatchdogIoOveruseStats( AtomsProto.CarWatchdogIoOveruseStats.Period period, AtomsProto.CarWatchdogPerStateBytes threshold, AtomsProto.CarWatchdogPerStateBytes writtenBytes)2673     static AtomsProto.CarWatchdogIoOveruseStats constructCarWatchdogIoOveruseStats(
2674             AtomsProto.CarWatchdogIoOveruseStats.Period period,
2675             AtomsProto.CarWatchdogPerStateBytes threshold,
2676             AtomsProto.CarWatchdogPerStateBytes writtenBytes) {
2677         // TODO(b/184310189): Report uptime once daemon pushes it to CarService.
2678         return AtomsProto.CarWatchdogIoOveruseStats.newBuilder()
2679                 .setPeriod(period)
2680                 .setThreshold(threshold)
2681                 .setWrittenBytes(writtenBytes).build();
2682     }
2683 
2684     @VisibleForTesting
constructCarWatchdogPerStateBytes( long foregroundBytes, long backgroundBytes, long garageModeBytes)2685     static AtomsProto.CarWatchdogPerStateBytes constructCarWatchdogPerStateBytes(
2686             long foregroundBytes, long backgroundBytes, long garageModeBytes) {
2687         AtomsProto.CarWatchdogPerStateBytes.Builder perStateBytesBuilder =
2688                 AtomsProto.CarWatchdogPerStateBytes.newBuilder();
2689         if (foregroundBytes != 0) {
2690             perStateBytesBuilder.setForegroundBytes(foregroundBytes);
2691         }
2692         if (backgroundBytes != 0) {
2693             perStateBytesBuilder.setBackgroundBytes(backgroundBytes);
2694         }
2695         if (garageModeBytes != 0) {
2696             perStateBytesBuilder.setGarageModeBytes(garageModeBytes);
2697         }
2698         return perStateBytesBuilder.build();
2699     }
2700 
toEnabledStateString(int enabledState)2701     private static String toEnabledStateString(int enabledState) {
2702         switch (enabledState) {
2703             case COMPONENT_ENABLED_STATE_DEFAULT:
2704                 return "COMPONENT_ENABLED_STATE_DEFAULT";
2705             case COMPONENT_ENABLED_STATE_ENABLED:
2706                 return "COMPONENT_ENABLED_STATE_ENABLED";
2707             case COMPONENT_ENABLED_STATE_DISABLED:
2708                 return "COMPONENT_ENABLED_STATE_DISABLED";
2709             case COMPONENT_ENABLED_STATE_DISABLED_USER:
2710                 return "COMPONENT_ENABLED_STATE_DISABLED_USER";
2711             case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
2712                 return "COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED";
2713             default:
2714                 return "UNKNOWN COMPONENT ENABLED STATE";
2715         }
2716     }
2717 
toUxStateString(@xStateType int uxState)2718     private static String toUxStateString(@UxStateType int uxState) {
2719         switch (uxState) {
2720             case UX_STATE_NO_DISTRACTION:
2721                 return "UX_STATE_NO_DISTRACTION";
2722             case UX_STATE_USER_NOTIFICATION:
2723                 return "UX_STATE_USER_NOTIFICATION";
2724             case UX_STATE_NO_INTERACTION:
2725                 return "UX_STATE_NO_INTERACTION";
2726             default:
2727                 return "UNKNOWN UX STATE";
2728         }
2729     }
2730 
2731     private final class PackageResourceUsage {
2732         public final String genericPackageName;
2733         public @UserIdInt final int userId;
2734         public final PackageIoUsage ioUsage = new PackageIoUsage();
2735         private @KillableState int mKillableState;
2736         public ZonedDateTime mKillableStateLastModifiedDate;
2737         private int mUid;
2738 
2739         /** Must be called only after acquiring {@link mLock} */
PackageResourceUsage(@serIdInt int userId, String genericPackageName, @KillableState int defaultKillableState)2740         PackageResourceUsage(@UserIdInt int userId, String genericPackageName,
2741                 @KillableState int defaultKillableState) {
2742             this.genericPackageName = genericPackageName;
2743             this.userId = userId;
2744             this.mKillableState = defaultKillableState;
2745             this.mKillableStateLastModifiedDate = mTimeSource.getCurrentDate();
2746             this.mUid = INVALID_UID;
2747         }
2748 
isSharedPackage()2749         public boolean isSharedPackage() {
2750             return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
2751         }
2752 
getUniqueId()2753         public String getUniqueId() {
2754             return getUserPackageUniqueId(userId, genericPackageName);
2755         }
2756 
getUid()2757         public int getUid() {
2758             return mUid;
2759         }
2760 
update(int uid, android.automotive.watchdog.IoOveruseStats internalStats, android.automotive.watchdog.PerStateBytes forgivenWriteBytes, @KillableState int defaultKillableState)2761         public void update(int uid, android.automotive.watchdog.IoOveruseStats internalStats,
2762                 android.automotive.watchdog.PerStateBytes forgivenWriteBytes,
2763                 @KillableState int defaultKillableState) {
2764             // Package UID would change if it was re-installed, so keep it up-to-date.
2765             mUid = uid;
2766             if (!internalStats.killableOnOveruse) {
2767                 /*
2768                  * Killable value specified in the internal stats is provided by the native daemon.
2769                  * This value reflects whether or not an application is safe-to-kill on overuse.
2770                  * This setting is from the I/O overuse configuration specified by the system and
2771                  * vendor services and doesn't reflect the user choices. Thus if the internal stats
2772                  * specify the application is not killable, the application is not safe-to-kill.
2773                  */
2774                 mKillableState = KILLABLE_STATE_NEVER;
2775             } else if (mKillableState == KILLABLE_STATE_NEVER) {
2776                 /*
2777                  * This case happens when a previously unsafe to kill system/vendor package was
2778                  * recently marked as safe-to-kill so update the old state to the default value.
2779                  */
2780                 mKillableState = defaultKillableState;
2781             }
2782             ioUsage.update(internalStats, forgivenWriteBytes);
2783         }
2784 
getResourceOveruseStatsBuilder()2785         public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
2786             return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
2787         }
2788 
2789 
getIoOveruseStats()2790         public IoOveruseStats getIoOveruseStats() {
2791             if (!ioUsage.hasUsage()) {
2792                 return null;
2793             }
2794             return ioUsage.getIoOveruseStats(mKillableState != KILLABLE_STATE_NEVER);
2795         }
2796 
getKillableState()2797         public @KillableState int getKillableState() {
2798             return mKillableState;
2799         }
2800 
setKillableState(@illableState int killableState, ZonedDateTime modifiedDate)2801         public void setKillableState(@KillableState int killableState, ZonedDateTime modifiedDate) {
2802             mKillableState = killableState;
2803             mKillableStateLastModifiedDate = modifiedDate;
2804         }
2805 
verifyAndSetKillableState(boolean isKillable, ZonedDateTime modifiedDate)2806         public boolean verifyAndSetKillableState(boolean isKillable, ZonedDateTime modifiedDate) {
2807             if (mKillableState == KILLABLE_STATE_NEVER) {
2808                 return false;
2809             }
2810             mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
2811             mKillableStateLastModifiedDate = modifiedDate;
2812             return true;
2813         }
2814 
syncAndFetchKillableState(int myComponentType, boolean isSafeToKill, @KillableState int defaultKillableState)2815         public int syncAndFetchKillableState(int myComponentType, boolean isSafeToKill,
2816                 @KillableState int defaultKillableState) {
2817             /*
2818              * The killable state goes out-of-sync:
2819              * 1. When the on-device safe-to-kill list was recently updated and the user package
2820              * didn't have any resource usage so the native daemon didn't update the killable state.
2821              * 2. When a package has no resource usage and is initialized outside of processing the
2822              * latest resource usage stats.
2823              */
2824             if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
2825                 mKillableState = KILLABLE_STATE_NEVER;
2826             } else if (mKillableState == KILLABLE_STATE_NEVER) {
2827                 mKillableState = defaultKillableState;
2828             }
2829             return mKillableState;
2830         }
2831 
getKillableStateLastModifiedDate()2832         public ZonedDateTime getKillableStateLastModifiedDate() {
2833             return mKillableStateLastModifiedDate;
2834         }
2835 
resetStats()2836         public void resetStats() {
2837             ioUsage.resetStats();
2838         }
2839     }
2840 
2841     /** Defines I/O usage fields for a package. */
2842     public static final class PackageIoUsage {
2843         private static final android.automotive.watchdog.PerStateBytes DEFAULT_PER_STATE_BYTES =
2844                 new android.automotive.watchdog.PerStateBytes();
2845         private static final int MISSING_VALUE = -1;
2846 
2847         private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
2848         private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
2849         private int mForgivenOveruses;
2850         private int mHistoricalNotForgivenOveruses;
2851         private int mTotalTimesKilled;
2852 
PackageIoUsage()2853         private PackageIoUsage() {
2854             mForgivenWriteBytes = DEFAULT_PER_STATE_BYTES;
2855             mForgivenOveruses = 0;
2856             mHistoricalNotForgivenOveruses = MISSING_VALUE;
2857             mTotalTimesKilled = 0;
2858         }
2859 
PackageIoUsage(android.automotive.watchdog.IoOveruseStats ioOveruseStats, android.automotive.watchdog.PerStateBytes forgivenWriteBytes, int forgivenOveruses, int totalTimesKilled)2860         public PackageIoUsage(android.automotive.watchdog.IoOveruseStats ioOveruseStats,
2861                 android.automotive.watchdog.PerStateBytes forgivenWriteBytes, int forgivenOveruses,
2862                 int totalTimesKilled) {
2863             mIoOveruseStats = ioOveruseStats;
2864             mForgivenWriteBytes = forgivenWriteBytes;
2865             mForgivenOveruses = forgivenOveruses;
2866             mTotalTimesKilled = totalTimesKilled;
2867             mHistoricalNotForgivenOveruses = MISSING_VALUE;
2868         }
2869 
2870         /** Returns the I/O overuse stats related to the package. */
getInternalIoOveruseStats()2871         public android.automotive.watchdog.IoOveruseStats getInternalIoOveruseStats() {
2872             return mIoOveruseStats;
2873         }
2874 
2875         /** Returns the forgiven write bytes. */
getForgivenWriteBytes()2876         public android.automotive.watchdog.PerStateBytes getForgivenWriteBytes() {
2877             return mForgivenWriteBytes;
2878         }
2879 
2880         /** Returns the number of forgiven overuses today. */
getForgivenOveruses()2881         public int getForgivenOveruses() {
2882             return mForgivenOveruses;
2883         }
2884 
2885         /**
2886          * Returns the number of not forgiven overuses. These are overuses that have not been
2887          * attributed previously to a package's recurring overuse.
2888          */
getNotForgivenOveruses()2889         public int getNotForgivenOveruses() {
2890             if (!hasUsage()) {
2891                 return 0;
2892             }
2893             int historicalNotForgivenOveruses =
2894                     mHistoricalNotForgivenOveruses != MISSING_VALUE
2895                             ? mHistoricalNotForgivenOveruses : 0;
2896             return (mIoOveruseStats.totalOveruses - mForgivenOveruses)
2897                     + historicalNotForgivenOveruses;
2898         }
2899 
2900         /** Sets historical not forgiven overuses. */
setHistoricalNotForgivenOveruses(int historicalNotForgivenOveruses)2901         public void setHistoricalNotForgivenOveruses(int historicalNotForgivenOveruses) {
2902             mHistoricalNotForgivenOveruses = historicalNotForgivenOveruses;
2903         }
2904 
2905         /** Forgives all the I/O overuse stats' overuses. */
forgiveOveruses()2906         public void forgiveOveruses() {
2907             if (!hasUsage()) {
2908                 return;
2909             }
2910             mForgivenOveruses = mIoOveruseStats.totalOveruses;
2911             mHistoricalNotForgivenOveruses = 0;
2912         }
2913 
2914         /** Returns the total number of times the package was killed. */
getTotalTimesKilled()2915         public int getTotalTimesKilled() {
2916             return mTotalTimesKilled;
2917         }
2918 
shouldForgiveHistoricalOveruses()2919         boolean shouldForgiveHistoricalOveruses() {
2920             return mHistoricalNotForgivenOveruses != MISSING_VALUE;
2921         }
2922 
hasUsage()2923         boolean hasUsage() {
2924             return mIoOveruseStats != null;
2925         }
2926 
overwrite(PackageIoUsage ioUsage)2927         void overwrite(PackageIoUsage ioUsage) {
2928             mIoOveruseStats = ioUsage.mIoOveruseStats;
2929             mForgivenWriteBytes = ioUsage.mForgivenWriteBytes;
2930             mTotalTimesKilled = ioUsage.mTotalTimesKilled;
2931             mHistoricalNotForgivenOveruses = ioUsage.mHistoricalNotForgivenOveruses;
2932         }
2933 
update(android.automotive.watchdog.IoOveruseStats internalStats, android.automotive.watchdog.PerStateBytes forgivenWriteBytes)2934         void update(android.automotive.watchdog.IoOveruseStats internalStats,
2935                 android.automotive.watchdog.PerStateBytes forgivenWriteBytes) {
2936             mIoOveruseStats = internalStats;
2937             mForgivenWriteBytes = forgivenWriteBytes;
2938         }
2939 
getIoOveruseStats(boolean isKillable)2940         IoOveruseStats getIoOveruseStats(boolean isKillable) {
2941             return toIoOveruseStatsBuilder(mIoOveruseStats, mTotalTimesKilled, isKillable).build();
2942         }
2943 
exceedsThreshold()2944         boolean exceedsThreshold() {
2945             if (!hasUsage()) {
2946                 return false;
2947             }
2948             android.automotive.watchdog.PerStateBytes remaining =
2949                     mIoOveruseStats.remainingWriteBytes;
2950             return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
2951                     || remaining.garageModeBytes == 0;
2952         }
2953 
killed()2954         void killed() {
2955             ++mTotalTimesKilled;
2956         }
2957 
resetStats()2958         void resetStats() {
2959             mIoOveruseStats = null;
2960             mForgivenWriteBytes = DEFAULT_PER_STATE_BYTES;
2961             mForgivenOveruses = 0;
2962             mHistoricalNotForgivenOveruses = MISSING_VALUE;
2963             mTotalTimesKilled = 0;
2964         }
2965     }
2966 
2967     private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
2968         public final IResourceOveruseListener listener;
2969         public final @CarWatchdogManager.ResourceOveruseFlag int flag;
2970         public final int pid;
2971         public final int uid;
2972         public final boolean isListenerForSystem;
2973 
ResourceOveruseListenerInfo(IResourceOveruseListener listener, @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid, boolean isListenerForSystem)2974         ResourceOveruseListenerInfo(IResourceOveruseListener listener,
2975                 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
2976                 boolean isListenerForSystem) {
2977             this.listener = listener;
2978             this.flag = flag;
2979             this.pid = pid;
2980             this.uid = uid;
2981             this.isListenerForSystem = isListenerForSystem;
2982         }
2983 
2984         @Override
binderDied()2985         public void binderDied() {
2986             Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
2987                     isListenerForSystem ? " for system" : "", pid);
2988             Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
2989                     listenerInfosByUid -> {
2990                         ArrayList<ResourceOveruseListenerInfo> listenerInfos =
2991                                 listenerInfosByUid.get(uid);
2992                         if (listenerInfos == null) {
2993                             return;
2994                         }
2995                         listenerInfos.remove(this);
2996                         if (listenerInfos.isEmpty()) {
2997                             listenerInfosByUid.remove(uid);
2998                         }
2999                     };
3000             synchronized (mLock) {
3001                 if (isListenerForSystem) {
3002                     removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
3003                 } else {
3004                     removeListenerInfo.accept(mOveruseListenerInfosByUid);
3005                 }
3006             }
3007             unlinkToDeath();
3008         }
3009 
notifyListener(@arWatchdogManager.ResourceOveruseFlag int resourceType, int overusingUid, String overusingGenericPackageName, ResourceOveruseStats resourceOveruseStats)3010         public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
3011                 int overusingUid, String overusingGenericPackageName,
3012                 ResourceOveruseStats resourceOveruseStats) {
3013             if ((flag & resourceType) == 0) {
3014                 return;
3015             }
3016             try {
3017                 listener.onOveruse(resourceOveruseStats);
3018             } catch (RemoteException e) {
3019                 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
3020                                 + "package(uid %d, generic package name '%s'): %s",
3021                         (isListenerForSystem ? "system listener" : "listener"), uid, pid,
3022                         overusingUid, overusingGenericPackageName, e);
3023             }
3024         }
3025 
linkToDeath()3026         private void linkToDeath() throws RemoteException {
3027             listener.asBinder().linkToDeath(this, 0);
3028         }
3029 
unlinkToDeath()3030         private void unlinkToDeath() {
3031             listener.asBinder().unlinkToDeath(this, 0);
3032         }
3033     }
3034 }
3035