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