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