blob: 19523b68b3e515395754ed77f626b5b2bbd2e058 [file] [log] [blame]
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001/*
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
17package com.android.car.watchdog;
18
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070019import static android.automotive.watchdog.internal.ResourceOveruseActionType.KILLED;
20import static android.automotive.watchdog.internal.ResourceOveruseActionType.KILLED_RECURRING_OVERUSE;
21import static android.automotive.watchdog.internal.ResourceOveruseActionType.NOT_KILLED;
22import static android.automotive.watchdog.internal.ResourceOveruseActionType.NOT_KILLED_USER_OPTED;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070023import static android.car.watchdog.CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070024import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NEVER;
25import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NO;
26import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_YES;
27import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
28import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
29import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
30
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070031import static com.android.car.watchdog.CarWatchdogService.DEBUG;
32import static com.android.car.watchdog.CarWatchdogService.TAG;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070033import static com.android.car.watchdog.PackageInfoHandler.SHARED_PACKAGE_PREFIX;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070034
35import android.annotation.NonNull;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070036import android.annotation.UserIdInt;
37import android.app.ActivityThread;
38import android.automotive.watchdog.ResourceType;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070039import android.automotive.watchdog.internal.ApplicationCategoryType;
40import android.automotive.watchdog.internal.ComponentType;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070041import android.automotive.watchdog.internal.PackageIdentifier;
42import android.automotive.watchdog.internal.PackageIoOveruseStats;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070043import android.automotive.watchdog.internal.PackageMetadata;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070044import android.automotive.watchdog.internal.PackageResourceOveruseAction;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070045import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
46import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070047import android.car.watchdog.CarWatchdogManager;
48import android.car.watchdog.IResourceOveruseListener;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070049import android.car.watchdog.IoOveruseAlertThreshold;
50import android.car.watchdog.IoOveruseConfiguration;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070051import android.car.watchdog.IoOveruseStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070052import android.car.watchdog.PackageKillableState;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070053import android.car.watchdog.PackageKillableState.KillableState;
54import android.car.watchdog.PerStateBytes;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070055import android.car.watchdog.ResourceOveruseConfiguration;
56import android.car.watchdog.ResourceOveruseStats;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070057import android.car.watchdoglib.CarWatchdogDaemonHelper;
58import android.content.Context;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070059import android.content.pm.ApplicationInfo;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070060import android.content.pm.IPackageManager;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070061import android.content.pm.PackageInfo;
62import android.content.pm.PackageManager;
63import android.content.pm.UserInfo;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070064import android.os.Binder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070065import android.os.Handler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070066import android.os.IBinder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070067import android.os.Looper;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070068import android.os.RemoteException;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070069import android.os.SystemClock;
70import android.os.TransactionTooLargeException;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070071import android.os.UserHandle;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070072import android.os.UserManager;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070073import android.util.ArrayMap;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070074import android.util.ArraySet;
75import android.util.IndentingPrintWriter;
76import android.util.SparseArray;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +000077import android.util.SparseBooleanArray;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070078
Lakshman Annadorai016127e2021-03-18 09:11:43 -070079import com.android.internal.annotations.GuardedBy;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070080import com.android.internal.annotations.VisibleForTesting;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070081import com.android.internal.util.Preconditions;
82import com.android.server.utils.Slogf;
83
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070084import java.time.ZoneOffset;
85import java.time.ZonedDateTime;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070086import java.util.ArrayList;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070087import java.util.Collections;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070088import java.util.List;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070089import java.util.Map;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070090import java.util.Objects;
91import java.util.Set;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070092import java.util.function.BiFunction;
Lakshman Annadorai16999d22021-05-25 08:33:30 -070093import java.util.function.Consumer;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070094
95/**
96 * Handles system resource performance monitoring module.
97 */
98public final class WatchdogPerfHandler {
Lakshman Annadoraie1720472021-04-13 15:22:57 -070099 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
100 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
101 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
102
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000103 static final long RESOURCE_OVERUSE_KILLING_DELAY_MILLS = 10_000;
104
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700105 private static final long MAX_WAIT_TIME_MILLS = 3_000;
106
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700107 private final Context mContext;
108 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
109 private final PackageInfoHandler mPackageInfoHandler;
110 private final Handler mMainHandler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700111 private final Object mLock = new Object();
112 /*
113 * Cache of added resource overuse listeners by uid.
114 */
115 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700116 private final ArrayMap<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700117 @GuardedBy("mLock")
118 private final List<PackageResourceOveruseAction> mOveruseActionsByUserPackage =
119 new ArrayList<>();
120 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700121 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid =
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700122 new SparseArray<>();
123 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700124 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>>
125 mOveruseSystemListenerInfosByUid = new SparseArray<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700126 /* Set of safe-to-kill system and vendor packages. */
127 @GuardedBy("mLock")
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000128 public final ArraySet<String> mSafeToKillSystemPackages = new ArraySet<>();
129 @GuardedBy("mLock")
130 public final ArraySet<String> mSafeToKillVendorPackages = new ArraySet<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700131 /* Default killable state for packages when not updated by the user. */
132 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700133 public final ArraySet<String> mDefaultNotKillableGenericPackages = new ArraySet<>();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700134 @GuardedBy("mLock")
135 private ZonedDateTime mLastStatsReportUTC;
136 @GuardedBy("mLock")
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700137 private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
138 mPendingSetResourceOveruseConfigurationsRequest = null;
139 @GuardedBy("mLock")
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700140 boolean mIsConnectedToDaemon;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000141 @GuardedBy("mLock")
142 long mResourceOveruseKillingDelayMills;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700143
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700144 public WatchdogPerfHandler(Context context, CarWatchdogDaemonHelper daemonHelper,
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700145 PackageInfoHandler packageInfoHandler) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700146 mContext = context;
147 mCarWatchdogDaemonHelper = daemonHelper;
148 mPackageInfoHandler = packageInfoHandler;
149 mMainHandler = new Handler(Looper.getMainLooper());
150 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000151 mResourceOveruseKillingDelayMills = RESOURCE_OVERUSE_KILLING_DELAY_MILLS;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700152 }
153
154 /** Initializes the handler. */
155 public void init() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700156 /*
157 * TODO(b/183947162): Opt-in to receive package change broadcast and handle package enabled
158 * state changes.
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700159 * TODO(b/192294393): Persist in-memory data: Read the current day's I/O overuse stats from
160 * database.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700161 */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700162 synchronized (mLock) {
163 checkAndHandleDateChangeLocked();
164 }
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700165 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700166 if (DEBUG) {
167 Slogf.d(TAG, "WatchdogPerfHandler is initialized");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700168 }
169 }
170
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700171 /** Releases the handler */
172 public void release() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700173 /* TODO(b/192294393): Write daily usage to SQLite DB storage. */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700174 if (DEBUG) {
175 Slogf.d(TAG, "WatchdogPerfHandler is released");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700176 }
177 }
178
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700179 /** Dumps its state. */
180 public void dump(IndentingPrintWriter writer) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700181 /*
182 * TODO(b/183436216): Implement this method.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700183 */
184 }
185
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700186 /** Retries any pending requests on re-connecting to the daemon */
187 public void onDaemonConnectionChange(boolean isConnected) {
188 synchronized (mLock) {
189 mIsConnectedToDaemon = isConnected;
190 }
191 if (isConnected) {
192 /*
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700193 * Retry pending set resource overuse configuration request before processing any new
194 * set/get requests. Thus notify the waiting requests only after the retry completes.
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700195 */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700196 retryPendingSetResourceOveruseConfigurations();
197 }
198 synchronized (mLock) {
199 mLock.notifyAll();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700200 }
201 }
202
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700203 /** Returns resource overuse stats for the calling package. */
204 @NonNull
205 public ResourceOveruseStats getResourceOveruseStats(
206 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
207 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
208 Preconditions.checkArgument((resourceOveruseFlag > 0),
209 "Must provide valid resource overuse flag");
210 Preconditions.checkArgument((maxStatsPeriod > 0),
211 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700212 // When more resource stats are added, make this as optional.
213 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
214 "Must provide resource I/O overuse flag");
215 int callingUid = Binder.getCallingUid();
216 int callingUserId = UserHandle.getUserId(callingUid);
217 UserHandle callingUserHandle = UserHandle.of(callingUserId);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700218 String genericPackageName =
219 mPackageInfoHandler.getNamesForUids(new int[]{callingUid})
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700220 .get(callingUid, null);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700221 if (genericPackageName == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700222 Slogf.w(TAG, "Failed to fetch package info for uid %d", callingUid);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700223 return new ResourceOveruseStats.Builder("", callingUserHandle).build();
224 }
225 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700226 new ResourceOveruseStats.Builder(genericPackageName, callingUserHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700227 statsBuilder.setIoOveruseStats(
228 getIoOveruseStatsForPeriod(callingUserId, genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700229 if (DEBUG) {
230 Slogf.d(TAG, "Returning all resource overuse stats for calling uid %d [user %d and "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700231 + "package '%s']", callingUid, callingUserId, genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700232 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700233 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700234 }
235
236 /** Returns resource overuse stats for all packages. */
237 @NonNull
238 public List<ResourceOveruseStats> getAllResourceOveruseStats(
239 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
240 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
241 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
242 Preconditions.checkArgument((resourceOveruseFlag > 0),
243 "Must provide valid resource overuse flag");
244 Preconditions.checkArgument((maxStatsPeriod > 0),
245 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700246 // When more resource types are added, make this as optional.
247 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
248 "Must provide resource I/O overuse flag");
249 long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag);
250 List<ResourceOveruseStats> allStats = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700251 synchronized (mLock) {
252 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
253 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
254 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
255 IoOveruseStats ioOveruseStats =
256 getIoOveruseStatsLocked(usage, minimumBytesWritten, maxStatsPeriod);
257 if (ioOveruseStats == null) {
258 continue;
259 }
260 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700261 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700262 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700263 if (DEBUG) {
264 Slogf.d(TAG, "Returning all resource overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700265 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700266 return allStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700267 }
268
269 /** Returns resource overuse stats for the specified user package. */
270 @NonNull
271 public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
272 @NonNull String packageName, @NonNull UserHandle userHandle,
273 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
274 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
275 Objects.requireNonNull(packageName, "Package name must be non-null");
276 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700277 Preconditions.checkArgument(!userHandle.equals(UserHandle.ALL),
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700278 "Must provide the user handle for a specific user");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700279 Preconditions.checkArgument((resourceOveruseFlag > 0),
280 "Must provide valid resource overuse flag");
281 Preconditions.checkArgument((maxStatsPeriod > 0),
282 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700283 // When more resource types are added, make this as optional.
284 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
285 "Must provide resource I/O overuse flag");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700286 String genericPackageName =
287 mPackageInfoHandler.getNameForUserPackage(packageName, userHandle.getIdentifier());
288 if (genericPackageName == null) {
289 throw new IllegalArgumentException("Package '" + packageName + "' not found");
290 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700291 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700292 new ResourceOveruseStats.Builder(genericPackageName, userHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700293 statsBuilder.setIoOveruseStats(getIoOveruseStatsForPeriod(userHandle.getIdentifier(),
294 genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700295 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700296 Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s', "
297 + "generic package '%s'", userHandle.getIdentifier(), packageName,
298 genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700299 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700300 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700301 }
302
303 /** Adds the resource overuse listener. */
304 public void addResourceOveruseListener(
305 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
306 @NonNull IResourceOveruseListener listener) {
307 Objects.requireNonNull(listener, "Listener must be non-null");
308 Preconditions.checkArgument((resourceOveruseFlag > 0),
309 "Must provide valid resource overuse flag");
310 synchronized (mLock) {
311 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
312 mOveruseListenerInfosByUid);
313 }
314 }
315
316 /** Removes the previously added resource overuse listener. */
317 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
318 Objects.requireNonNull(listener, "Listener must be non-null");
319 synchronized (mLock) {
320 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
321 }
322 }
323
324 /** Adds the resource overuse system listener. */
325 public void addResourceOveruseListenerForSystem(
326 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
327 @NonNull IResourceOveruseListener listener) {
328 Objects.requireNonNull(listener, "Listener must be non-null");
329 Preconditions.checkArgument((resourceOveruseFlag > 0),
330 "Must provide valid resource overuse flag");
331 synchronized (mLock) {
332 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
333 mOveruseSystemListenerInfosByUid);
334 }
335 }
336
337 /** Removes the previously added resource overuse system listener. */
338 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
339 Objects.requireNonNull(listener, "Listener must be non-null");
340 synchronized (mLock) {
341 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
342 }
343 }
344
345 /** Sets whether or not a package is killable on resource overuse. */
346 public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
347 boolean isKillable) {
348 Objects.requireNonNull(packageName, "Package name must be non-null");
349 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700350
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700351 if (userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700352 setPackageKillableStateForAllUsers(packageName, isKillable);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700353 return;
354 }
355 int userId = userHandle.getIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700356 String genericPackageName = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
357 if (genericPackageName == null) {
358 throw new IllegalArgumentException("Package '" + packageName + "' not found");
359 }
360 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700361 synchronized (mLock) {
362 /*
363 * When the queried package is not cached in {@link mUsageByUserPackage}, the set API
364 * will update the killable state even when the package should never be killed.
365 * But the get API will return the correct killable state. This behavior is tolerable
366 * because in production the set API should be called only after the get API.
367 * For instance, when this case happens by mistake and the package overuses resource
368 * between the set and the get API calls, the daemon will provide correct killable
369 * state when pushing the latest stats. Ergo, the invalid killable state doesn't have
370 * any effect.
371 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700372 PackageResourceUsage usage = mUsageByUserPackage.get(key);
373 if (usage == null) {
374 usage = new PackageResourceUsage(userId, genericPackageName);
375 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700376 if (!usage.setKillableStateLocked(isKillable)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700377 Slogf.e(TAG, "User %d cannot set killable state for package '%s'",
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700378 userHandle.getIdentifier(), genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700379 throw new IllegalArgumentException("Package killable state is not updatable");
380 }
381 mUsageByUserPackage.put(key, usage);
382 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700383 if (DEBUG) {
384 Slogf.d(TAG, "Successfully set killable package state for user %d", userId);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700385 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700386 }
387
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700388 private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
389 UserManager userManager = UserManager.get(mContext);
390 List<UserInfo> userInfos = userManager.getAliveUsers();
391 String genericPackageName = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700392 synchronized (mLock) {
393 for (int i = 0; i < userInfos.size(); ++i) {
394 int userId = userInfos.get(i).id;
395 String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
396 if (name == null) {
397 continue;
398 }
399 genericPackageName = name;
400 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700401 PackageResourceUsage usage = mUsageByUserPackage.get(key);
402 if (usage == null) {
403 continue;
404 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700405 if (!usage.setKillableStateLocked(isKillable)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700406 Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName);
407 throw new IllegalArgumentException(
408 "Package killable state is not updatable");
409 }
410 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700411 if (genericPackageName != null) {
412 if (!isKillable) {
413 mDefaultNotKillableGenericPackages.add(genericPackageName);
414 } else {
415 mDefaultNotKillableGenericPackages.remove(genericPackageName);
416 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700417 }
418 }
419 if (DEBUG) {
420 Slogf.d(TAG, "Successfully set killable package state for all users");
421 }
422 }
423
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700424 /** Returns the list of package killable states on resource overuse for the user. */
425 @NonNull
426 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
427 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700428 PackageManager pm = mContext.getPackageManager();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700429 if (!userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700430 if (DEBUG) {
431 Slogf.d(TAG, "Returning all package killable states for user %d",
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700432 userHandle.getIdentifier());
433 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700434 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
435 }
436 List<PackageKillableState> packageKillableStates = new ArrayList<>();
437 UserManager userManager = UserManager.get(mContext);
438 List<UserInfo> userInfos = userManager.getAliveUsers();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700439 for (int i = 0; i < userInfos.size(); ++i) {
440 packageKillableStates.addAll(
441 getPackageKillableStatesForUserId(userInfos.get(i).id, pm));
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700442 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700443 if (DEBUG) {
444 Slogf.d(TAG, "Returning all package killable states for all users");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700445 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700446 return packageKillableStates;
447 }
448
449 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
450 PackageManager pm) {
451 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */0, userId);
452 List<PackageKillableState> states = new ArrayList<>();
453 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700454 ArrayMap<String, List<ApplicationInfo>> applicationInfosBySharedPackage =
455 new ArrayMap<>();
456 for (int i = 0; i < packageInfos.size(); ++i) {
457 PackageInfo packageInfo = packageInfos.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700458 String genericPackageName = mPackageInfoHandler.getNameForPackage(packageInfo);
459 if (packageInfo.sharedUserId == null) {
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000460 int componentType = mPackageInfoHandler.getComponentType(
461 packageInfo.applicationInfo);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700462 int killableState = getPackageKillableStateForUserPackageLocked(
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000463 userId, genericPackageName, componentType,
464 isSafeToKillLocked(genericPackageName, componentType, null));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700465 states.add(new PackageKillableState(packageInfo.packageName, userId,
466 killableState));
467 continue;
468 }
469 List<ApplicationInfo> applicationInfos =
470 applicationInfosBySharedPackage.get(genericPackageName);
471 if (applicationInfos == null) {
472 applicationInfos = new ArrayList<>();
473 }
474 applicationInfos.add(packageInfo.applicationInfo);
475 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos);
476 }
477 for (Map.Entry<String, List<ApplicationInfo>> entry :
478 applicationInfosBySharedPackage.entrySet()) {
479 String genericPackageName = entry.getKey();
480 List<ApplicationInfo> applicationInfos = entry.getValue();
481 int componentType = mPackageInfoHandler.getSharedComponentType(
482 applicationInfos, genericPackageName);
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000483 List<String> packageNames = new ArrayList<>(applicationInfos.size());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700484 for (int i = 0; i < applicationInfos.size(); ++i) {
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000485 packageNames.add(applicationInfos.get(i).packageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700486 }
487 int killableState = getPackageKillableStateForUserPackageLocked(
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000488 userId, genericPackageName, componentType,
489 isSafeToKillLocked(genericPackageName, componentType, packageNames));
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700490 for (int i = 0; i < applicationInfos.size(); ++i) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700491 states.add(new PackageKillableState(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700492 applicationInfos.get(i).packageName, userId, killableState));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700493 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700494 }
495 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700496 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700497 Slogf.d(TAG, "Returning the package killable states for user packages");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700498 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700499 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700500 }
501
502 /** Sets the given resource overuse configurations. */
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700503 @CarWatchdogManager.ReturnCode
504 public int setResourceOveruseConfigurations(
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700505 List<ResourceOveruseConfiguration> configurations,
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700506 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
507 throws RemoteException {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700508 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700509 Preconditions.checkArgument((configurations.size() > 0),
510 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700511 Preconditions.checkArgument((resourceOveruseFlag > 0),
512 "Must provide valid resource overuse flag");
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000513 checkResourceOveruseConfigs(configurations, resourceOveruseFlag);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700514 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
515 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700516 for (int i = 0; i < configurations.size(); ++i) {
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000517 internalConfigs.add(toInternalResourceOveruseConfiguration(configurations.get(i),
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700518 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700519 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700520 synchronized (mLock) {
521 if (!mIsConnectedToDaemon) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700522 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700523 return CarWatchdogManager.RETURN_CODE_SUCCESS;
524 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700525 /* Verify no pending request in progress. */
526 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700527 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700528 return setResourceOveruseConfigurationsInternal(internalConfigs,
529 /* isPendingRequest= */ false);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700530 }
531
532 /** Returns the available resource overuse configurations. */
533 @NonNull
534 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
535 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
536 Preconditions.checkArgument((resourceOveruseFlag > 0),
537 "Must provide valid resource overuse flag");
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700538 if (!isConnectedToDaemon()) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700539 throw new IllegalStateException("Car watchdog daemon is not connected");
540 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700541 synchronized (mLock) {
542 /* Verify no pending request in progress. */
543 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
544 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700545 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
546 new ArrayList<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700547 try {
548 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
549 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700550 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700551 throw new IllegalStateException(e);
552 }
553 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700554 for (int i = 0; i < internalConfigs.size(); ++i) {
555 configs.add(
556 toResourceOveruseConfiguration(internalConfigs.get(i), resourceOveruseFlag));
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700557 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700558 if (DEBUG) {
559 Slogf.d(TAG, "Returning the resource overuse configuration");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700560 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700561 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700562 }
563
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700564 /** Processes the latest I/O overuse stats */
565 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000566 SparseBooleanArray recurringIoOverusesByUid = new SparseBooleanArray();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700567 int[] uids = new int[packageIoOveruseStats.size()];
568 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
569 uids[i] = packageIoOveruseStats.get(i).uid;
570 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700571 SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700572 synchronized (mLock) {
573 checkAndHandleDateChangeLocked();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700574 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
575 PackageIoOveruseStats stats = packageIoOveruseStats.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700576 String genericPackageName = genericPackageNamesByUid.get(stats.uid);
577 if (genericPackageName == null) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700578 continue;
579 }
580 int userId = UserHandle.getUserId(stats.uid);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700581 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700582 stats.ioOveruseStats);
583 if (stats.shouldNotify) {
584 /*
585 * Packages that exceed the warn threshold percentage should be notified as well
586 * and only the daemon is aware of such packages. Thus the flag is used to
587 * indicate which packages should be notified.
588 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700589 ResourceOveruseStats resourceOveruseStats =
590 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700591 usage.getIoOveruseStatsLocked()).build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700592 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700593 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700594
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700595 if (!usage.ioUsage.exceedsThreshold()) {
596 continue;
597 }
598 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
599 overuseAction.packageIdentifier = new PackageIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700600 overuseAction.packageIdentifier.name = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700601 overuseAction.packageIdentifier.uid = stats.uid;
602 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
603 overuseAction.resourceOveruseActionType = NOT_KILLED;
604 /*
605 * No action required on I/O overuse on one of the following cases:
606 * #1 The package is not safe to kill as it is critical for system stability.
607 * #2 The package has no recurring overuse behavior and the user opted to not
608 * kill the package so honor the user's decision.
609 */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700610 int killableState = usage.getKillableStateLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700611 if (killableState == KILLABLE_STATE_NEVER) {
612 mOveruseActionsByUserPackage.add(overuseAction);
613 continue;
614 }
615 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
616 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
617 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
618 mOveruseActionsByUserPackage.add(overuseAction);
619 continue;
620 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000621
622 recurringIoOverusesByUid.put(stats.uid, hasRecurringOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700623 }
624 if (!mOveruseActionsByUserPackage.isEmpty()) {
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700625 mMainHandler.post(this::notifyActionsTakenOnOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700626 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000627 if (recurringIoOverusesByUid.size() > 0) {
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700628 mMainHandler.postDelayed(
629 () -> handleIoOveruseKilling(
630 recurringIoOverusesByUid, genericPackageNamesByUid),
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000631 mResourceOveruseKillingDelayMills);
632 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700633 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700634 if (DEBUG) {
635 Slogf.d(TAG, "Processed latest I/O overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700636 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700637 }
638
639 /** Notify daemon about the actions take on resource overuse */
640 public void notifyActionsTakenOnOveruse() {
641 List<PackageResourceOveruseAction> actions;
642 synchronized (mLock) {
643 if (mOveruseActionsByUserPackage.isEmpty()) {
644 return;
645 }
646 actions = new ArrayList<>(mOveruseActionsByUserPackage);
647 mOveruseActionsByUserPackage.clear();
648 }
649 try {
650 mCarWatchdogDaemonHelper.actionTakenOnResourceOveruse(actions);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700651 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700652 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of actions taken on resource "
653 + "overuse");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700654 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700655 if (DEBUG) {
656 Slogf.d(TAG, "Notified car watchdog daemon of actions taken on resource overuse");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700657 }
658 }
659
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000660 /** Handle packages that exceed resource overuse thresholds */
661 private void handleIoOveruseKilling(SparseBooleanArray recurringIoOverusesByUid,
662 SparseArray<String> genericPackageNamesByUid) {
663 IPackageManager packageManager = ActivityThread.getPackageManager();
664 synchronized (mLock) {
665 for (int i = 0; i < recurringIoOverusesByUid.size(); i++) {
666 int uid = recurringIoOverusesByUid.keyAt(i);
667 boolean hasRecurringOveruse = recurringIoOverusesByUid.valueAt(i);
668 String genericPackageName = genericPackageNamesByUid.get(uid);
669 int userId = UserHandle.getUserId(uid);
670 String key = getUserPackageUniqueId(userId, genericPackageName);
671 PackageResourceUsage usage = mUsageByUserPackage.get(key);
672
673 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
674 overuseAction.packageIdentifier = new PackageIdentifier();
675 overuseAction.packageIdentifier.name = genericPackageName;
676 overuseAction.packageIdentifier.uid = uid;
677 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
678 overuseAction.resourceOveruseActionType = NOT_KILLED;
679
680 List<String> packages = Collections.singletonList(genericPackageName);
681 if (usage.isSharedPackage()) {
682 packages = mPackageInfoHandler.getPackagesForUid(uid, genericPackageName);
683 }
684 for (int pkgIdx = 0; pkgIdx < packages.size(); pkgIdx++) {
685 String packageName = packages.get(pkgIdx);
686 try {
687 int oldEnabledState = -1;
688 if (!hasRecurringOveruse) {
689 oldEnabledState = packageManager.getApplicationEnabledSetting(
690 packageName, userId);
691 if (oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED
692 || oldEnabledState == COMPONENT_ENABLED_STATE_DISABLED_USER
693 || oldEnabledState
694 == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
695 continue;
696 }
697 }
698 packageManager.setApplicationEnabledSetting(packageName,
699 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
700 mContext.getPackageName());
701 overuseAction.resourceOveruseActionType = hasRecurringOveruse
702 ? KILLED_RECURRING_OVERUSE : KILLED;
703 if (oldEnabledState != -1) {
704 usage.oldEnabledStateByPackage.put(packageName, oldEnabledState);
705 }
706 } catch (RemoteException e) {
707 Slogf.e(TAG, "Failed to disable application for user %d, package '%s'",
708 userId, packageName);
709 }
710 }
711 mOveruseActionsByUserPackage.add(overuseAction);
712 }
713 if (!mOveruseActionsByUserPackage.isEmpty()) {
714 notifyActionsTakenOnOveruse();
715 }
716 }
717 }
718
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700719 /** Resets the resource overuse stats for the given generic package names. */
720 public void resetResourceOveruseStats(Set<String> genericPackageNames) {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700721 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700722 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
723 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700724 if (genericPackageNames.contains(usage.genericPackageName)) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700725 usage.resetStatsLocked();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700726 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700727 * TODO(b/192294393): When the stats are persisted in local DB, reset the stats
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700728 * for this package from local DB.
729 */
730 }
731 }
732 }
733 }
734
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000735 /** Set the delay to kill a package after the package is notified of resource overuse. */
736 public void setResourceOveruseKillingDelay(long millis) {
737 synchronized (mLock) {
738 mResourceOveruseKillingDelayMills = millis;
739 }
740 }
741
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000742 /** Fetches and syncs the resource overuse configurations from watchdog daemon. */
743 private void fetchAndSyncResourceOveruseConfigurations() {
744 synchronized (mLock) {
745 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs;
746 try {
747 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
748 } catch (RemoteException | RuntimeException e) {
749 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
750 return;
751 }
752 if (internalConfigs.isEmpty()) {
753 Slogf.e(TAG, "Failed to fetch resource overuse configurations");
754 return;
755 }
756 mSafeToKillSystemPackages.clear();
757 mSafeToKillVendorPackages.clear();
758 for (int i = 0; i < internalConfigs.size(); i++) {
759 switch (internalConfigs.get(i).componentType) {
760 case ComponentType.SYSTEM:
761 mSafeToKillSystemPackages.addAll(internalConfigs.get(i).safeToKillPackages);
762 break;
763 case ComponentType.VENDOR:
764 mSafeToKillVendorPackages.addAll(internalConfigs.get(i).safeToKillPackages);
765 break;
766 default:
767 // All third-party apps are killable.
768 break;
769 }
770 }
771 if (DEBUG) {
772 Slogf.d(TAG, "Fetched and synced safe to kill packages.");
773 }
774 }
775 }
776
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700777 @GuardedBy("mLock")
778 private int getPackageKillableStateForUserPackageLocked(
779 int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
780 String key = getUserPackageUniqueId(userId, genericPackageName);
781 PackageResourceUsage usage = mUsageByUserPackage.get(key);
782 if (usage == null) {
783 usage = new PackageResourceUsage(userId, genericPackageName);
784 }
785 int killableState = usage.syncAndFetchKillableStateLocked(componentType, isSafeToKill);
786 mUsageByUserPackage.put(key, usage);
787 return killableState;
788 }
789
790 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700791 private void notifyResourceOveruseStatsLocked(int uid,
792 ResourceOveruseStats resourceOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700793 String genericPackageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700794 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
795 if (listenerInfos != null) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700796 for (int i = 0; i < listenerInfos.size(); ++i) {
797 listenerInfos.get(i).notifyListener(
798 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700799 }
800 }
801 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700802 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700803 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700804 for (int j = 0; j < systemListenerInfos.size(); ++j) {
805 systemListenerInfos.get(j).notifyListener(
806 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700807 }
808 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700809 if (DEBUG) {
810 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700811 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700812 }
813
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700814 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700815 private void checkAndHandleDateChangeLocked() {
816 ZonedDateTime previousUTC = mLastStatsReportUTC;
817 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
818 if (mLastStatsReportUTC.getDayOfYear() == previousUTC.getDayOfYear()
819 && mLastStatsReportUTC.getYear() == previousUTC.getYear()) {
820 return;
821 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700822 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
823 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700824 // Forgive the daily disabled package on date change.
825 for (Map.Entry<String, Integer> entry : usage.oldEnabledStateByPackage.entrySet()) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700826 try {
827 IPackageManager packageManager = ActivityThread.getPackageManager();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700828 if (packageManager.getApplicationEnabledSetting(entry.getKey(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700829 usage.userId)
830 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
831 continue;
832 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700833 packageManager.setApplicationEnabledSetting(entry.getKey(),
834 entry.getValue(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700835 /* flags= */ 0, usage.userId, mContext.getPackageName());
836 } catch (RemoteException e) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700837 Slogf.e(TAG,
838 "Failed to reset enabled setting for disabled package '%s', user '%d'",
839 usage.genericPackageName, usage.userId);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700840 }
841 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700842 /* TODO(b/192294393): Stash the old usage into SQLite DB storage. */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700843 usage.resetStatsLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700844 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700845 if (DEBUG) {
846 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700847 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700848 }
849
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700850 @GuardedBy("mLock")
851 private PackageResourceUsage cacheAndFetchUsageLocked(
852 @UserIdInt int userId, String genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700853 android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700854 String key = getUserPackageUniqueId(userId, genericPackageName);
855 PackageResourceUsage usage = mUsageByUserPackage.get(key);
856 if (usage == null) {
857 usage = new PackageResourceUsage(userId, genericPackageName);
858 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700859 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700860 mUsageByUserPackage.put(key, usage);
861 return usage;
862 }
863
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700864 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700865 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
866 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700867 * TODO(b/192294393): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700868 * has recurring I/O overuse behavior.
869 */
870 return false;
871 }
872
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700873 private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName,
874 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
875 synchronized (mLock) {
876 String key = getUserPackageUniqueId(userId, genericPackageName);
877 PackageResourceUsage usage = mUsageByUserPackage.get(key);
878 if (usage == null) {
879 return null;
880 }
881 return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700882 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700883 }
884
885 @GuardedBy("mLock")
886 private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage,
887 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
888 IoOveruseStats stats = usage.getIoOveruseStatsLocked();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700889 long totalBytesWritten = stats != null ? stats.getTotalBytesWritten() : 0;
890 /*
891 * TODO(b/185431129): When maxStatsPeriod > current day, populate the historical stats
892 * from the local database. Also handle the case where the package doesn't have current
893 * day stats but has historical stats.
894 */
895 if (totalBytesWritten < minimumBytesWritten) {
896 return null;
897 }
898 return stats;
899 }
900
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700901 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700902 private void addResourceOveruseListenerLocked(
903 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
904 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700905 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700906 int callingPid = Binder.getCallingPid();
907 int callingUid = Binder.getCallingUid();
908 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
909 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
910 "resource overuse listener";
911
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700912 IBinder binder = listener.asBinder();
913 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
914 if (listenerInfos == null) {
915 listenerInfos = new ArrayList<>();
916 listenerInfosByUid.put(callingUid, listenerInfos);
917 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700918 for (int i = 0; i < listenerInfos.size(); ++i) {
919 if (listenerInfos.get(i).listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700920 throw new IllegalStateException(
921 "Cannot add " + listenerType + " as it is already added");
922 }
923 }
924
925 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
926 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
927 try {
928 listenerInfo.linkToDeath();
929 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700930 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700931 return;
932 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700933 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700934 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700935 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
936 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700937 }
938 }
939
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700940 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700941 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700942 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700943 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700944 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
945 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700946 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
947 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700948 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700949 return;
950 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700951 IBinder binder = listener.asBinder();
952 ResourceOveruseListenerInfo cachedListenerInfo = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700953 for (int i = 0; i < listenerInfos.size(); ++i) {
954 if (listenerInfos.get(i).listener.asBinder() == binder) {
955 cachedListenerInfo = listenerInfos.get(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700956 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700957 }
958 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700959 if (cachedListenerInfo == null) {
960 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
961 return;
962 }
963 cachedListenerInfo.unlinkToDeath();
964 listenerInfos.remove(cachedListenerInfo);
965 if (listenerInfos.isEmpty()) {
966 listenerInfosByUid.remove(callingUid);
967 }
968 if (DEBUG) {
969 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
970 cachedListenerInfo.pid, cachedListenerInfo.uid);
971 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700972 }
973
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700974 @GuardedBy("mLock")
975 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
976 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
977 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
978 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
979 return;
980 }
981 throw new IllegalStateException(
982 "Pending setResourceOveruseConfigurations request in progress");
983 }
984 mPendingSetResourceOveruseConfigurationsRequest = configs;
985 }
986
987 private void retryPendingSetResourceOveruseConfigurations() {
988 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700989 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700990 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
991 return;
992 }
993 configs = mPendingSetResourceOveruseConfigurationsRequest;
994 }
995 try {
996 int result = setResourceOveruseConfigurationsInternal(configs,
997 /* isPendingRequest= */ true);
998 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
999 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
1000 + "%d", result);
1001 }
1002 } catch (Exception e) {
1003 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
1004 }
1005 }
1006
1007 private int setResourceOveruseConfigurationsInternal(
1008 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
1009 boolean isPendingRequest) throws RemoteException {
1010 boolean doClearPendingRequest = isPendingRequest;
1011 try {
1012 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
Lakshman Annadoraia7189562021-08-17 16:36:16 -07001013 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001014 } catch (RemoteException e) {
1015 if (e instanceof TransactionTooLargeException) {
1016 throw e;
1017 }
1018 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
1019 synchronized (mLock) {
1020 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
1021 }
1022 doClearPendingRequest = false;
1023 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1024 } finally {
1025 if (doClearPendingRequest) {
1026 synchronized (mLock) {
1027 mPendingSetResourceOveruseConfigurationsRequest = null;
1028 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001029 }
1030 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001031 if (DEBUG) {
1032 Slogf.d(TAG, "Set the resource overuse configuration successfully");
1033 }
1034 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1035 }
1036
1037 private boolean isConnectedToDaemon() {
1038 synchronized (mLock) {
1039 long startTimeMillis = SystemClock.uptimeMillis();
1040 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1041 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
1042 try {
1043 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
1044 } catch (InterruptedException e) {
1045 Thread.currentThread().interrupt();
1046 continue;
1047 } finally {
1048 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1049 }
1050 break;
1051 }
1052 return mIsConnectedToDaemon;
1053 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001054 }
1055
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +00001056 @GuardedBy("mLock")
1057 private boolean isSafeToKillLocked(String genericPackageName, int componentType,
1058 List<String> sharedPackages) {
1059 BiFunction<List<String>, Set<String>, Boolean> isSafeToKillAnyPackage =
1060 (packages, safeToKillPackages) -> {
1061 if (packages == null) {
1062 return false;
1063 }
1064 for (int i = 0; i < packages.size(); i++) {
1065 if (safeToKillPackages.contains(packages.get(i))) {
1066 return true;
1067 }
1068 }
1069 return false;
1070 };
1071
1072 switch (componentType) {
1073 case ComponentType.SYSTEM:
1074 if (mSafeToKillSystemPackages.contains(genericPackageName)) {
1075 return true;
1076 }
1077 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages);
1078 case ComponentType.VENDOR:
1079 if (mSafeToKillVendorPackages.contains(genericPackageName)) {
1080 return true;
1081 }
1082 /*
1083 * Packages under the vendor shared UID may contain system packages because when
1084 * CarWatchdogService derives the shared component type it attributes system
1085 * packages as vendor packages when there is at least one vendor package.
1086 */
1087 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages)
1088 || isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillVendorPackages);
1089 default:
1090 // Third-party apps are always killable
1091 return true;
1092 }
1093 }
1094
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001095 private static String getUserPackageUniqueId(int userId, String genericPackageName) {
1096 return String.valueOf(userId) + ":" + genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001097 }
1098
1099 @VisibleForTesting
1100 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
1101 android.automotive.watchdog.IoOveruseStats internalStats) {
1102 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
1103 internalStats.startTime, internalStats.durationInSeconds);
1104 statsBuilder.setRemainingWriteBytes(
1105 toPerStateBytes(internalStats.remainingWriteBytes));
1106 statsBuilder.setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes));
1107 statsBuilder.setTotalOveruses(internalStats.totalOveruses);
1108 return statsBuilder;
1109 }
1110
1111 private static PerStateBytes toPerStateBytes(
1112 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001113 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001114 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001115 }
1116
1117 private static long totalPerStateBytes(
1118 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
1119 BiFunction<Long, Long, Long> sum = (l, r) -> {
1120 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
1121 };
1122 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
1123 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
1124 }
1125
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001126 private static long getMinimumBytesWritten(
1127 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
1128 switch (minimumStatsIoFlag) {
1129 case 0:
1130 return 0;
1131 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
1132 return 1024 * 1024;
1133 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
1134 return 100 * 1024 * 1024;
1135 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
1136 return 1024 * 1024 * 1024;
1137 default:
1138 throw new IllegalArgumentException(
1139 "Must provide valid minimum stats flag for I/O resource");
1140 }
1141 }
1142
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001143 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
1144 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
1145 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1146 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
1147 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
1148 internalConfig.componentType = config.getComponentType();
1149 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
1150 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
1151 internalConfig.packageMetadata = new ArrayList<>();
1152 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
1153 if (entry.getKey().isEmpty()) {
1154 continue;
1155 }
1156 PackageMetadata metadata = new PackageMetadata();
1157 metadata.packageName = entry.getKey();
1158 switch(entry.getValue()) {
1159 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1160 metadata.appCategoryType = ApplicationCategoryType.MAPS;
1161 break;
1162 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1163 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
1164 break;
1165 default:
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001166 Slogf.i(TAG, "Invalid application category type: %s skipping package: %s",
1167 entry.getValue(), metadata.packageName);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001168 continue;
1169 }
1170 internalConfig.packageMetadata.add(metadata);
1171 }
1172 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
1173 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1174 && config.getIoOveruseConfiguration() != null) {
1175 internalConfig.resourceSpecificConfigurations.add(
1176 toResourceSpecificConfiguration(config.getComponentType(),
1177 config.getIoOveruseConfiguration()));
1178 }
1179 return internalConfig;
1180 }
1181
1182 private static ResourceSpecificConfiguration
1183 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
1184 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
1185 new android.automotive.watchdog.internal.IoOveruseConfiguration();
1186 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
1187 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
1188 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
1189 config.getPackageSpecificThresholds());
1190 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
1191 config.getAppCategorySpecificThresholds());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001192 for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) {
1193 PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001194 switch(threshold.name) {
1195 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1196 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
1197 break;
1198 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1199 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
1200 break;
1201 default:
1202 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
1203 }
1204 }
1205 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
1206 config.getSystemWideThresholds());
1207
1208 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1209 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1210 return resourceSpecificConfig;
1211 }
1212
1213 @VisibleForTesting
1214 static String toComponentTypeStr(int componentType) {
1215 switch(componentType) {
1216 case ComponentType.SYSTEM:
1217 return "SYSTEM";
1218 case ComponentType.VENDOR:
1219 return "VENDOR";
1220 case ComponentType.THIRD_PARTY:
1221 return "THIRD_PARTY";
1222 default:
1223 return "UNKNOWN";
1224 }
1225 }
1226
1227 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1228 Map<String, PerStateBytes> thresholds) {
1229 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1230 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001231 if (!thresholds.isEmpty()) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001232 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1233 entry.getValue()));
1234 }
1235 }
1236 return internalThresholds;
1237 }
1238
1239 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1240 PerStateBytes perStateBytes) {
1241 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1242 threshold.name = name;
1243 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1244 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1245 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1246 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1247 return threshold;
1248 }
1249
1250 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1251 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1252 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1253 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001254 for (int i = 0; i < thresholds.size(); ++i) {
1255 if (thresholds.get(i).getDurationInSeconds() == 0
1256 || thresholds.get(i).getWrittenBytesPerSecond() == 0) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001257 continue;
1258 }
1259 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1260 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001261 internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds();
1262 internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond();
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001263 internalThresholds.add(internalThreshold);
1264 }
1265 return internalThresholds;
1266 }
1267
1268 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1269 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1270 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001271 ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1272 for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001273 String categoryTypeStr;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001274 switch (internalConfig.packageMetadata.get(i).appCategoryType) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001275 case ApplicationCategoryType.MAPS:
1276 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1277 break;
1278 case ApplicationCategoryType.MEDIA:
1279 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1280 break;
1281 default:
1282 continue;
1283 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001284 packagesToAppCategoryTypes.put(
1285 internalConfig.packageMetadata.get(i).packageName, categoryTypeStr);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001286 }
1287 ResourceOveruseConfiguration.Builder configBuilder =
1288 new ResourceOveruseConfiguration.Builder(
1289 internalConfig.componentType,
1290 internalConfig.safeToKillPackages,
1291 internalConfig.vendorPackagePrefixes,
1292 packagesToAppCategoryTypes);
1293 for (ResourceSpecificConfiguration resourceSpecificConfig :
1294 internalConfig.resourceSpecificConfigurations) {
1295 if (resourceSpecificConfig.getTag()
1296 == ResourceSpecificConfiguration.ioOveruseConfiguration
1297 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1298 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1299 resourceSpecificConfig.getIoOveruseConfiguration()));
1300 }
1301 }
1302 return configBuilder.build();
1303 }
1304
1305 private static IoOveruseConfiguration toIoOveruseConfiguration(
1306 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
1307 PerStateBytes componentLevelThresholds =
1308 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001309 ArrayMap<String, PerStateBytes> packageSpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001310 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001311 ArrayMap<String, PerStateBytes> appCategorySpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001312 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
1313 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
1314 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
1315 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
1316 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1317 List<IoOveruseAlertThreshold> systemWideThresholds =
1318 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1319
1320 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1321 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1322 systemWideThresholds);
1323 return configBuilder.build();
1324 }
1325
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001326 private static ArrayMap<String, PerStateBytes> toPerStateBytesMap(
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001327 List<PerStateIoOveruseThreshold> thresholds) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001328 ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1329 for (int i = 0; i < thresholds.size(); ++i) {
1330 thresholdsMap.put(
1331 thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001332 }
1333 return thresholdsMap;
1334 }
1335
1336 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1337 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1338 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001339 for (int i = 0; i < internalThresholds.size(); ++i) {
1340 thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds,
1341 internalThresholds.get(i).writtenBytesPerSecond));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001342 }
1343 return thresholds;
1344 }
1345
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001346 private static void checkResourceOveruseConfigs(
1347 List<ResourceOveruseConfiguration> configurations,
1348 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1349 ArraySet<Integer> seenComponentTypes = new ArraySet<>();
1350 for (int i = 0; i < configurations.size(); ++i) {
1351 ResourceOveruseConfiguration config = configurations.get(i);
1352 if (seenComponentTypes.contains(config.getComponentType())) {
1353 throw new IllegalArgumentException(
1354 "Cannot provide duplicate configurations for the same component type");
1355 }
1356 checkResourceOveruseConfig(config, resourceOveruseFlag);
1357 seenComponentTypes.add(config.getComponentType());
1358 }
1359 }
1360
1361 private static void checkResourceOveruseConfig(ResourceOveruseConfiguration config,
1362 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1363 int componentType = config.getComponentType();
1364 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
1365 throw new IllegalArgumentException(
1366 "Invalid component type in the configuration: " + componentType);
1367 }
1368 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1369 && config.getIoOveruseConfiguration() == null) {
1370 throw new IllegalArgumentException("Must provide I/O overuse configuration");
1371 }
1372 checkIoOveruseConfig(config.getIoOveruseConfiguration(), componentType);
1373 }
1374
1375 private static void checkIoOveruseConfig(IoOveruseConfiguration config, int componentType) {
1376 if (config.getComponentLevelThresholds().getBackgroundModeBytes() <= 0
1377 || config.getComponentLevelThresholds().getForegroundModeBytes() <= 0
1378 || config.getComponentLevelThresholds().getGarageModeBytes() <= 0) {
1379 throw new IllegalArgumentException(
1380 "For component: " + toComponentTypeStr(componentType)
1381 + " some thresholds are zero for: "
1382 + config.getComponentLevelThresholds().toString());
1383 }
1384 if (componentType == ComponentType.SYSTEM) {
1385 List<IoOveruseAlertThreshold> systemThresholds = config.getSystemWideThresholds();
1386 if (systemThresholds.isEmpty()) {
1387 throw new IllegalArgumentException(
1388 "Empty system-wide alert thresholds provided in "
1389 + toComponentTypeStr(componentType)
1390 + " config.");
1391 }
1392 for (int i = 0; i < systemThresholds.size(); i++) {
1393 checkIoOveruseAlertThreshold(systemThresholds.get(i));
1394 }
1395 }
1396 }
1397
1398 private static void checkIoOveruseAlertThreshold(
1399 IoOveruseAlertThreshold ioOveruseAlertThreshold) {
1400 if (ioOveruseAlertThreshold.getDurationInSeconds() <= 0) {
1401 throw new IllegalArgumentException(
1402 "System wide threshold duration must be greater than zero for: "
1403 + ioOveruseAlertThreshold);
1404 }
1405 if (ioOveruseAlertThreshold.getWrittenBytesPerSecond() <= 0) {
1406 throw new IllegalArgumentException(
1407 "System wide threshold written bytes per second must be greater than zero for: "
1408 + ioOveruseAlertThreshold);
1409 }
1410 }
1411
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001412 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1413 PerStateBytes perStateBytes = map.get(oldKey);
1414 if (perStateBytes != null) {
1415 map.put(newKey, perStateBytes);
1416 map.remove(oldKey);
1417 }
1418 }
1419
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001420 private final class PackageResourceUsage {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001421 public final String genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001422 public @UserIdInt final int userId;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001423 @GuardedBy("mLock")
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001424 public final PackageIoUsage ioUsage = new PackageIoUsage();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001425 @GuardedBy("mLock")
1426 public final ArrayMap<String, Integer> oldEnabledStateByPackage = new ArrayMap<>();
1427 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001428 private @KillableState int mKillableState;
1429
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001430 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001431 PackageResourceUsage(@UserIdInt int userId, String genericPackageName) {
1432 this.genericPackageName = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001433 this.userId = userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001434 this.mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001435 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001436 }
1437
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001438 public boolean isSharedPackage() {
1439 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
1440 }
1441
1442 @GuardedBy("mLock")
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001443 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001444 if (!internalStats.killableOnOveruse) {
1445 /*
1446 * Killable value specified in the internal stats is provided by the native daemon.
1447 * This value reflects whether or not an application is safe-to-kill on overuse.
1448 * This setting is from the I/O overuse configuration specified by the system and
1449 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1450 * specify the application is not killable, the application is not safe-to-kill.
1451 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001452 mKillableState = KILLABLE_STATE_NEVER;
1453 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1454 /*
1455 * This case happens when a previously unsafe to kill system/vendor package was
1456 * recently marked as safe-to-kill so update the old state to the default value.
1457 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001458 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001459 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001460 }
1461 ioUsage.update(internalStats);
1462 }
1463
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001464 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001465 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001466 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001467
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001468 @GuardedBy("mLock")
1469 public IoOveruseStats getIoOveruseStatsLocked() {
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001470 if (!ioUsage.hasUsage()) {
1471 return null;
1472 }
1473 return ioUsage.getStatsBuilder().setKillableOnOveruse(
1474 mKillableState != KILLABLE_STATE_NEVER).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001475 }
1476
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001477 @GuardedBy("mLock")
1478 public @KillableState int getKillableStateLocked() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001479 return mKillableState;
1480 }
1481
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001482 @GuardedBy("mLock")
1483 public boolean setKillableStateLocked(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001484 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001485 return false;
1486 }
1487 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1488 return true;
1489 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001490
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001491 @GuardedBy("mLock")
1492 public int syncAndFetchKillableStateLocked(int myComponentType, boolean isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001493 /*
1494 * The killable state goes out-of-sync:
1495 * 1. When the on-device safe-to-kill list is recently updated and the user package
1496 * didn't have any resource usage so the native daemon didn't update the killable state.
1497 * 2. When a package has no resource usage and is initialized outside of processing the
1498 * latest resource usage stats.
1499 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001500 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001501 mKillableState = KILLABLE_STATE_NEVER;
1502 } else if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001503 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001504 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1505 }
1506 return mKillableState;
1507 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001508
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001509 @GuardedBy("mLock")
1510 public void resetStatsLocked() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001511 oldEnabledStateByPackage.clear();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001512 ioUsage.resetStats();
1513 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001514 }
1515
1516 private static final class PackageIoUsage {
1517 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1518 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
1519 private long mTotalTimesKilled;
1520
1521 PackageIoUsage() {
1522 mTotalTimesKilled = 0;
1523 }
1524
1525 public boolean hasUsage() {
1526 return mIoOveruseStats != null;
1527 }
1528
1529 public void update(android.automotive.watchdog.IoOveruseStats internalStats) {
1530 mIoOveruseStats = internalStats;
1531 if (exceedsThreshold()) {
1532 /*
1533 * Forgive written bytes on overuse as the package is either forgiven or killed on
1534 * overuse. When the package is killed, the user may opt to open the corresponding
1535 * app and the package should be forgiven anyways.
1536 * NOTE: If this logic is updated, update the daemon side logic as well.
1537 */
1538 mForgivenWriteBytes = internalStats.writtenBytes;
1539 }
1540 }
1541
1542 public IoOveruseStats.Builder getStatsBuilder() {
1543 IoOveruseStats.Builder statsBuilder = toIoOveruseStatsBuilder(mIoOveruseStats);
1544 statsBuilder.setTotalTimesKilled(mTotalTimesKilled);
1545 return statsBuilder;
1546 }
1547
1548 public boolean exceedsThreshold() {
1549 if (!hasUsage()) {
1550 return false;
1551 }
1552 android.automotive.watchdog.PerStateBytes remaining =
1553 mIoOveruseStats.remainingWriteBytes;
1554 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1555 || remaining.garageModeBytes == 0;
1556 }
1557
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001558 public void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001559 mIoOveruseStats = null;
1560 mForgivenWriteBytes = null;
1561 mTotalTimesKilled = 0;
1562 }
1563 }
1564
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001565 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1566 public final IResourceOveruseListener listener;
1567 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1568 public final int pid;
1569 public final int uid;
1570 public final boolean isListenerForSystem;
1571
1572 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1573 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1574 boolean isListenerForSystem) {
1575 this.listener = listener;
1576 this.flag = flag;
1577 this.pid = pid;
1578 this.uid = uid;
1579 this.isListenerForSystem = isListenerForSystem;
1580 }
1581
1582 @Override
1583 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001584 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001585 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001586 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1587 listenerInfosByUid -> {
1588 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1589 listenerInfosByUid.get(uid);
1590 if (listenerInfos == null) {
1591 return;
1592 }
1593 listenerInfos.remove(this);
1594 if (listenerInfos.isEmpty()) {
1595 listenerInfosByUid.remove(uid);
1596 }
1597 };
1598 if (isListenerForSystem) {
1599 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1600 } else {
1601 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1602 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001603 unlinkToDeath();
1604 }
1605
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001606 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001607 int overusingUid, String overusingGenericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001608 ResourceOveruseStats resourceOveruseStats) {
1609 if ((flag & resourceType) == 0) {
1610 return;
1611 }
1612 try {
1613 listener.onOveruse(resourceOveruseStats);
1614 } catch (RemoteException e) {
1615 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001616 + "package(uid %d, generic package name '%s'): %s",
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001617 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001618 overusingUid, overusingGenericPackageName, e);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001619 }
1620 }
1621
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001622 private void linkToDeath() throws RemoteException {
1623 listener.asBinder().linkToDeath(this, 0);
1624 }
1625
1626 private void unlinkToDeath() {
1627 listener.asBinder().unlinkToDeath(this, 0);
1628 }
1629 }
1630}