blob: b526a89de6d8003023313b4bd2dca9560a28cbac [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 Annadoraicf5f3a92021-04-02 15:26:16 -070034import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070035
36import android.annotation.NonNull;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070037import android.annotation.UserIdInt;
38import android.app.ActivityThread;
39import android.automotive.watchdog.ResourceType;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070040import android.automotive.watchdog.internal.ApplicationCategoryType;
41import android.automotive.watchdog.internal.ComponentType;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070042import android.automotive.watchdog.internal.PackageIdentifier;
43import android.automotive.watchdog.internal.PackageIoOveruseStats;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070044import android.automotive.watchdog.internal.PackageMetadata;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070045import android.automotive.watchdog.internal.PackageResourceOveruseAction;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070046import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
47import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070048import android.car.watchdog.CarWatchdogManager;
49import android.car.watchdog.IResourceOveruseListener;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070050import android.car.watchdog.IoOveruseAlertThreshold;
51import android.car.watchdog.IoOveruseConfiguration;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070052import android.car.watchdog.IoOveruseStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070053import android.car.watchdog.PackageKillableState;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070054import android.car.watchdog.PackageKillableState.KillableState;
55import android.car.watchdog.PerStateBytes;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070056import android.car.watchdog.ResourceOveruseConfiguration;
57import android.car.watchdog.ResourceOveruseStats;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070058import android.car.watchdoglib.CarWatchdogDaemonHelper;
59import android.content.Context;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070060import android.content.pm.ApplicationInfo;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070061import android.content.pm.IPackageManager;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070062import android.content.pm.PackageInfo;
63import android.content.pm.PackageManager;
64import android.content.pm.UserInfo;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070065import android.os.Binder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070066import android.os.Handler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070067import android.os.IBinder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070068import android.os.Looper;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070069import android.os.RemoteException;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070070import android.os.SystemClock;
71import android.os.TransactionTooLargeException;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070072import android.os.UserHandle;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070073import android.os.UserManager;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070074import android.util.ArrayMap;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070075import android.util.ArraySet;
76import android.util.IndentingPrintWriter;
77import android.util.SparseArray;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +000078import android.util.SparseBooleanArray;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070079
Lakshman Annadorai016127e2021-03-18 09:11:43 -070080import com.android.internal.annotations.GuardedBy;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070081import com.android.internal.annotations.VisibleForTesting;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070082import com.android.internal.util.Preconditions;
83import com.android.server.utils.Slogf;
84
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070085import java.time.ZoneOffset;
86import java.time.ZonedDateTime;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070087import java.util.ArrayList;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070088import java.util.Collections;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070089import java.util.List;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070090import java.util.Map;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070091import java.util.Objects;
92import java.util.Set;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070093import java.util.function.BiFunction;
Lakshman Annadorai16999d22021-05-25 08:33:30 -070094import java.util.function.Consumer;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070095
96/**
97 * Handles system resource performance monitoring module.
98 */
99public final class WatchdogPerfHandler {
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700100 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
101 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
102 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
103
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000104 static final long RESOURCE_OVERUSE_KILLING_DELAY_MILLS = 10_000;
105
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700106 private static final long MAX_WAIT_TIME_MILLS = 3_000;
107
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700108 private final Context mContext;
109 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
110 private final PackageInfoHandler mPackageInfoHandler;
111 private final Handler mMainHandler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700112 private final Object mLock = new Object();
113 /*
114 * Cache of added resource overuse listeners by uid.
115 */
116 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700117 private final ArrayMap<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700118 @GuardedBy("mLock")
119 private final List<PackageResourceOveruseAction> mOveruseActionsByUserPackage =
120 new ArrayList<>();
121 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700122 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid =
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700123 new SparseArray<>();
124 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700125 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>>
126 mOveruseSystemListenerInfosByUid = new SparseArray<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700127 /* Set of safe-to-kill system and vendor packages. */
128 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700129 public final ArraySet<String> mSafeToKillPackages = new ArraySet<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700130 /* Default killable state for packages when not updated by the user. */
131 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700132 public final ArraySet<String> mDefaultNotKillableGenericPackages = new ArraySet<>();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700133 @GuardedBy("mLock")
134 private ZonedDateTime mLastStatsReportUTC;
135 @GuardedBy("mLock")
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700136 private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
137 mPendingSetResourceOveruseConfigurationsRequest = null;
138 @GuardedBy("mLock")
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700139 boolean mIsConnectedToDaemon;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000140 @GuardedBy("mLock")
141 long mResourceOveruseKillingDelayMills;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700142
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700143 public WatchdogPerfHandler(Context context, CarWatchdogDaemonHelper daemonHelper,
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700144 PackageInfoHandler packageInfoHandler) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700145 mContext = context;
146 mCarWatchdogDaemonHelper = daemonHelper;
147 mPackageInfoHandler = packageInfoHandler;
148 mMainHandler = new Handler(Looper.getMainLooper());
149 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000150 mResourceOveruseKillingDelayMills = RESOURCE_OVERUSE_KILLING_DELAY_MILLS;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700151 }
152
153 /** Initializes the handler. */
154 public void init() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700155 /*
156 * TODO(b/183947162): Opt-in to receive package change broadcast and handle package enabled
157 * state changes.
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700158 * TODO(b/192294393): Persist in-memory data: Read the current day's I/O overuse stats from
159 * database.
160 * TODO(b/192665269): Fetch the safe-to-kill from daemon on initialization and update
161 * mSafeToKillPackages.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700162 */
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700163 synchronized (mLock) {
164 checkAndHandleDateChangeLocked();
165 }
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) {
460 int killableState = getPackageKillableStateForUserPackageLocked(
461 userId, genericPackageName,
462 mPackageInfoHandler.getComponentType(packageInfo.applicationInfo),
463 mSafeToKillPackages.contains(genericPackageName));
464 states.add(new PackageKillableState(packageInfo.packageName, userId,
465 killableState));
466 continue;
467 }
468 List<ApplicationInfo> applicationInfos =
469 applicationInfosBySharedPackage.get(genericPackageName);
470 if (applicationInfos == null) {
471 applicationInfos = new ArrayList<>();
472 }
473 applicationInfos.add(packageInfo.applicationInfo);
474 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos);
475 }
476 for (Map.Entry<String, List<ApplicationInfo>> entry :
477 applicationInfosBySharedPackage.entrySet()) {
478 String genericPackageName = entry.getKey();
479 List<ApplicationInfo> applicationInfos = entry.getValue();
480 int componentType = mPackageInfoHandler.getSharedComponentType(
481 applicationInfos, genericPackageName);
482 boolean isSafeToKill = mSafeToKillPackages.contains(genericPackageName);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700483 for (int i = 0; i < applicationInfos.size(); ++i) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700484 isSafeToKill = isSafeToKill
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700485 || mSafeToKillPackages.contains(applicationInfos.get(i).packageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700486 }
487 int killableState = getPackageKillableStateForUserPackageLocked(
488 userId, genericPackageName, componentType, isSafeToKill);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700489 for (int i = 0; i < applicationInfos.size(); ++i) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700490 states.add(new PackageKillableState(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700491 applicationInfos.get(i).packageName, userId, killableState));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700492 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700493 }
494 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700495 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700496 Slogf.d(TAG, "Returning the package killable states for user packages");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700497 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700498 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700499 }
500
501 /** Sets the given resource overuse configurations. */
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700502 @CarWatchdogManager.ReturnCode
503 public int setResourceOveruseConfigurations(
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700504 List<ResourceOveruseConfiguration> configurations,
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700505 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
506 throws RemoteException {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700507 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700508 Preconditions.checkArgument((configurations.size() > 0),
509 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700510 Preconditions.checkArgument((resourceOveruseFlag > 0),
511 "Must provide valid resource overuse flag");
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000512 checkResourceOveruseConfigs(configurations, resourceOveruseFlag);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700513 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
514 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700515 for (int i = 0; i < configurations.size(); ++i) {
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000516 internalConfigs.add(toInternalResourceOveruseConfiguration(configurations.get(i),
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700517 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700518 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700519 synchronized (mLock) {
520 if (!mIsConnectedToDaemon) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700521 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700522 return CarWatchdogManager.RETURN_CODE_SUCCESS;
523 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700524 /* Verify no pending request in progress. */
525 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700526 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700527 return setResourceOveruseConfigurationsInternal(internalConfigs,
528 /* isPendingRequest= */ false);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700529 }
530
531 /** Returns the available resource overuse configurations. */
532 @NonNull
533 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
534 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
535 Preconditions.checkArgument((resourceOveruseFlag > 0),
536 "Must provide valid resource overuse flag");
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700537 if (!isConnectedToDaemon()) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700538 throw new IllegalStateException("Car watchdog daemon is not connected");
539 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700540 synchronized (mLock) {
541 /* Verify no pending request in progress. */
542 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
543 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700544 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
545 new ArrayList<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700546 try {
547 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
548 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700549 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700550 throw new IllegalStateException(e);
551 }
552 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700553 for (int i = 0; i < internalConfigs.size(); ++i) {
554 configs.add(
555 toResourceOveruseConfiguration(internalConfigs.get(i), resourceOveruseFlag));
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700556 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700557 if (DEBUG) {
558 Slogf.d(TAG, "Returning the resource overuse configuration");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700559 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700560 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700561 }
562
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700563 /** Processes the latest I/O overuse stats */
564 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000565 SparseBooleanArray recurringIoOverusesByUid = new SparseBooleanArray();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700566 int[] uids = new int[packageIoOveruseStats.size()];
567 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
568 uids[i] = packageIoOveruseStats.get(i).uid;
569 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700570 SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700571 synchronized (mLock) {
572 checkAndHandleDateChangeLocked();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700573 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
574 PackageIoOveruseStats stats = packageIoOveruseStats.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700575 String genericPackageName = genericPackageNamesByUid.get(stats.uid);
576 if (genericPackageName == null) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700577 continue;
578 }
579 int userId = UserHandle.getUserId(stats.uid);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700580 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700581 stats.ioOveruseStats);
582 if (stats.shouldNotify) {
583 /*
584 * Packages that exceed the warn threshold percentage should be notified as well
585 * and only the daemon is aware of such packages. Thus the flag is used to
586 * indicate which packages should be notified.
587 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700588 ResourceOveruseStats resourceOveruseStats =
589 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700590 usage.getIoOveruseStatsLocked()).build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700591 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700592 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700593
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700594 if (!usage.ioUsage.exceedsThreshold()) {
595 continue;
596 }
597 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
598 overuseAction.packageIdentifier = new PackageIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700599 overuseAction.packageIdentifier.name = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700600 overuseAction.packageIdentifier.uid = stats.uid;
601 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
602 overuseAction.resourceOveruseActionType = NOT_KILLED;
603 /*
604 * No action required on I/O overuse on one of the following cases:
605 * #1 The package is not safe to kill as it is critical for system stability.
606 * #2 The package has no recurring overuse behavior and the user opted to not
607 * kill the package so honor the user's decision.
608 */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700609 int killableState = usage.getKillableStateLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700610 if (killableState == KILLABLE_STATE_NEVER) {
611 mOveruseActionsByUserPackage.add(overuseAction);
612 continue;
613 }
614 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
615 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
616 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
617 mOveruseActionsByUserPackage.add(overuseAction);
618 continue;
619 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000620
621 recurringIoOverusesByUid.put(stats.uid, hasRecurringOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700622 }
623 if (!mOveruseActionsByUserPackage.isEmpty()) {
624 mMainHandler.sendMessage(obtainMessage(
625 WatchdogPerfHandler::notifyActionsTakenOnOveruse, this));
626 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000627 if (recurringIoOverusesByUid.size() > 0) {
628 mMainHandler.sendMessageDelayed(
629 obtainMessage(WatchdogPerfHandler::handleIoOveruseKilling,
630 this, recurringIoOverusesByUid, genericPackageNamesByUid),
631 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
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700742 @GuardedBy("mLock")
743 private int getPackageKillableStateForUserPackageLocked(
744 int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
745 String key = getUserPackageUniqueId(userId, genericPackageName);
746 PackageResourceUsage usage = mUsageByUserPackage.get(key);
747 if (usage == null) {
748 usage = new PackageResourceUsage(userId, genericPackageName);
749 }
750 int killableState = usage.syncAndFetchKillableStateLocked(componentType, isSafeToKill);
751 mUsageByUserPackage.put(key, usage);
752 return killableState;
753 }
754
755 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700756 private void notifyResourceOveruseStatsLocked(int uid,
757 ResourceOveruseStats resourceOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700758 String genericPackageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700759 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
760 if (listenerInfos != null) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700761 for (int i = 0; i < listenerInfos.size(); ++i) {
762 listenerInfos.get(i).notifyListener(
763 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700764 }
765 }
766 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700767 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700768 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700769 for (int j = 0; j < systemListenerInfos.size(); ++j) {
770 systemListenerInfos.get(j).notifyListener(
771 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700772 }
773 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700774 if (DEBUG) {
775 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700776 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700777 }
778
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700779 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700780 private void checkAndHandleDateChangeLocked() {
781 ZonedDateTime previousUTC = mLastStatsReportUTC;
782 mLastStatsReportUTC = ZonedDateTime.now(ZoneOffset.UTC);
783 if (mLastStatsReportUTC.getDayOfYear() == previousUTC.getDayOfYear()
784 && mLastStatsReportUTC.getYear() == previousUTC.getYear()) {
785 return;
786 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700787 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
788 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700789 // Forgive the daily disabled package on date change.
790 for (Map.Entry<String, Integer> entry : usage.oldEnabledStateByPackage.entrySet()) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700791 try {
792 IPackageManager packageManager = ActivityThread.getPackageManager();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700793 if (packageManager.getApplicationEnabledSetting(entry.getKey(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700794 usage.userId)
795 != COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
796 continue;
797 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700798 packageManager.setApplicationEnabledSetting(entry.getKey(),
799 entry.getValue(),
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700800 /* flags= */ 0, usage.userId, mContext.getPackageName());
801 } catch (RemoteException e) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700802 Slogf.e(TAG,
803 "Failed to reset enabled setting for disabled package '%s', user '%d'",
804 usage.genericPackageName, usage.userId);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700805 }
806 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700807 /* TODO(b/192294393): Stash the old usage into SQLite DB storage. */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700808 usage.resetStatsLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700809 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700810 if (DEBUG) {
811 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700812 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700813 }
814
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700815 @GuardedBy("mLock")
816 private PackageResourceUsage cacheAndFetchUsageLocked(
817 @UserIdInt int userId, String genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700818 android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700819 String key = getUserPackageUniqueId(userId, genericPackageName);
820 PackageResourceUsage usage = mUsageByUserPackage.get(key);
821 if (usage == null) {
822 usage = new PackageResourceUsage(userId, genericPackageName);
823 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700824 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700825 mUsageByUserPackage.put(key, usage);
826 return usage;
827 }
828
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700829 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700830 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
831 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700832 * TODO(b/192294393): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700833 * has recurring I/O overuse behavior.
834 */
835 return false;
836 }
837
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700838 private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName,
839 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
840 synchronized (mLock) {
841 String key = getUserPackageUniqueId(userId, genericPackageName);
842 PackageResourceUsage usage = mUsageByUserPackage.get(key);
843 if (usage == null) {
844 return null;
845 }
846 return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700847 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700848 }
849
850 @GuardedBy("mLock")
851 private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage,
852 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
853 IoOveruseStats stats = usage.getIoOveruseStatsLocked();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700854 long totalBytesWritten = stats != null ? stats.getTotalBytesWritten() : 0;
855 /*
856 * TODO(b/185431129): When maxStatsPeriod > current day, populate the historical stats
857 * from the local database. Also handle the case where the package doesn't have current
858 * day stats but has historical stats.
859 */
860 if (totalBytesWritten < minimumBytesWritten) {
861 return null;
862 }
863 return stats;
864 }
865
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700866 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700867 private void addResourceOveruseListenerLocked(
868 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
869 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700870 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700871 int callingPid = Binder.getCallingPid();
872 int callingUid = Binder.getCallingUid();
873 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
874 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
875 "resource overuse listener";
876
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700877 IBinder binder = listener.asBinder();
878 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
879 if (listenerInfos == null) {
880 listenerInfos = new ArrayList<>();
881 listenerInfosByUid.put(callingUid, listenerInfos);
882 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700883 for (int i = 0; i < listenerInfos.size(); ++i) {
884 if (listenerInfos.get(i).listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700885 throw new IllegalStateException(
886 "Cannot add " + listenerType + " as it is already added");
887 }
888 }
889
890 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
891 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
892 try {
893 listenerInfo.linkToDeath();
894 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700895 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700896 return;
897 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700898 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700899 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700900 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
901 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700902 }
903 }
904
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700905 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700906 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700907 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700908 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700909 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
910 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700911 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
912 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700913 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700914 return;
915 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700916 IBinder binder = listener.asBinder();
917 ResourceOveruseListenerInfo cachedListenerInfo = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700918 for (int i = 0; i < listenerInfos.size(); ++i) {
919 if (listenerInfos.get(i).listener.asBinder() == binder) {
920 cachedListenerInfo = listenerInfos.get(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700921 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700922 }
923 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700924 if (cachedListenerInfo == null) {
925 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
926 return;
927 }
928 cachedListenerInfo.unlinkToDeath();
929 listenerInfos.remove(cachedListenerInfo);
930 if (listenerInfos.isEmpty()) {
931 listenerInfosByUid.remove(callingUid);
932 }
933 if (DEBUG) {
934 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
935 cachedListenerInfo.pid, cachedListenerInfo.uid);
936 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700937 }
938
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700939 @GuardedBy("mLock")
940 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
941 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
942 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
943 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
944 return;
945 }
946 throw new IllegalStateException(
947 "Pending setResourceOveruseConfigurations request in progress");
948 }
949 mPendingSetResourceOveruseConfigurationsRequest = configs;
950 }
951
952 private void retryPendingSetResourceOveruseConfigurations() {
953 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700954 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700955 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
956 return;
957 }
958 configs = mPendingSetResourceOveruseConfigurationsRequest;
959 }
960 try {
961 int result = setResourceOveruseConfigurationsInternal(configs,
962 /* isPendingRequest= */ true);
963 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
964 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
965 + "%d", result);
966 }
967 } catch (Exception e) {
968 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
969 }
970 }
971
972 private int setResourceOveruseConfigurationsInternal(
973 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
974 boolean isPendingRequest) throws RemoteException {
975 boolean doClearPendingRequest = isPendingRequest;
976 try {
977 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
978 } catch (RemoteException e) {
979 if (e instanceof TransactionTooLargeException) {
980 throw e;
981 }
982 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
983 synchronized (mLock) {
984 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
985 }
986 doClearPendingRequest = false;
987 return CarWatchdogManager.RETURN_CODE_SUCCESS;
988 } finally {
989 if (doClearPendingRequest) {
990 synchronized (mLock) {
991 mPendingSetResourceOveruseConfigurationsRequest = null;
992 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700993 }
994 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700995 /* TODO(b/192665269): Fetch safe-to-kill list from daemon and update mSafeToKillPackages. */
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700996 if (DEBUG) {
997 Slogf.d(TAG, "Set the resource overuse configuration successfully");
998 }
999 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1000 }
1001
1002 private boolean isConnectedToDaemon() {
1003 synchronized (mLock) {
1004 long startTimeMillis = SystemClock.uptimeMillis();
1005 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1006 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
1007 try {
1008 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
1009 } catch (InterruptedException e) {
1010 Thread.currentThread().interrupt();
1011 continue;
1012 } finally {
1013 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1014 }
1015 break;
1016 }
1017 return mIsConnectedToDaemon;
1018 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001019 }
1020
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001021 private static String getUserPackageUniqueId(int userId, String genericPackageName) {
1022 return String.valueOf(userId) + ":" + genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001023 }
1024
1025 @VisibleForTesting
1026 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
1027 android.automotive.watchdog.IoOveruseStats internalStats) {
1028 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
1029 internalStats.startTime, internalStats.durationInSeconds);
1030 statsBuilder.setRemainingWriteBytes(
1031 toPerStateBytes(internalStats.remainingWriteBytes));
1032 statsBuilder.setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes));
1033 statsBuilder.setTotalOveruses(internalStats.totalOveruses);
1034 return statsBuilder;
1035 }
1036
1037 private static PerStateBytes toPerStateBytes(
1038 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001039 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001040 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001041 }
1042
1043 private static long totalPerStateBytes(
1044 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
1045 BiFunction<Long, Long, Long> sum = (l, r) -> {
1046 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
1047 };
1048 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
1049 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
1050 }
1051
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001052 private static long getMinimumBytesWritten(
1053 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
1054 switch (minimumStatsIoFlag) {
1055 case 0:
1056 return 0;
1057 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
1058 return 1024 * 1024;
1059 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
1060 return 100 * 1024 * 1024;
1061 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
1062 return 1024 * 1024 * 1024;
1063 default:
1064 throw new IllegalArgumentException(
1065 "Must provide valid minimum stats flag for I/O resource");
1066 }
1067 }
1068
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001069 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
1070 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
1071 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1072 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
1073 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
1074 internalConfig.componentType = config.getComponentType();
1075 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
1076 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
1077 internalConfig.packageMetadata = new ArrayList<>();
1078 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
1079 if (entry.getKey().isEmpty()) {
1080 continue;
1081 }
1082 PackageMetadata metadata = new PackageMetadata();
1083 metadata.packageName = entry.getKey();
1084 switch(entry.getValue()) {
1085 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1086 metadata.appCategoryType = ApplicationCategoryType.MAPS;
1087 break;
1088 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1089 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
1090 break;
1091 default:
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001092 Slogf.i(TAG, "Invalid application category type: %s skipping package: %s",
1093 entry.getValue(), metadata.packageName);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001094 continue;
1095 }
1096 internalConfig.packageMetadata.add(metadata);
1097 }
1098 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
1099 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1100 && config.getIoOveruseConfiguration() != null) {
1101 internalConfig.resourceSpecificConfigurations.add(
1102 toResourceSpecificConfiguration(config.getComponentType(),
1103 config.getIoOveruseConfiguration()));
1104 }
1105 return internalConfig;
1106 }
1107
1108 private static ResourceSpecificConfiguration
1109 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
1110 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
1111 new android.automotive.watchdog.internal.IoOveruseConfiguration();
1112 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
1113 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
1114 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
1115 config.getPackageSpecificThresholds());
1116 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
1117 config.getAppCategorySpecificThresholds());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001118 for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) {
1119 PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001120 switch(threshold.name) {
1121 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1122 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
1123 break;
1124 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1125 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
1126 break;
1127 default:
1128 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
1129 }
1130 }
1131 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
1132 config.getSystemWideThresholds());
1133
1134 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1135 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1136 return resourceSpecificConfig;
1137 }
1138
1139 @VisibleForTesting
1140 static String toComponentTypeStr(int componentType) {
1141 switch(componentType) {
1142 case ComponentType.SYSTEM:
1143 return "SYSTEM";
1144 case ComponentType.VENDOR:
1145 return "VENDOR";
1146 case ComponentType.THIRD_PARTY:
1147 return "THIRD_PARTY";
1148 default:
1149 return "UNKNOWN";
1150 }
1151 }
1152
1153 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1154 Map<String, PerStateBytes> thresholds) {
1155 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1156 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001157 if (!thresholds.isEmpty()) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001158 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1159 entry.getValue()));
1160 }
1161 }
1162 return internalThresholds;
1163 }
1164
1165 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1166 PerStateBytes perStateBytes) {
1167 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1168 threshold.name = name;
1169 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1170 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1171 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1172 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1173 return threshold;
1174 }
1175
1176 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1177 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1178 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1179 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001180 for (int i = 0; i < thresholds.size(); ++i) {
1181 if (thresholds.get(i).getDurationInSeconds() == 0
1182 || thresholds.get(i).getWrittenBytesPerSecond() == 0) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001183 continue;
1184 }
1185 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1186 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001187 internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds();
1188 internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond();
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001189 internalThresholds.add(internalThreshold);
1190 }
1191 return internalThresholds;
1192 }
1193
1194 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1195 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1196 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001197 ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1198 for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001199 String categoryTypeStr;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001200 switch (internalConfig.packageMetadata.get(i).appCategoryType) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001201 case ApplicationCategoryType.MAPS:
1202 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1203 break;
1204 case ApplicationCategoryType.MEDIA:
1205 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1206 break;
1207 default:
1208 continue;
1209 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001210 packagesToAppCategoryTypes.put(
1211 internalConfig.packageMetadata.get(i).packageName, categoryTypeStr);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001212 }
1213 ResourceOveruseConfiguration.Builder configBuilder =
1214 new ResourceOveruseConfiguration.Builder(
1215 internalConfig.componentType,
1216 internalConfig.safeToKillPackages,
1217 internalConfig.vendorPackagePrefixes,
1218 packagesToAppCategoryTypes);
1219 for (ResourceSpecificConfiguration resourceSpecificConfig :
1220 internalConfig.resourceSpecificConfigurations) {
1221 if (resourceSpecificConfig.getTag()
1222 == ResourceSpecificConfiguration.ioOveruseConfiguration
1223 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1224 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1225 resourceSpecificConfig.getIoOveruseConfiguration()));
1226 }
1227 }
1228 return configBuilder.build();
1229 }
1230
1231 private static IoOveruseConfiguration toIoOveruseConfiguration(
1232 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
1233 PerStateBytes componentLevelThresholds =
1234 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001235 ArrayMap<String, PerStateBytes> packageSpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001236 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001237 ArrayMap<String, PerStateBytes> appCategorySpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001238 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
1239 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
1240 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
1241 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
1242 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1243 List<IoOveruseAlertThreshold> systemWideThresholds =
1244 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1245
1246 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1247 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1248 systemWideThresholds);
1249 return configBuilder.build();
1250 }
1251
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001252 private static ArrayMap<String, PerStateBytes> toPerStateBytesMap(
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001253 List<PerStateIoOveruseThreshold> thresholds) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001254 ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1255 for (int i = 0; i < thresholds.size(); ++i) {
1256 thresholdsMap.put(
1257 thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001258 }
1259 return thresholdsMap;
1260 }
1261
1262 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1263 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1264 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001265 for (int i = 0; i < internalThresholds.size(); ++i) {
1266 thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds,
1267 internalThresholds.get(i).writtenBytesPerSecond));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001268 }
1269 return thresholds;
1270 }
1271
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001272 private static void checkResourceOveruseConfigs(
1273 List<ResourceOveruseConfiguration> configurations,
1274 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1275 ArraySet<Integer> seenComponentTypes = new ArraySet<>();
1276 for (int i = 0; i < configurations.size(); ++i) {
1277 ResourceOveruseConfiguration config = configurations.get(i);
1278 if (seenComponentTypes.contains(config.getComponentType())) {
1279 throw new IllegalArgumentException(
1280 "Cannot provide duplicate configurations for the same component type");
1281 }
1282 checkResourceOveruseConfig(config, resourceOveruseFlag);
1283 seenComponentTypes.add(config.getComponentType());
1284 }
1285 }
1286
1287 private static void checkResourceOveruseConfig(ResourceOveruseConfiguration config,
1288 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1289 int componentType = config.getComponentType();
1290 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
1291 throw new IllegalArgumentException(
1292 "Invalid component type in the configuration: " + componentType);
1293 }
1294 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1295 && config.getIoOveruseConfiguration() == null) {
1296 throw new IllegalArgumentException("Must provide I/O overuse configuration");
1297 }
1298 checkIoOveruseConfig(config.getIoOveruseConfiguration(), componentType);
1299 }
1300
1301 private static void checkIoOveruseConfig(IoOveruseConfiguration config, int componentType) {
1302 if (config.getComponentLevelThresholds().getBackgroundModeBytes() <= 0
1303 || config.getComponentLevelThresholds().getForegroundModeBytes() <= 0
1304 || config.getComponentLevelThresholds().getGarageModeBytes() <= 0) {
1305 throw new IllegalArgumentException(
1306 "For component: " + toComponentTypeStr(componentType)
1307 + " some thresholds are zero for: "
1308 + config.getComponentLevelThresholds().toString());
1309 }
1310 if (componentType == ComponentType.SYSTEM) {
1311 List<IoOveruseAlertThreshold> systemThresholds = config.getSystemWideThresholds();
1312 if (systemThresholds.isEmpty()) {
1313 throw new IllegalArgumentException(
1314 "Empty system-wide alert thresholds provided in "
1315 + toComponentTypeStr(componentType)
1316 + " config.");
1317 }
1318 for (int i = 0; i < systemThresholds.size(); i++) {
1319 checkIoOveruseAlertThreshold(systemThresholds.get(i));
1320 }
1321 }
1322 }
1323
1324 private static void checkIoOveruseAlertThreshold(
1325 IoOveruseAlertThreshold ioOveruseAlertThreshold) {
1326 if (ioOveruseAlertThreshold.getDurationInSeconds() <= 0) {
1327 throw new IllegalArgumentException(
1328 "System wide threshold duration must be greater than zero for: "
1329 + ioOveruseAlertThreshold);
1330 }
1331 if (ioOveruseAlertThreshold.getWrittenBytesPerSecond() <= 0) {
1332 throw new IllegalArgumentException(
1333 "System wide threshold written bytes per second must be greater than zero for: "
1334 + ioOveruseAlertThreshold);
1335 }
1336 }
1337
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001338 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1339 PerStateBytes perStateBytes = map.get(oldKey);
1340 if (perStateBytes != null) {
1341 map.put(newKey, perStateBytes);
1342 map.remove(oldKey);
1343 }
1344 }
1345
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001346 private final class PackageResourceUsage {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001347 public final String genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001348 public @UserIdInt final int userId;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001349 @GuardedBy("mLock")
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001350 public final PackageIoUsage ioUsage = new PackageIoUsage();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001351 @GuardedBy("mLock")
1352 public final ArrayMap<String, Integer> oldEnabledStateByPackage = new ArrayMap<>();
1353 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001354 private @KillableState int mKillableState;
1355
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001356 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001357 PackageResourceUsage(@UserIdInt int userId, String genericPackageName) {
1358 this.genericPackageName = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001359 this.userId = userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001360 this.mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001361 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001362 }
1363
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001364 public boolean isSharedPackage() {
1365 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
1366 }
1367
1368 @GuardedBy("mLock")
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001369 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001370 if (!internalStats.killableOnOveruse) {
1371 /*
1372 * Killable value specified in the internal stats is provided by the native daemon.
1373 * This value reflects whether or not an application is safe-to-kill on overuse.
1374 * This setting is from the I/O overuse configuration specified by the system and
1375 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1376 * specify the application is not killable, the application is not safe-to-kill.
1377 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001378 mKillableState = KILLABLE_STATE_NEVER;
1379 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1380 /*
1381 * This case happens when a previously unsafe to kill system/vendor package was
1382 * recently marked as safe-to-kill so update the old state to the default value.
1383 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001384 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001385 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001386 }
1387 ioUsage.update(internalStats);
1388 }
1389
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001390 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001391 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001392 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001393
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001394 @GuardedBy("mLock")
1395 public IoOveruseStats getIoOveruseStatsLocked() {
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001396 if (!ioUsage.hasUsage()) {
1397 return null;
1398 }
1399 return ioUsage.getStatsBuilder().setKillableOnOveruse(
1400 mKillableState != KILLABLE_STATE_NEVER).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001401 }
1402
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001403 @GuardedBy("mLock")
1404 public @KillableState int getKillableStateLocked() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001405 return mKillableState;
1406 }
1407
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001408 @GuardedBy("mLock")
1409 public boolean setKillableStateLocked(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001410 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001411 return false;
1412 }
1413 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1414 return true;
1415 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001416
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001417 @GuardedBy("mLock")
1418 public int syncAndFetchKillableStateLocked(int myComponentType, boolean isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001419 /*
1420 * The killable state goes out-of-sync:
1421 * 1. When the on-device safe-to-kill list is recently updated and the user package
1422 * didn't have any resource usage so the native daemon didn't update the killable state.
1423 * 2. When a package has no resource usage and is initialized outside of processing the
1424 * latest resource usage stats.
1425 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001426 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001427 mKillableState = KILLABLE_STATE_NEVER;
1428 } else if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001429 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001430 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1431 }
1432 return mKillableState;
1433 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001434
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001435 @GuardedBy("mLock")
1436 public void resetStatsLocked() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001437 oldEnabledStateByPackage.clear();
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001438 ioUsage.resetStats();
1439 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001440 }
1441
1442 private static final class PackageIoUsage {
1443 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1444 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
1445 private long mTotalTimesKilled;
1446
1447 PackageIoUsage() {
1448 mTotalTimesKilled = 0;
1449 }
1450
1451 public boolean hasUsage() {
1452 return mIoOveruseStats != null;
1453 }
1454
1455 public void update(android.automotive.watchdog.IoOveruseStats internalStats) {
1456 mIoOveruseStats = internalStats;
1457 if (exceedsThreshold()) {
1458 /*
1459 * Forgive written bytes on overuse as the package is either forgiven or killed on
1460 * overuse. When the package is killed, the user may opt to open the corresponding
1461 * app and the package should be forgiven anyways.
1462 * NOTE: If this logic is updated, update the daemon side logic as well.
1463 */
1464 mForgivenWriteBytes = internalStats.writtenBytes;
1465 }
1466 }
1467
1468 public IoOveruseStats.Builder getStatsBuilder() {
1469 IoOveruseStats.Builder statsBuilder = toIoOveruseStatsBuilder(mIoOveruseStats);
1470 statsBuilder.setTotalTimesKilled(mTotalTimesKilled);
1471 return statsBuilder;
1472 }
1473
1474 public boolean exceedsThreshold() {
1475 if (!hasUsage()) {
1476 return false;
1477 }
1478 android.automotive.watchdog.PerStateBytes remaining =
1479 mIoOveruseStats.remainingWriteBytes;
1480 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1481 || remaining.garageModeBytes == 0;
1482 }
1483
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001484 public void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001485 mIoOveruseStats = null;
1486 mForgivenWriteBytes = null;
1487 mTotalTimesKilled = 0;
1488 }
1489 }
1490
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001491 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1492 public final IResourceOveruseListener listener;
1493 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1494 public final int pid;
1495 public final int uid;
1496 public final boolean isListenerForSystem;
1497
1498 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1499 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1500 boolean isListenerForSystem) {
1501 this.listener = listener;
1502 this.flag = flag;
1503 this.pid = pid;
1504 this.uid = uid;
1505 this.isListenerForSystem = isListenerForSystem;
1506 }
1507
1508 @Override
1509 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001510 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001511 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001512 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1513 listenerInfosByUid -> {
1514 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1515 listenerInfosByUid.get(uid);
1516 if (listenerInfos == null) {
1517 return;
1518 }
1519 listenerInfos.remove(this);
1520 if (listenerInfos.isEmpty()) {
1521 listenerInfosByUid.remove(uid);
1522 }
1523 };
1524 if (isListenerForSystem) {
1525 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1526 } else {
1527 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1528 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001529 unlinkToDeath();
1530 }
1531
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001532 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001533 int overusingUid, String overusingGenericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001534 ResourceOveruseStats resourceOveruseStats) {
1535 if ((flag & resourceType) == 0) {
1536 return;
1537 }
1538 try {
1539 listener.onOveruse(resourceOveruseStats);
1540 } catch (RemoteException e) {
1541 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001542 + "package(uid %d, generic package name '%s'): %s",
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001543 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001544 overusingUid, overusingGenericPackageName, e);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001545 }
1546 }
1547
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001548 private void linkToDeath() throws RemoteException {
1549 listener.asBinder().linkToDeath(this, 0);
1550 }
1551
1552 private void unlinkToDeath() {
1553 listener.asBinder().unlinkToDeath(this, 0);
1554 }
1555 }
1556}