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