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