blob: 6cf779433880c832a377a7cc6c87ace9c0da2aff [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 Annadorai0d3b1992021-08-17 09:11:24 -070024import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_CURRENT_DAY;
25import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_15_DAYS;
26import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_30_DAYS;
27import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_3_DAYS;
28import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070029import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NEVER;
30import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NO;
31import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_YES;
32import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
33import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
34import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
35
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070036import static com.android.car.watchdog.CarWatchdogService.DEBUG;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070037import static com.android.car.watchdog.CarWatchdogService.SYSTEM_INSTANCE;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070038import static com.android.car.watchdog.CarWatchdogService.TAG;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070039import static com.android.car.watchdog.PackageInfoHandler.SHARED_PACKAGE_PREFIX;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070040import static com.android.car.watchdog.WatchdogStorage.STATS_TEMPORAL_UNIT;
41import static com.android.car.watchdog.WatchdogStorage.ZONE_OFFSET;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070042
43import android.annotation.NonNull;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070044import android.annotation.UserIdInt;
45import android.app.ActivityThread;
46import android.automotive.watchdog.ResourceType;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070047import android.automotive.watchdog.internal.ApplicationCategoryType;
48import android.automotive.watchdog.internal.ComponentType;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070049import android.automotive.watchdog.internal.PackageIdentifier;
50import android.automotive.watchdog.internal.PackageIoOveruseStats;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070051import android.automotive.watchdog.internal.PackageMetadata;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070052import android.automotive.watchdog.internal.PackageResourceOveruseAction;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070053import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
54import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070055import android.car.watchdog.CarWatchdogManager;
56import android.car.watchdog.IResourceOveruseListener;
Lakshman Annadoraie1720472021-04-13 15:22:57 -070057import android.car.watchdog.IoOveruseAlertThreshold;
58import android.car.watchdog.IoOveruseConfiguration;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070059import android.car.watchdog.IoOveruseStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070060import android.car.watchdog.PackageKillableState;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070061import android.car.watchdog.PackageKillableState.KillableState;
62import android.car.watchdog.PerStateBytes;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070063import android.car.watchdog.ResourceOveruseConfiguration;
64import android.car.watchdog.ResourceOveruseStats;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070065import android.car.watchdoglib.CarWatchdogDaemonHelper;
66import android.content.Context;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070067import android.content.pm.ApplicationInfo;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070068import android.content.pm.IPackageManager;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070069import android.content.pm.PackageInfo;
70import android.content.pm.PackageManager;
71import android.content.pm.UserInfo;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070072import android.os.Binder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070073import android.os.Handler;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070074import android.os.IBinder;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070075import android.os.Looper;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070076import android.os.RemoteException;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -070077import android.os.SystemClock;
78import android.os.TransactionTooLargeException;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070079import android.os.UserHandle;
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -070080import android.os.UserManager;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070081import android.util.ArrayMap;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070082import android.util.ArraySet;
83import android.util.IndentingPrintWriter;
84import android.util.SparseArray;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +000085import android.util.SparseBooleanArray;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070086
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -070087import com.android.car.CarServiceUtils;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070088import com.android.internal.annotations.GuardedBy;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070089import com.android.internal.annotations.VisibleForTesting;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070090import com.android.internal.util.Preconditions;
91import com.android.server.utils.Slogf;
92
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070093import java.time.ZoneOffset;
94import java.time.ZonedDateTime;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070095import java.util.ArrayList;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -070096import java.util.Collections;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070097import java.util.List;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -070098import java.util.Map;
Lakshman Annadorai016127e2021-03-18 09:11:43 -070099import java.util.Objects;
100import java.util.Set;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700101import java.util.function.BiFunction;
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700102import java.util.function.Consumer;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700103
104/**
105 * Handles system resource performance monitoring module.
106 */
107public final class WatchdogPerfHandler {
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700108 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS = "MAPS";
109 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
110 public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
111
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000112 static final long RESOURCE_OVERUSE_KILLING_DELAY_MILLS = 10_000;
113
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700114 private static final long MAX_WAIT_TIME_MILLS = 3_000;
115
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700116 private final Context mContext;
117 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
118 private final PackageInfoHandler mPackageInfoHandler;
119 private final Handler mMainHandler;
Keun young Park5dc5a0c2021-09-29 14:44:27 -0700120 private final Handler mServiceHandler;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700121 private final WatchdogStorage mWatchdogStorage;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700122 private final Object mLock = new Object();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700123 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700124 private final ArrayMap<String, PackageResourceUsage> mUsageByUserPackage = new ArrayMap<>();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700125 @GuardedBy("mLock")
126 private final List<PackageResourceOveruseAction> mOveruseActionsByUserPackage =
127 new ArrayList<>();
128 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700129 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>> mOveruseListenerInfosByUid =
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700130 new SparseArray<>();
131 @GuardedBy("mLock")
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700132 private final SparseArray<ArrayList<ResourceOveruseListenerInfo>>
133 mOveruseSystemListenerInfosByUid = new SparseArray<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700134 /* Set of safe-to-kill system and vendor packages. */
135 @GuardedBy("mLock")
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000136 public final ArraySet<String> mSafeToKillSystemPackages = new ArraySet<>();
137 @GuardedBy("mLock")
138 public final ArraySet<String> mSafeToKillVendorPackages = new ArraySet<>();
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700139 /* Default killable state for packages when not updated by the user. */
140 @GuardedBy("mLock")
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700141 public final ArraySet<String> mDefaultNotKillableGenericPackages = new ArraySet<>();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700142 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700143 private ZonedDateTime mLatestStatsReportDate;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700144 @GuardedBy("mLock")
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700145 private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
146 mPendingSetResourceOveruseConfigurationsRequest = null;
147 @GuardedBy("mLock")
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700148 boolean mIsConnectedToDaemon;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000149 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700150 boolean mIsWrittenToDatabase;
151 @GuardedBy("mLock")
152 private TimeSourceInterface mTimeSource;
153 @GuardedBy("mLock")
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000154 long mResourceOveruseKillingDelayMills;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700155
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700156 public WatchdogPerfHandler(Context context, CarWatchdogDaemonHelper daemonHelper,
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700157 PackageInfoHandler packageInfoHandler, WatchdogStorage watchdogStorage) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700158 mContext = context;
159 mCarWatchdogDaemonHelper = daemonHelper;
160 mPackageInfoHandler = packageInfoHandler;
161 mMainHandler = new Handler(Looper.getMainLooper());
Keun young Park5dc5a0c2021-09-29 14:44:27 -0700162 mServiceHandler = new Handler(CarServiceUtils.getHandlerThread(
163 CarWatchdogService.class.getSimpleName()).getLooper());
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700164 mWatchdogStorage = watchdogStorage;
165 mTimeSource = SYSTEM_INSTANCE;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000166 mResourceOveruseKillingDelayMills = RESOURCE_OVERUSE_KILLING_DELAY_MILLS;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700167 }
168
169 /** Initializes the handler. */
170 public void init() {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700171 /* First database read is expensive, so post it on a separate handler thread. */
Keun young Park5dc5a0c2021-09-29 14:44:27 -0700172 mServiceHandler.post(() -> {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700173 readFromDatabase();
174 synchronized (mLock) {
175 checkAndHandleDateChangeLocked();
176 mIsWrittenToDatabase = false;
177 }});
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700178 if (DEBUG) {
179 Slogf.d(TAG, "WatchdogPerfHandler is initialized");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700180 }
181 }
182
183 /** Dumps its state. */
184 public void dump(IndentingPrintWriter writer) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700185 /*
186 * TODO(b/183436216): Implement this method.
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700187 */
188 }
189
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700190 /** Retries any pending requests on re-connecting to the daemon */
191 public void onDaemonConnectionChange(boolean isConnected) {
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700192 boolean hasPendingRequest;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700193 synchronized (mLock) {
194 mIsConnectedToDaemon = isConnected;
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700195 hasPendingRequest = mPendingSetResourceOveruseConfigurationsRequest != null;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700196 }
197 if (isConnected) {
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700198 if (hasPendingRequest) {
199 /*
200 * Retry pending set resource overuse configuration request before processing any
201 * new set/get requests. Thus notify the waiting requests only after the retry
202 * completes.
203 */
204 retryPendingSetResourceOveruseConfigurations();
205 } else {
206 /* Start fetch/sync configs only when there are no pending set requests because the
207 * above retry starts fetch/sync configs on success. If the retry fails, the daemon
208 * has crashed and shouldn't start fetchAndSyncResourceOveruseConfigurations.
209 */
210 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
211 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700212 }
213 synchronized (mLock) {
214 mLock.notifyAll();
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700215 }
216 }
217
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700218 /** Returns resource overuse stats for the calling package. */
219 @NonNull
220 public ResourceOveruseStats getResourceOveruseStats(
221 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
222 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
223 Preconditions.checkArgument((resourceOveruseFlag > 0),
224 "Must provide valid resource overuse flag");
225 Preconditions.checkArgument((maxStatsPeriod > 0),
226 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700227 // When more resource stats are added, make this as optional.
228 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
229 "Must provide resource I/O overuse flag");
230 int callingUid = Binder.getCallingUid();
231 int callingUserId = UserHandle.getUserId(callingUid);
232 UserHandle callingUserHandle = UserHandle.of(callingUserId);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700233 String genericPackageName =
234 mPackageInfoHandler.getNamesForUids(new int[]{callingUid})
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700235 .get(callingUid, null);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700236 if (genericPackageName == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700237 Slogf.w(TAG, "Failed to fetch package info for uid %d", callingUid);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700238 return new ResourceOveruseStats.Builder("", callingUserHandle).build();
239 }
240 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700241 new ResourceOveruseStats.Builder(genericPackageName, callingUserHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700242 statsBuilder.setIoOveruseStats(
243 getIoOveruseStatsForPeriod(callingUserId, genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700244 if (DEBUG) {
245 Slogf.d(TAG, "Returning all resource overuse stats for calling uid %d [user %d and "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700246 + "package '%s']", callingUid, callingUserId, genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700247 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700248 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700249 }
250
251 /** Returns resource overuse stats for all packages. */
252 @NonNull
253 public List<ResourceOveruseStats> getAllResourceOveruseStats(
254 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
255 @CarWatchdogManager.MinimumStatsFlag int minimumStatsFlag,
256 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
257 Preconditions.checkArgument((resourceOveruseFlag > 0),
258 "Must provide valid resource overuse flag");
259 Preconditions.checkArgument((maxStatsPeriod > 0),
260 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700261 // When more resource types are added, make this as optional.
262 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
263 "Must provide resource I/O overuse flag");
264 long minimumBytesWritten = getMinimumBytesWritten(minimumStatsFlag);
265 List<ResourceOveruseStats> allStats = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700266 synchronized (mLock) {
267 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
268 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
269 ResourceOveruseStats.Builder statsBuilder = usage.getResourceOveruseStatsBuilder();
270 IoOveruseStats ioOveruseStats =
271 getIoOveruseStatsLocked(usage, minimumBytesWritten, maxStatsPeriod);
272 if (ioOveruseStats == null) {
273 continue;
274 }
275 allStats.add(statsBuilder.setIoOveruseStats(ioOveruseStats).build());
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700276 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700277 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700278 if (DEBUG) {
279 Slogf.d(TAG, "Returning all resource overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700280 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700281 return allStats;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700282 }
283
284 /** Returns resource overuse stats for the specified user package. */
285 @NonNull
286 public ResourceOveruseStats getResourceOveruseStatsForUserPackage(
287 @NonNull String packageName, @NonNull UserHandle userHandle,
288 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
289 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
290 Objects.requireNonNull(packageName, "Package name must be non-null");
291 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700292 Preconditions.checkArgument(!userHandle.equals(UserHandle.ALL),
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700293 "Must provide the user handle for a specific user");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700294 Preconditions.checkArgument((resourceOveruseFlag > 0),
295 "Must provide valid resource overuse flag");
296 Preconditions.checkArgument((maxStatsPeriod > 0),
297 "Must provide valid maximum stats period");
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700298 // When more resource types are added, make this as optional.
299 Preconditions.checkArgument((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0,
300 "Must provide resource I/O overuse flag");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700301 String genericPackageName =
302 mPackageInfoHandler.getNameForUserPackage(packageName, userHandle.getIdentifier());
303 if (genericPackageName == null) {
304 throw new IllegalArgumentException("Package '" + packageName + "' not found");
305 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700306 ResourceOveruseStats.Builder statsBuilder =
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700307 new ResourceOveruseStats.Builder(genericPackageName, userHandle);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700308 statsBuilder.setIoOveruseStats(getIoOveruseStatsForPeriod(userHandle.getIdentifier(),
309 genericPackageName, maxStatsPeriod));
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700310 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700311 Slogf.d(TAG, "Returning resource overuse stats for user %d, package '%s', "
312 + "generic package '%s'", userHandle.getIdentifier(), packageName,
313 genericPackageName);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700314 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700315 return statsBuilder.build();
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700316 }
317
318 /** Adds the resource overuse listener. */
319 public void addResourceOveruseListener(
320 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
321 @NonNull IResourceOveruseListener listener) {
322 Objects.requireNonNull(listener, "Listener must be non-null");
323 Preconditions.checkArgument((resourceOveruseFlag > 0),
324 "Must provide valid resource overuse flag");
325 synchronized (mLock) {
326 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
327 mOveruseListenerInfosByUid);
328 }
329 }
330
331 /** Removes the previously added resource overuse listener. */
332 public void removeResourceOveruseListener(@NonNull IResourceOveruseListener listener) {
333 Objects.requireNonNull(listener, "Listener must be non-null");
334 synchronized (mLock) {
335 removeResourceOveruseListenerLocked(listener, mOveruseListenerInfosByUid);
336 }
337 }
338
339 /** Adds the resource overuse system listener. */
340 public void addResourceOveruseListenerForSystem(
341 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
342 @NonNull IResourceOveruseListener listener) {
343 Objects.requireNonNull(listener, "Listener must be non-null");
344 Preconditions.checkArgument((resourceOveruseFlag > 0),
345 "Must provide valid resource overuse flag");
346 synchronized (mLock) {
347 addResourceOveruseListenerLocked(resourceOveruseFlag, listener,
348 mOveruseSystemListenerInfosByUid);
349 }
350 }
351
352 /** Removes the previously added resource overuse system listener. */
353 public void removeResourceOveruseListenerForSystem(@NonNull IResourceOveruseListener listener) {
354 Objects.requireNonNull(listener, "Listener must be non-null");
355 synchronized (mLock) {
356 removeResourceOveruseListenerLocked(listener, mOveruseSystemListenerInfosByUid);
357 }
358 }
359
360 /** Sets whether or not a package is killable on resource overuse. */
361 public void setKillablePackageAsUser(String packageName, UserHandle userHandle,
362 boolean isKillable) {
363 Objects.requireNonNull(packageName, "Package name must be non-null");
364 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700365
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700366 if (userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700367 setPackageKillableStateForAllUsers(packageName, isKillable);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700368 return;
369 }
370 int userId = userHandle.getIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700371 String genericPackageName = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
372 if (genericPackageName == null) {
373 throw new IllegalArgumentException("Package '" + packageName + "' not found");
374 }
375 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700376 synchronized (mLock) {
377 /*
378 * When the queried package is not cached in {@link mUsageByUserPackage}, the set API
379 * will update the killable state even when the package should never be killed.
380 * But the get API will return the correct killable state. This behavior is tolerable
381 * because in production the set API should be called only after the get API.
382 * For instance, when this case happens by mistake and the package overuses resource
383 * between the set and the get API calls, the daemon will provide correct killable
384 * state when pushing the latest stats. Ergo, the invalid killable state doesn't have
385 * any effect.
386 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700387 PackageResourceUsage usage = mUsageByUserPackage.get(key);
388 if (usage == null) {
389 usage = new PackageResourceUsage(userId, genericPackageName);
390 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700391 if (!usage.verifyAndSetKillableStateLocked(isKillable)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700392 Slogf.e(TAG, "User %d cannot set killable state for package '%s'",
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700393 userHandle.getIdentifier(), genericPackageName);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700394 throw new IllegalArgumentException("Package killable state is not updatable");
395 }
396 mUsageByUserPackage.put(key, usage);
397 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700398 if (DEBUG) {
399 Slogf.d(TAG, "Successfully set killable package state for user %d", userId);
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700400 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700401 }
402
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700403 private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
404 UserManager userManager = UserManager.get(mContext);
405 List<UserInfo> userInfos = userManager.getAliveUsers();
406 String genericPackageName = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700407 synchronized (mLock) {
408 for (int i = 0; i < userInfos.size(); ++i) {
409 int userId = userInfos.get(i).id;
410 String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
411 if (name == null) {
412 continue;
413 }
414 genericPackageName = name;
415 String key = getUserPackageUniqueId(userId, genericPackageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700416 PackageResourceUsage usage = mUsageByUserPackage.get(key);
417 if (usage == null) {
418 continue;
419 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700420 if (!usage.verifyAndSetKillableStateLocked(isKillable)) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700421 Slogf.e(TAG, "Cannot set killable state for package '%s'", packageName);
422 throw new IllegalArgumentException(
423 "Package killable state is not updatable");
424 }
425 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700426 if (genericPackageName != null) {
427 if (!isKillable) {
428 mDefaultNotKillableGenericPackages.add(genericPackageName);
429 } else {
430 mDefaultNotKillableGenericPackages.remove(genericPackageName);
431 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700432 }
433 }
434 if (DEBUG) {
435 Slogf.d(TAG, "Successfully set killable package state for all users");
436 }
437 }
438
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700439 /** Returns the list of package killable states on resource overuse for the user. */
440 @NonNull
441 public List<PackageKillableState> getPackageKillableStatesAsUser(UserHandle userHandle) {
442 Objects.requireNonNull(userHandle, "User handle must be non-null");
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700443 PackageManager pm = mContext.getPackageManager();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700444 if (!userHandle.equals(UserHandle.ALL)) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700445 if (DEBUG) {
446 Slogf.d(TAG, "Returning all package killable states for user %d",
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700447 userHandle.getIdentifier());
448 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700449 return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
450 }
451 List<PackageKillableState> packageKillableStates = new ArrayList<>();
452 UserManager userManager = UserManager.get(mContext);
453 List<UserInfo> userInfos = userManager.getAliveUsers();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700454 for (int i = 0; i < userInfos.size(); ++i) {
455 packageKillableStates.addAll(
456 getPackageKillableStatesForUserId(userInfos.get(i).id, pm));
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700457 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700458 if (DEBUG) {
459 Slogf.d(TAG, "Returning all package killable states for all users");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700460 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700461 return packageKillableStates;
462 }
463
464 private List<PackageKillableState> getPackageKillableStatesForUserId(int userId,
465 PackageManager pm) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700466 List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(/* flags= */ 0, userId);
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700467 List<PackageKillableState> states = new ArrayList<>();
468 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700469 ArrayMap<String, List<ApplicationInfo>> applicationInfosBySharedPackage =
470 new ArrayMap<>();
471 for (int i = 0; i < packageInfos.size(); ++i) {
472 PackageInfo packageInfo = packageInfos.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700473 String genericPackageName = mPackageInfoHandler.getNameForPackage(packageInfo);
474 if (packageInfo.sharedUserId == null) {
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000475 int componentType = mPackageInfoHandler.getComponentType(
476 packageInfo.applicationInfo);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700477 int killableState = getPackageKillableStateForUserPackageLocked(
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000478 userId, genericPackageName, componentType,
479 isSafeToKillLocked(genericPackageName, componentType, null));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700480 states.add(new PackageKillableState(packageInfo.packageName, userId,
481 killableState));
482 continue;
483 }
484 List<ApplicationInfo> applicationInfos =
485 applicationInfosBySharedPackage.get(genericPackageName);
486 if (applicationInfos == null) {
487 applicationInfos = new ArrayList<>();
488 }
489 applicationInfos.add(packageInfo.applicationInfo);
490 applicationInfosBySharedPackage.put(genericPackageName, applicationInfos);
491 }
492 for (Map.Entry<String, List<ApplicationInfo>> entry :
493 applicationInfosBySharedPackage.entrySet()) {
494 String genericPackageName = entry.getKey();
495 List<ApplicationInfo> applicationInfos = entry.getValue();
496 int componentType = mPackageInfoHandler.getSharedComponentType(
497 applicationInfos, genericPackageName);
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000498 List<String> packageNames = new ArrayList<>(applicationInfos.size());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700499 for (int i = 0; i < applicationInfos.size(); ++i) {
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000500 packageNames.add(applicationInfos.get(i).packageName);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700501 }
502 int killableState = getPackageKillableStateForUserPackageLocked(
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000503 userId, genericPackageName, componentType,
504 isSafeToKillLocked(genericPackageName, componentType, packageNames));
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700505 for (int i = 0; i < applicationInfos.size(); ++i) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700506 states.add(new PackageKillableState(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700507 applicationInfos.get(i).packageName, userId, killableState));
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700508 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700509 }
510 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700511 if (DEBUG) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700512 Slogf.d(TAG, "Returning the package killable states for user packages");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700513 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700514 return states;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700515 }
516
517 /** Sets the given resource overuse configurations. */
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700518 @CarWatchdogManager.ReturnCode
519 public int setResourceOveruseConfigurations(
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700520 List<ResourceOveruseConfiguration> configurations,
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700521 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag)
522 throws RemoteException {
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700523 Objects.requireNonNull(configurations, "Configurations must be non-null");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700524 Preconditions.checkArgument((configurations.size() > 0),
525 "Must provide at least one configuration");
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700526 Preconditions.checkArgument((resourceOveruseFlag > 0),
527 "Must provide valid resource overuse flag");
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000528 checkResourceOveruseConfigs(configurations, resourceOveruseFlag);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700529 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
530 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700531 for (int i = 0; i < configurations.size(); ++i) {
Jahdiel Alvarez61481142021-07-31 00:13:19 +0000532 internalConfigs.add(toInternalResourceOveruseConfiguration(configurations.get(i),
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700533 resourceOveruseFlag));
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700534 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700535 synchronized (mLock) {
536 if (!mIsConnectedToDaemon) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700537 setPendingSetResourceOveruseConfigurationsRequestLocked(internalConfigs);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700538 return CarWatchdogManager.RETURN_CODE_SUCCESS;
539 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700540 /* Verify no pending request in progress. */
541 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700542 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700543 return setResourceOveruseConfigurationsInternal(internalConfigs,
544 /* isPendingRequest= */ false);
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700545 }
546
547 /** Returns the available resource overuse configurations. */
548 @NonNull
549 public List<ResourceOveruseConfiguration> getResourceOveruseConfigurations(
550 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
551 Preconditions.checkArgument((resourceOveruseFlag > 0),
552 "Must provide valid resource overuse flag");
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700553 if (!isConnectedToDaemon()) {
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -0700554 throw new IllegalStateException("Car watchdog daemon is not connected");
555 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700556 synchronized (mLock) {
557 /* Verify no pending request in progress. */
558 setPendingSetResourceOveruseConfigurationsRequestLocked(null);
559 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700560 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
561 new ArrayList<>();
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700562 try {
563 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
564 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700565 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700566 throw new IllegalStateException(e);
567 }
568 List<ResourceOveruseConfiguration> configs = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700569 for (int i = 0; i < internalConfigs.size(); ++i) {
570 configs.add(
571 toResourceOveruseConfiguration(internalConfigs.get(i), resourceOveruseFlag));
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700572 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700573 if (DEBUG) {
574 Slogf.d(TAG, "Returning the resource overuse configuration");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700575 }
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700576 return configs;
Lakshman Annadorai016127e2021-03-18 09:11:43 -0700577 }
578
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700579 /** Processes the latest I/O overuse stats */
580 public void latestIoOveruseStats(List<PackageIoOveruseStats> packageIoOveruseStats) {
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000581 SparseBooleanArray recurringIoOverusesByUid = new SparseBooleanArray();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700582 int[] uids = new int[packageIoOveruseStats.size()];
583 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
584 uids[i] = packageIoOveruseStats.get(i).uid;
585 }
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700586 SparseArray<String> genericPackageNamesByUid = mPackageInfoHandler.getNamesForUids(uids);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700587 synchronized (mLock) {
588 checkAndHandleDateChangeLocked();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700589 for (int i = 0; i < packageIoOveruseStats.size(); ++i) {
590 PackageIoOveruseStats stats = packageIoOveruseStats.get(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700591 String genericPackageName = genericPackageNamesByUid.get(stats.uid);
592 if (genericPackageName == null) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700593 continue;
594 }
595 int userId = UserHandle.getUserId(stats.uid);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700596 PackageResourceUsage usage = cacheAndFetchUsageLocked(userId, genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700597 stats.ioOveruseStats);
598 if (stats.shouldNotify) {
599 /*
600 * Packages that exceed the warn threshold percentage should be notified as well
601 * and only the daemon is aware of such packages. Thus the flag is used to
602 * indicate which packages should be notified.
603 */
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700604 ResourceOveruseStats resourceOveruseStats =
605 usage.getResourceOveruseStatsBuilder().setIoOveruseStats(
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700606 usage.getIoOveruseStatsLocked()).build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700607 notifyResourceOveruseStatsLocked(stats.uid, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700608 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700609
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700610 if (!usage.ioUsage.exceedsThreshold()) {
611 continue;
612 }
613 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
614 overuseAction.packageIdentifier = new PackageIdentifier();
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700615 overuseAction.packageIdentifier.name = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700616 overuseAction.packageIdentifier.uid = stats.uid;
617 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
618 overuseAction.resourceOveruseActionType = NOT_KILLED;
619 /*
620 * No action required on I/O overuse on one of the following cases:
621 * #1 The package is not safe to kill as it is critical for system stability.
622 * #2 The package has no recurring overuse behavior and the user opted to not
623 * kill the package so honor the user's decision.
624 */
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700625 int killableState = usage.getKillableStateLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700626 if (killableState == KILLABLE_STATE_NEVER) {
627 mOveruseActionsByUserPackage.add(overuseAction);
628 continue;
629 }
630 boolean hasRecurringOveruse = isRecurringOveruseLocked(usage);
631 if (!hasRecurringOveruse && killableState == KILLABLE_STATE_NO) {
632 overuseAction.resourceOveruseActionType = NOT_KILLED_USER_OPTED;
633 mOveruseActionsByUserPackage.add(overuseAction);
634 continue;
635 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000636 recurringIoOverusesByUid.put(stats.uid, hasRecurringOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700637 }
638 if (!mOveruseActionsByUserPackage.isEmpty()) {
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700639 mMainHandler.post(this::notifyActionsTakenOnOveruse);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700640 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000641 if (recurringIoOverusesByUid.size() > 0) {
Lakshman Annadoraia7189562021-08-17 16:36:16 -0700642 mMainHandler.postDelayed(
643 () -> handleIoOveruseKilling(
644 recurringIoOverusesByUid, genericPackageNamesByUid),
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000645 mResourceOveruseKillingDelayMills);
646 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700647 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700648 if (DEBUG) {
649 Slogf.d(TAG, "Processed latest I/O overuse stats");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700650 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700651 }
652
653 /** Notify daemon about the actions take on resource overuse */
654 public void notifyActionsTakenOnOveruse() {
655 List<PackageResourceOveruseAction> actions;
656 synchronized (mLock) {
657 if (mOveruseActionsByUserPackage.isEmpty()) {
658 return;
659 }
660 actions = new ArrayList<>(mOveruseActionsByUserPackage);
661 mOveruseActionsByUserPackage.clear();
662 }
663 try {
664 mCarWatchdogDaemonHelper.actionTakenOnResourceOveruse(actions);
Lakshman Annadoraie1720472021-04-13 15:22:57 -0700665 } catch (RemoteException | RuntimeException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700666 Slogf.w(TAG, e, "Failed to notify car watchdog daemon of actions taken on resource "
667 + "overuse");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700668 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700669 if (DEBUG) {
670 Slogf.d(TAG, "Notified car watchdog daemon of actions taken on resource overuse");
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700671 }
672 }
673
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000674 /** Handle packages that exceed resource overuse thresholds */
675 private void handleIoOveruseKilling(SparseBooleanArray recurringIoOverusesByUid,
676 SparseArray<String> genericPackageNamesByUid) {
677 IPackageManager packageManager = ActivityThread.getPackageManager();
678 synchronized (mLock) {
679 for (int i = 0; i < recurringIoOverusesByUid.size(); i++) {
680 int uid = recurringIoOverusesByUid.keyAt(i);
681 boolean hasRecurringOveruse = recurringIoOverusesByUid.valueAt(i);
682 String genericPackageName = genericPackageNamesByUid.get(uid);
683 int userId = UserHandle.getUserId(uid);
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000684
685 PackageResourceOveruseAction overuseAction = new PackageResourceOveruseAction();
686 overuseAction.packageIdentifier = new PackageIdentifier();
687 overuseAction.packageIdentifier.name = genericPackageName;
688 overuseAction.packageIdentifier.uid = uid;
689 overuseAction.resourceTypes = new int[]{ ResourceType.IO };
690 overuseAction.resourceOveruseActionType = NOT_KILLED;
691
Lakshman Annadoraib9bfe492021-08-12 12:08:06 -0700692 String key = getUserPackageUniqueId(userId, genericPackageName);
693 PackageResourceUsage usage = mUsageByUserPackage.get(key);
694 if (usage == null) {
695 /* This case shouldn't happen but placed here as a fail safe. */
696 mOveruseActionsByUserPackage.add(overuseAction);
697 continue;
698 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000699 List<String> packages = Collections.singletonList(genericPackageName);
700 if (usage.isSharedPackage()) {
701 packages = mPackageInfoHandler.getPackagesForUid(uid, genericPackageName);
702 }
703 for (int pkgIdx = 0; pkgIdx < packages.size(); pkgIdx++) {
704 String packageName = packages.get(pkgIdx);
705 try {
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000706 if (!hasRecurringOveruse) {
Lakshman Annadoraicda55ca2021-09-16 14:33:04 -0700707 int currentEnabledState = packageManager.getApplicationEnabledSetting(
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000708 packageName, userId);
Lakshman Annadoraicda55ca2021-09-16 14:33:04 -0700709 if (currentEnabledState == COMPONENT_ENABLED_STATE_DISABLED
710 || currentEnabledState == COMPONENT_ENABLED_STATE_DISABLED_USER
711 || currentEnabledState
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000712 == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
713 continue;
714 }
715 }
716 packageManager.setApplicationEnabledSetting(packageName,
717 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0, userId,
718 mContext.getPackageName());
719 overuseAction.resourceOveruseActionType = hasRecurringOveruse
720 ? KILLED_RECURRING_OVERUSE : KILLED;
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000721 } catch (RemoteException e) {
722 Slogf.e(TAG, "Failed to disable application for user %d, package '%s'",
723 userId, packageName);
724 }
725 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700726 if (overuseAction.resourceOveruseActionType == KILLED
727 || overuseAction.resourceOveruseActionType == KILLED_RECURRING_OVERUSE) {
728 usage.ioUsage.killed();
729 }
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000730 mOveruseActionsByUserPackage.add(overuseAction);
731 }
732 if (!mOveruseActionsByUserPackage.isEmpty()) {
733 notifyActionsTakenOnOveruse();
734 }
735 }
736 }
737
Jahdiel Alvarezf91bf312021-09-04 01:03:50 +0000738 /** Resets the resource overuse settings and stats for the given generic package names. */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700739 public void resetResourceOveruseStats(Set<String> genericPackageNames) {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700740 synchronized (mLock) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700741 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
742 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700743 if (genericPackageNames.contains(usage.genericPackageName)) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700744 usage.resetStatsLocked();
Jahdiel Alvarezf91bf312021-09-04 01:03:50 +0000745 usage.verifyAndSetKillableStateLocked(/* isKillable= */ true);
746 mWatchdogStorage.deleteUserPackage(usage.userId, usage.genericPackageName);
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -0700747 }
748 }
749 }
750 }
751
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700752 /** Sets the time source. */
753 public void setTimeSource(TimeSourceInterface timeSource) {
754 synchronized (mLock) {
755 mTimeSource = timeSource;
756 }
757 }
758
759 /** Sets the delay to kill a package after the package is notified of resource overuse. */
Jahdiel Alvarez8bf64ce2021-07-21 01:03:42 +0000760 public void setResourceOveruseKillingDelay(long millis) {
761 synchronized (mLock) {
762 mResourceOveruseKillingDelayMills = millis;
763 }
764 }
765
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000766 /** Fetches and syncs the resource overuse configurations from watchdog daemon. */
767 private void fetchAndSyncResourceOveruseConfigurations() {
768 synchronized (mLock) {
769 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs;
770 try {
771 internalConfigs = mCarWatchdogDaemonHelper.getResourceOveruseConfigurations();
772 } catch (RemoteException | RuntimeException e) {
773 Slogf.w(TAG, e, "Failed to fetch resource overuse configurations");
774 return;
775 }
776 if (internalConfigs.isEmpty()) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700777 Slogf.e(TAG, "Fetched resource overuse configurations are empty");
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000778 return;
779 }
780 mSafeToKillSystemPackages.clear();
781 mSafeToKillVendorPackages.clear();
782 for (int i = 0; i < internalConfigs.size(); i++) {
783 switch (internalConfigs.get(i).componentType) {
784 case ComponentType.SYSTEM:
785 mSafeToKillSystemPackages.addAll(internalConfigs.get(i).safeToKillPackages);
786 break;
787 case ComponentType.VENDOR:
788 mSafeToKillVendorPackages.addAll(internalConfigs.get(i).safeToKillPackages);
Jahdiel Alvarez5528c712021-08-10 21:07:40 +0000789 mPackageInfoHandler.setVendorPackagePrefixes(
790 internalConfigs.get(i).vendorPackagePrefixes);
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000791 break;
792 default:
793 // All third-party apps are killable.
794 break;
795 }
796 }
797 if (DEBUG) {
Jahdiel Alvarez5528c712021-08-10 21:07:40 +0000798 Slogf.d(TAG, "Fetched and synced resource overuse configs.");
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +0000799 }
800 }
801 }
802
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700803 private void readFromDatabase() {
804 List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries =
805 mWatchdogStorage.getUserPackageSettings();
806 Slogf.i(TAG, "Read %d user package settings from database", settingsEntries.size());
807 List<WatchdogStorage.IoUsageStatsEntry> ioStatsEntries =
808 mWatchdogStorage.getTodayIoUsageStats();
809 Slogf.i(TAG, "Read %d I/O usage stats from database", ioStatsEntries.size());
810 synchronized (mLock) {
811 for (int i = 0; i < settingsEntries.size(); ++i) {
812 WatchdogStorage.UserPackageSettingsEntry entry = settingsEntries.get(i);
813 if (entry.userId == UserHandle.USER_ALL) {
814 if (entry.killableState != KILLABLE_STATE_YES) {
815 mDefaultNotKillableGenericPackages.add(entry.packageName);
816 }
817 continue;
818 }
819 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
820 PackageResourceUsage usage = mUsageByUserPackage.get(key);
821 if (usage == null) {
822 usage = new PackageResourceUsage(entry.userId, entry.packageName);
823 }
824 usage.setKillableStateLocked(entry.killableState);
825 mUsageByUserPackage.put(key, usage);
826 }
827 ZonedDateTime curReportDate =
828 mTimeSource.now().atZone(ZONE_OFFSET).truncatedTo(STATS_TEMPORAL_UNIT);
829 for (int i = 0; i < ioStatsEntries.size(); ++i) {
830 WatchdogStorage.IoUsageStatsEntry entry = ioStatsEntries.get(i);
831 String key = getUserPackageUniqueId(entry.userId, entry.packageName);
832 PackageResourceUsage usage = mUsageByUserPackage.get(key);
833 if (usage == null) {
834 usage = new PackageResourceUsage(entry.userId, entry.packageName);
835 }
836 /* Overwrite in memory cache as the stats will be merged on the daemon side and
837 * pushed on the next latestIoOveruseStats call. This is tolerable because the next
838 * push should happen soon.
839 */
840 usage.ioUsage.overwrite(entry.ioUsage);
841 mUsageByUserPackage.put(key, usage);
842 }
843 if (!ioStatsEntries.isEmpty()) {
844 /* When mLatestStatsReportDate is null, the latest stats push from daemon hasn't
845 * happened yet. Thus the cached stats contains only the stats read from database.
846 */
847 mIsWrittenToDatabase = mLatestStatsReportDate == null;
848 mLatestStatsReportDate = curReportDate;
849 }
850 }
851 }
852
853 /** Writes user package settings and stats to database. */
854 public void writeToDatabase() {
855 synchronized (mLock) {
856 if (mIsWrittenToDatabase) {
857 return;
858 }
859 List<WatchdogStorage.UserPackageSettingsEntry> entries =
860 new ArrayList<>(mUsageByUserPackage.size());
861 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
862 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
863 entries.add(new WatchdogStorage.UserPackageSettingsEntry(
864 usage.userId, usage.genericPackageName, usage.getKillableStateLocked()));
865 }
866 for (String packageName : mDefaultNotKillableGenericPackages) {
867 entries.add(new WatchdogStorage.UserPackageSettingsEntry(
868 UserHandle.USER_ALL, packageName, KILLABLE_STATE_NO));
869 }
870 if (!mWatchdogStorage.saveUserPackageSettings(entries)) {
871 Slogf.e(TAG, "Failed to write user package settings to database");
872 } else {
873 Slogf.i(TAG, "Successfully saved %d user package settings to database",
874 entries.size());
875 }
876 writeStatsLocked();
877 mIsWrittenToDatabase = true;
878 }
879 }
880
881 @GuardedBy("mLock")
882 private void writeStatsLocked() {
883 List<WatchdogStorage.IoUsageStatsEntry> entries =
884 new ArrayList<>(mUsageByUserPackage.size());
885 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
886 PackageResourceUsage usage = mUsageByUserPackage.valueAt(i);
887 if (!usage.ioUsage.hasUsage()) {
888 continue;
889 }
890 entries.add(new WatchdogStorage.IoUsageStatsEntry(
891 usage.userId, usage.genericPackageName, usage.ioUsage));
892 }
893 if (!mWatchdogStorage.saveIoUsageStats(entries)) {
894 Slogf.e(TAG, "Failed to write %d I/O overuse stats to database", entries.size());
895 } else {
896 Slogf.i(TAG, "Successfully saved %d I/O overuse stats to database", entries.size());
897 }
898 }
899
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700900 @GuardedBy("mLock")
901 private int getPackageKillableStateForUserPackageLocked(
902 int userId, String genericPackageName, int componentType, boolean isSafeToKill) {
903 String key = getUserPackageUniqueId(userId, genericPackageName);
904 PackageResourceUsage usage = mUsageByUserPackage.get(key);
905 if (usage == null) {
906 usage = new PackageResourceUsage(userId, genericPackageName);
907 }
908 int killableState = usage.syncAndFetchKillableStateLocked(componentType, isSafeToKill);
909 mUsageByUserPackage.put(key, usage);
910 return killableState;
911 }
912
913 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700914 private void notifyResourceOveruseStatsLocked(int uid,
915 ResourceOveruseStats resourceOveruseStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700916 String genericPackageName = resourceOveruseStats.getPackageName();
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700917 ArrayList<ResourceOveruseListenerInfo> listenerInfos = mOveruseListenerInfosByUid.get(uid);
918 if (listenerInfos != null) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700919 for (int i = 0; i < listenerInfos.size(); ++i) {
920 listenerInfos.get(i).notifyListener(
921 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700922 }
923 }
924 for (int i = 0; i < mOveruseSystemListenerInfosByUid.size(); ++i) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -0700925 ArrayList<ResourceOveruseListenerInfo> systemListenerInfos =
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700926 mOveruseSystemListenerInfosByUid.valueAt(i);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700927 for (int j = 0; j < systemListenerInfos.size(); ++j) {
928 systemListenerInfos.get(j).notifyListener(
929 FLAG_RESOURCE_OVERUSE_IO, uid, genericPackageName, resourceOveruseStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700930 }
931 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700932 if (DEBUG) {
933 Slogf.d(TAG, "Notified resource overuse stats to listening applications");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700934 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700935 }
936
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700937 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700938 private void checkAndHandleDateChangeLocked() {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700939 ZonedDateTime currentDate = mTimeSource.now().atZone(ZoneOffset.UTC)
940 .truncatedTo(STATS_TEMPORAL_UNIT);
941 if (currentDate.equals(mLatestStatsReportDate)) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700942 return;
943 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700944 /* After the first database read or on the first stats sync from the daemon, whichever
945 * happens first, the cached stats would either be empty or initialized from the database.
946 * In either case, don't write to database.
947 */
948 if (mLatestStatsReportDate != null && !mIsWrittenToDatabase) {
949 writeStatsLocked();
950 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700951 for (int i = 0; i < mUsageByUserPackage.size(); ++i) {
Lakshman Annadoraicda55ca2021-09-16 14:33:04 -0700952 mUsageByUserPackage.valueAt(i).resetStatsLocked();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700953 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700954 mLatestStatsReportDate = currentDate;
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700955 if (DEBUG) {
956 Slogf.d(TAG, "Handled date change successfully");
Lakshman Annadoraid7b8a032021-04-20 12:31:05 -0700957 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700958 }
959
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700960 @GuardedBy("mLock")
961 private PackageResourceUsage cacheAndFetchUsageLocked(
962 @UserIdInt int userId, String genericPackageName,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700963 android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700964 String key = getUserPackageUniqueId(userId, genericPackageName);
965 PackageResourceUsage usage = mUsageByUserPackage.get(key);
966 if (usage == null) {
967 usage = new PackageResourceUsage(userId, genericPackageName);
968 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -0700969 usage.updateLocked(internalStats);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700970 mUsageByUserPackage.put(key, usage);
971 return usage;
972 }
973
Lakshman Annadorai94323ff2021-04-29 12:14:34 -0700974 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700975 private boolean isRecurringOveruseLocked(PackageResourceUsage ioUsage) {
976 /*
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -0700977 * TODO(b/192294393): Look up I/O overuse history and determine whether or not the package
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -0700978 * has recurring I/O overuse behavior.
979 */
980 return false;
981 }
982
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700983 private IoOveruseStats getIoOveruseStatsForPeriod(int userId, String genericPackageName,
984 @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
985 synchronized (mLock) {
986 String key = getUserPackageUniqueId(userId, genericPackageName);
987 PackageResourceUsage usage = mUsageByUserPackage.get(key);
988 if (usage == null) {
989 return null;
990 }
991 return getIoOveruseStatsLocked(usage, /* minimumBytesWritten= */ 0, maxStatsPeriod);
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -0700992 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -0700993 }
994
995 @GuardedBy("mLock")
996 private IoOveruseStats getIoOveruseStatsLocked(PackageResourceUsage usage,
997 long minimumBytesWritten, @CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -0700998 if (!usage.ioUsage.hasUsage()) {
999 /* Return I/O overuse stats only when the package has usage for the current day.
1000 * Without the current day usage, the returned stats will contain zero remaining
1001 * bytes, which is incorrect.
1002 */
1003 return null;
1004 }
1005 IoOveruseStats currentStats = usage.getIoOveruseStatsLocked();
1006 long totalBytesWritten = currentStats.getTotalBytesWritten();
1007 int numDays = toNumDays(maxStatsPeriod);
1008 IoOveruseStats historyStats = null;
1009 if (numDays > 0) {
1010 historyStats = mWatchdogStorage.getHistoricalIoOveruseStats(
1011 usage.userId, usage.genericPackageName, numDays - 1);
1012 totalBytesWritten += historyStats != null ? historyStats.getTotalBytesWritten() : 0;
1013 }
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001014 if (totalBytesWritten < minimumBytesWritten) {
1015 return null;
1016 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001017 if (historyStats == null) {
1018 return currentStats;
1019 }
1020 IoOveruseStats.Builder statsBuilder = new IoOveruseStats.Builder(
1021 historyStats.getStartTime(),
1022 historyStats.getDurationInSeconds() + currentStats.getDurationInSeconds());
1023 statsBuilder.setTotalTimesKilled(
1024 historyStats.getTotalTimesKilled() + currentStats.getTotalTimesKilled());
1025 statsBuilder.setTotalOveruses(
1026 historyStats.getTotalOveruses() + currentStats.getTotalOveruses());
1027 statsBuilder.setTotalBytesWritten(
1028 historyStats.getTotalBytesWritten() + currentStats.getTotalBytesWritten());
1029 statsBuilder.setKillableOnOveruse(currentStats.isKillableOnOveruse());
1030 statsBuilder.setRemainingWriteBytes(currentStats.getRemainingWriteBytes());
1031 return statsBuilder.build();
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001032 }
1033
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001034 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001035 private void addResourceOveruseListenerLocked(
1036 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag,
1037 @NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001038 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001039 int callingPid = Binder.getCallingPid();
1040 int callingUid = Binder.getCallingUid();
1041 boolean isListenerForSystem = listenerInfosByUid == mOveruseSystemListenerInfosByUid;
1042 String listenerType = isListenerForSystem ? "resource overuse listener for system" :
1043 "resource overuse listener";
1044
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001045 IBinder binder = listener.asBinder();
1046 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1047 if (listenerInfos == null) {
1048 listenerInfos = new ArrayList<>();
1049 listenerInfosByUid.put(callingUid, listenerInfos);
1050 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001051 for (int i = 0; i < listenerInfos.size(); ++i) {
1052 if (listenerInfos.get(i).listener.asBinder() == binder) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001053 throw new IllegalStateException(
1054 "Cannot add " + listenerType + " as it is already added");
1055 }
1056 }
1057
1058 ResourceOveruseListenerInfo listenerInfo = new ResourceOveruseListenerInfo(listener,
1059 resourceOveruseFlag, callingPid, callingUid, isListenerForSystem);
1060 try {
1061 listenerInfo.linkToDeath();
1062 } catch (RemoteException e) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001063 Slogf.w(TAG, "Cannot add %s: linkToDeath to listener failed", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001064 return;
1065 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001066 listenerInfos.add(listenerInfo);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001067 if (DEBUG) {
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001068 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is added", listenerType,
1069 callingPid, callingUid);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001070 }
1071 }
1072
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001073 @GuardedBy("mLock")
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001074 private void removeResourceOveruseListenerLocked(@NonNull IResourceOveruseListener listener,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001075 SparseArray<ArrayList<ResourceOveruseListenerInfo>> listenerInfosByUid) {
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001076 int callingUid = Binder.getCallingUid();
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001077 String listenerType = listenerInfosByUid == mOveruseSystemListenerInfosByUid
1078 ? "resource overuse system listener" : "resource overuse listener";
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001079 ArrayList<ResourceOveruseListenerInfo> listenerInfos = listenerInfosByUid.get(callingUid);
1080 if (listenerInfos == null) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001081 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001082 return;
1083 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001084 IBinder binder = listener.asBinder();
1085 ResourceOveruseListenerInfo cachedListenerInfo = null;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001086 for (int i = 0; i < listenerInfos.size(); ++i) {
1087 if (listenerInfos.get(i).listener.asBinder() == binder) {
1088 cachedListenerInfo = listenerInfos.get(i);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001089 break;
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001090 }
1091 }
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001092 if (cachedListenerInfo == null) {
1093 Slogf.w(TAG, "Cannot remove the %s: it has not been registered before", listenerType);
1094 return;
1095 }
1096 cachedListenerInfo.unlinkToDeath();
1097 listenerInfos.remove(cachedListenerInfo);
1098 if (listenerInfos.isEmpty()) {
1099 listenerInfosByUid.remove(callingUid);
1100 }
1101 if (DEBUG) {
1102 Slogf.d(TAG, "The %s (pid: %d, uid: %d) is removed", listenerType,
1103 cachedListenerInfo.pid, cachedListenerInfo.uid);
1104 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001105 }
1106
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001107 @GuardedBy("mLock")
1108 private void setPendingSetResourceOveruseConfigurationsRequestLocked(
1109 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs) {
1110 if (mPendingSetResourceOveruseConfigurationsRequest != null) {
1111 if (mPendingSetResourceOveruseConfigurationsRequest == configs) {
1112 return;
1113 }
1114 throw new IllegalStateException(
1115 "Pending setResourceOveruseConfigurations request in progress");
1116 }
1117 mPendingSetResourceOveruseConfigurationsRequest = configs;
1118 }
1119
1120 private void retryPendingSetResourceOveruseConfigurations() {
1121 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs;
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001122 synchronized (mLock) {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001123 if (mPendingSetResourceOveruseConfigurationsRequest == null) {
1124 return;
1125 }
1126 configs = mPendingSetResourceOveruseConfigurationsRequest;
1127 }
1128 try {
1129 int result = setResourceOveruseConfigurationsInternal(configs,
1130 /* isPendingRequest= */ true);
1131 if (result != CarWatchdogManager.RETURN_CODE_SUCCESS) {
1132 Slogf.e(TAG, "Failed to set pending resource overuse configurations. Return code "
1133 + "%d", result);
1134 }
1135 } catch (Exception e) {
1136 Slogf.e(TAG, e, "Exception on set pending resource overuse configurations");
1137 }
1138 }
1139
1140 private int setResourceOveruseConfigurationsInternal(
1141 List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs,
1142 boolean isPendingRequest) throws RemoteException {
1143 boolean doClearPendingRequest = isPendingRequest;
1144 try {
1145 mCarWatchdogDaemonHelper.updateResourceOveruseConfigurations(configs);
Lakshman Annadoraia7189562021-08-17 16:36:16 -07001146 mMainHandler.post(this::fetchAndSyncResourceOveruseConfigurations);
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001147 } catch (RemoteException e) {
1148 if (e instanceof TransactionTooLargeException) {
1149 throw e;
1150 }
1151 Slogf.e(TAG, e, "Remote exception on set resource overuse configuration");
1152 synchronized (mLock) {
1153 setPendingSetResourceOveruseConfigurationsRequestLocked(configs);
1154 }
1155 doClearPendingRequest = false;
1156 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1157 } finally {
1158 if (doClearPendingRequest) {
1159 synchronized (mLock) {
1160 mPendingSetResourceOveruseConfigurationsRequest = null;
1161 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001162 }
1163 }
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001164 if (DEBUG) {
1165 Slogf.d(TAG, "Set the resource overuse configuration successfully");
1166 }
1167 return CarWatchdogManager.RETURN_CODE_SUCCESS;
1168 }
1169
1170 private boolean isConnectedToDaemon() {
1171 synchronized (mLock) {
1172 long startTimeMillis = SystemClock.uptimeMillis();
1173 long sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1174 while (!mIsConnectedToDaemon && sleptDurationMillis < MAX_WAIT_TIME_MILLS) {
1175 try {
1176 mLock.wait(MAX_WAIT_TIME_MILLS - sleptDurationMillis);
1177 } catch (InterruptedException e) {
1178 Thread.currentThread().interrupt();
1179 continue;
1180 } finally {
1181 sleptDurationMillis = SystemClock.uptimeMillis() - startTimeMillis;
1182 }
1183 break;
1184 }
1185 return mIsConnectedToDaemon;
1186 }
Lakshman Annadorai8ee56ec2021-04-27 11:51:08 -07001187 }
1188
Jahdiel Alvarez99c33d72021-07-16 03:46:39 +00001189 @GuardedBy("mLock")
1190 private boolean isSafeToKillLocked(String genericPackageName, int componentType,
1191 List<String> sharedPackages) {
1192 BiFunction<List<String>, Set<String>, Boolean> isSafeToKillAnyPackage =
1193 (packages, safeToKillPackages) -> {
1194 if (packages == null) {
1195 return false;
1196 }
1197 for (int i = 0; i < packages.size(); i++) {
1198 if (safeToKillPackages.contains(packages.get(i))) {
1199 return true;
1200 }
1201 }
1202 return false;
1203 };
1204
1205 switch (componentType) {
1206 case ComponentType.SYSTEM:
1207 if (mSafeToKillSystemPackages.contains(genericPackageName)) {
1208 return true;
1209 }
1210 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages);
1211 case ComponentType.VENDOR:
1212 if (mSafeToKillVendorPackages.contains(genericPackageName)) {
1213 return true;
1214 }
1215 /*
1216 * Packages under the vendor shared UID may contain system packages because when
1217 * CarWatchdogService derives the shared component type it attributes system
1218 * packages as vendor packages when there is at least one vendor package.
1219 */
1220 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages)
1221 || isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillVendorPackages);
1222 default:
1223 // Third-party apps are always killable
1224 return true;
1225 }
1226 }
1227
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001228 private static String getUserPackageUniqueId(int userId, String genericPackageName) {
1229 return String.valueOf(userId) + ":" + genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001230 }
1231
1232 @VisibleForTesting
1233 static IoOveruseStats.Builder toIoOveruseStatsBuilder(
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001234 android.automotive.watchdog.IoOveruseStats internalStats,
1235 int totalTimesKilled, boolean isKillableOnOveruses) {
1236 return new IoOveruseStats.Builder(internalStats.startTime, internalStats.durationInSeconds)
1237 .setTotalOveruses(internalStats.totalOveruses)
1238 .setTotalTimesKilled(totalTimesKilled)
1239 .setTotalBytesWritten(totalPerStateBytes(internalStats.writtenBytes))
1240 .setKillableOnOveruse(isKillableOnOveruses)
1241 .setRemainingWriteBytes(toPerStateBytes(internalStats.remainingWriteBytes));
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001242 }
1243
1244 private static PerStateBytes toPerStateBytes(
1245 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001246 return new PerStateBytes(internalPerStateBytes.foregroundBytes,
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001247 internalPerStateBytes.backgroundBytes, internalPerStateBytes.garageModeBytes);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001248 }
1249
1250 private static long totalPerStateBytes(
1251 android.automotive.watchdog.PerStateBytes internalPerStateBytes) {
1252 BiFunction<Long, Long, Long> sum = (l, r) -> {
1253 return (Long.MAX_VALUE - l > r) ? l + r : Long.MAX_VALUE;
1254 };
1255 return sum.apply(sum.apply(internalPerStateBytes.foregroundBytes,
1256 internalPerStateBytes.backgroundBytes), internalPerStateBytes.garageModeBytes);
1257 }
1258
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001259 private static long getMinimumBytesWritten(
1260 @CarWatchdogManager.MinimumStatsFlag int minimumStatsIoFlag) {
1261 switch (minimumStatsIoFlag) {
1262 case 0:
1263 return 0;
1264 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB:
1265 return 1024 * 1024;
1266 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB:
1267 return 100 * 1024 * 1024;
1268 case CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_GB:
1269 return 1024 * 1024 * 1024;
1270 default:
1271 throw new IllegalArgumentException(
1272 "Must provide valid minimum stats flag for I/O resource");
1273 }
1274 }
1275
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001276 private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
1277 toInternalResourceOveruseConfiguration(ResourceOveruseConfiguration config,
1278 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1279 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig =
1280 new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
1281 internalConfig.componentType = config.getComponentType();
1282 internalConfig.safeToKillPackages = config.getSafeToKillPackages();
1283 internalConfig.vendorPackagePrefixes = config.getVendorPackagePrefixes();
1284 internalConfig.packageMetadata = new ArrayList<>();
1285 for (Map.Entry<String, String> entry : config.getPackagesToAppCategoryTypes().entrySet()) {
1286 if (entry.getKey().isEmpty()) {
1287 continue;
1288 }
1289 PackageMetadata metadata = new PackageMetadata();
1290 metadata.packageName = entry.getKey();
1291 switch(entry.getValue()) {
1292 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1293 metadata.appCategoryType = ApplicationCategoryType.MAPS;
1294 break;
1295 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1296 metadata.appCategoryType = ApplicationCategoryType.MEDIA;
1297 break;
1298 default:
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001299 Slogf.i(TAG, "Invalid application category type: %s skipping package: %s",
1300 entry.getValue(), metadata.packageName);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001301 continue;
1302 }
1303 internalConfig.packageMetadata.add(metadata);
1304 }
1305 internalConfig.resourceSpecificConfigurations = new ArrayList<>();
1306 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1307 && config.getIoOveruseConfiguration() != null) {
1308 internalConfig.resourceSpecificConfigurations.add(
1309 toResourceSpecificConfiguration(config.getComponentType(),
1310 config.getIoOveruseConfiguration()));
1311 }
1312 return internalConfig;
1313 }
1314
1315 private static ResourceSpecificConfiguration
1316 toResourceSpecificConfiguration(int componentType, IoOveruseConfiguration config) {
1317 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig =
1318 new android.automotive.watchdog.internal.IoOveruseConfiguration();
1319 internalConfig.componentLevelThresholds = toPerStateIoOveruseThreshold(
1320 toComponentTypeStr(componentType), config.getComponentLevelThresholds());
1321 internalConfig.packageSpecificThresholds = toPerStateIoOveruseThresholds(
1322 config.getPackageSpecificThresholds());
1323 internalConfig.categorySpecificThresholds = toPerStateIoOveruseThresholds(
1324 config.getAppCategorySpecificThresholds());
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001325 for (int i = 0; i < internalConfig.categorySpecificThresholds.size(); ++i) {
1326 PerStateIoOveruseThreshold threshold = internalConfig.categorySpecificThresholds.get(i);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001327 switch(threshold.name) {
1328 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS:
1329 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS;
1330 break;
1331 case ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA:
1332 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA;
1333 break;
1334 default:
1335 threshold.name = INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN;
1336 }
1337 }
1338 internalConfig.systemWideThresholds = toInternalIoOveruseAlertThresholds(
1339 config.getSystemWideThresholds());
1340
1341 ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
1342 resourceSpecificConfig.setIoOveruseConfiguration(internalConfig);
1343 return resourceSpecificConfig;
1344 }
1345
1346 @VisibleForTesting
1347 static String toComponentTypeStr(int componentType) {
1348 switch(componentType) {
1349 case ComponentType.SYSTEM:
1350 return "SYSTEM";
1351 case ComponentType.VENDOR:
1352 return "VENDOR";
1353 case ComponentType.THIRD_PARTY:
1354 return "THIRD_PARTY";
1355 default:
1356 return "UNKNOWN";
1357 }
1358 }
1359
1360 private static List<PerStateIoOveruseThreshold> toPerStateIoOveruseThresholds(
1361 Map<String, PerStateBytes> thresholds) {
1362 List<PerStateIoOveruseThreshold> internalThresholds = new ArrayList<>();
1363 for (Map.Entry<String, PerStateBytes> entry : thresholds.entrySet()) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001364 if (!thresholds.isEmpty()) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001365 internalThresholds.add(toPerStateIoOveruseThreshold(entry.getKey(),
1366 entry.getValue()));
1367 }
1368 }
1369 return internalThresholds;
1370 }
1371
1372 private static PerStateIoOveruseThreshold toPerStateIoOveruseThreshold(String name,
1373 PerStateBytes perStateBytes) {
1374 PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
1375 threshold.name = name;
1376 threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
1377 threshold.perStateWriteBytes.foregroundBytes = perStateBytes.getForegroundModeBytes();
1378 threshold.perStateWriteBytes.backgroundBytes = perStateBytes.getBackgroundModeBytes();
1379 threshold.perStateWriteBytes.garageModeBytes = perStateBytes.getGarageModeBytes();
1380 return threshold;
1381 }
1382
1383 private static List<android.automotive.watchdog.internal.IoOveruseAlertThreshold>
1384 toInternalIoOveruseAlertThresholds(List<IoOveruseAlertThreshold> thresholds) {
1385 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds =
1386 new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001387 for (int i = 0; i < thresholds.size(); ++i) {
1388 if (thresholds.get(i).getDurationInSeconds() == 0
1389 || thresholds.get(i).getWrittenBytesPerSecond() == 0) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001390 continue;
1391 }
1392 android.automotive.watchdog.internal.IoOveruseAlertThreshold internalThreshold =
1393 new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001394 internalThreshold.durationInSeconds = thresholds.get(i).getDurationInSeconds();
1395 internalThreshold.writtenBytesPerSecond = thresholds.get(i).getWrittenBytesPerSecond();
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001396 internalThresholds.add(internalThreshold);
1397 }
1398 return internalThresholds;
1399 }
1400
1401 private static ResourceOveruseConfiguration toResourceOveruseConfiguration(
1402 android.automotive.watchdog.internal.ResourceOveruseConfiguration internalConfig,
1403 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001404 ArrayMap<String, String> packagesToAppCategoryTypes = new ArrayMap<>();
1405 for (int i = 0; i < internalConfig.packageMetadata.size(); ++i) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001406 String categoryTypeStr;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001407 switch (internalConfig.packageMetadata.get(i).appCategoryType) {
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001408 case ApplicationCategoryType.MAPS:
1409 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS;
1410 break;
1411 case ApplicationCategoryType.MEDIA:
1412 categoryTypeStr = ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA;
1413 break;
1414 default:
1415 continue;
1416 }
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001417 packagesToAppCategoryTypes.put(
1418 internalConfig.packageMetadata.get(i).packageName, categoryTypeStr);
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001419 }
1420 ResourceOveruseConfiguration.Builder configBuilder =
1421 new ResourceOveruseConfiguration.Builder(
1422 internalConfig.componentType,
1423 internalConfig.safeToKillPackages,
1424 internalConfig.vendorPackagePrefixes,
1425 packagesToAppCategoryTypes);
1426 for (ResourceSpecificConfiguration resourceSpecificConfig :
1427 internalConfig.resourceSpecificConfigurations) {
1428 if (resourceSpecificConfig.getTag()
1429 == ResourceSpecificConfiguration.ioOveruseConfiguration
1430 && (resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0) {
1431 configBuilder.setIoOveruseConfiguration(toIoOveruseConfiguration(
1432 resourceSpecificConfig.getIoOveruseConfiguration()));
1433 }
1434 }
1435 return configBuilder.build();
1436 }
1437
1438 private static IoOveruseConfiguration toIoOveruseConfiguration(
1439 android.automotive.watchdog.internal.IoOveruseConfiguration internalConfig) {
1440 PerStateBytes componentLevelThresholds =
1441 toPerStateBytes(internalConfig.componentLevelThresholds.perStateWriteBytes);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001442 ArrayMap<String, PerStateBytes> packageSpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001443 toPerStateBytesMap(internalConfig.packageSpecificThresholds);
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001444 ArrayMap<String, PerStateBytes> appCategorySpecificThresholds =
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001445 toPerStateBytesMap(internalConfig.categorySpecificThresholds);
Lakshman Annadoraia55ac6d2021-09-27 11:52:09 -07001446 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001447 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS);
Lakshman Annadoraia55ac6d2021-09-27 11:52:09 -07001448 replaceKey(appCategorySpecificThresholds, INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001449 ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA);
1450 List<IoOveruseAlertThreshold> systemWideThresholds =
1451 toIoOveruseAlertThresholds(internalConfig.systemWideThresholds);
1452
1453 IoOveruseConfiguration.Builder configBuilder = new IoOveruseConfiguration.Builder(
1454 componentLevelThresholds, packageSpecificThresholds, appCategorySpecificThresholds,
1455 systemWideThresholds);
1456 return configBuilder.build();
1457 }
1458
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001459 private static ArrayMap<String, PerStateBytes> toPerStateBytesMap(
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001460 List<PerStateIoOveruseThreshold> thresholds) {
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001461 ArrayMap<String, PerStateBytes> thresholdsMap = new ArrayMap<>();
1462 for (int i = 0; i < thresholds.size(); ++i) {
1463 thresholdsMap.put(
1464 thresholds.get(i).name, toPerStateBytes(thresholds.get(i).perStateWriteBytes));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001465 }
1466 return thresholdsMap;
1467 }
1468
1469 private static List<IoOveruseAlertThreshold> toIoOveruseAlertThresholds(
1470 List<android.automotive.watchdog.internal.IoOveruseAlertThreshold> internalThresholds) {
1471 List<IoOveruseAlertThreshold> thresholds = new ArrayList<>();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001472 for (int i = 0; i < internalThresholds.size(); ++i) {
1473 thresholds.add(new IoOveruseAlertThreshold(internalThresholds.get(i).durationInSeconds,
1474 internalThresholds.get(i).writtenBytesPerSecond));
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001475 }
1476 return thresholds;
1477 }
1478
Jahdiel Alvarez61481142021-07-31 00:13:19 +00001479 private static void checkResourceOveruseConfigs(
1480 List<ResourceOveruseConfiguration> configurations,
1481 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1482 ArraySet<Integer> seenComponentTypes = new ArraySet<>();
1483 for (int i = 0; i < configurations.size(); ++i) {
1484 ResourceOveruseConfiguration config = configurations.get(i);
1485 if (seenComponentTypes.contains(config.getComponentType())) {
1486 throw new IllegalArgumentException(
1487 "Cannot provide duplicate configurations for the same component type");
1488 }
1489 checkResourceOveruseConfig(config, resourceOveruseFlag);
1490 seenComponentTypes.add(config.getComponentType());
1491 }
1492 }
1493
1494 private static void checkResourceOveruseConfig(ResourceOveruseConfiguration config,
1495 @CarWatchdogManager.ResourceOveruseFlag int resourceOveruseFlag) {
1496 int componentType = config.getComponentType();
1497 if (toComponentTypeStr(componentType).equals("UNKNOWN")) {
1498 throw new IllegalArgumentException(
1499 "Invalid component type in the configuration: " + componentType);
1500 }
1501 if ((resourceOveruseFlag & FLAG_RESOURCE_OVERUSE_IO) != 0
1502 && config.getIoOveruseConfiguration() == null) {
1503 throw new IllegalArgumentException("Must provide I/O overuse configuration");
1504 }
1505 checkIoOveruseConfig(config.getIoOveruseConfiguration(), componentType);
1506 }
1507
1508 private static void checkIoOveruseConfig(IoOveruseConfiguration config, int componentType) {
1509 if (config.getComponentLevelThresholds().getBackgroundModeBytes() <= 0
1510 || config.getComponentLevelThresholds().getForegroundModeBytes() <= 0
1511 || config.getComponentLevelThresholds().getGarageModeBytes() <= 0) {
1512 throw new IllegalArgumentException(
1513 "For component: " + toComponentTypeStr(componentType)
1514 + " some thresholds are zero for: "
1515 + config.getComponentLevelThresholds().toString());
1516 }
1517 if (componentType == ComponentType.SYSTEM) {
1518 List<IoOveruseAlertThreshold> systemThresholds = config.getSystemWideThresholds();
1519 if (systemThresholds.isEmpty()) {
1520 throw new IllegalArgumentException(
1521 "Empty system-wide alert thresholds provided in "
1522 + toComponentTypeStr(componentType)
1523 + " config.");
1524 }
1525 for (int i = 0; i < systemThresholds.size(); i++) {
1526 checkIoOveruseAlertThreshold(systemThresholds.get(i));
1527 }
1528 }
1529 }
1530
1531 private static void checkIoOveruseAlertThreshold(
1532 IoOveruseAlertThreshold ioOveruseAlertThreshold) {
1533 if (ioOveruseAlertThreshold.getDurationInSeconds() <= 0) {
1534 throw new IllegalArgumentException(
1535 "System wide threshold duration must be greater than zero for: "
1536 + ioOveruseAlertThreshold);
1537 }
1538 if (ioOveruseAlertThreshold.getWrittenBytesPerSecond() <= 0) {
1539 throw new IllegalArgumentException(
1540 "System wide threshold written bytes per second must be greater than zero for: "
1541 + ioOveruseAlertThreshold);
1542 }
1543 }
1544
Lakshman Annadoraie1720472021-04-13 15:22:57 -07001545 private static void replaceKey(Map<String, PerStateBytes> map, String oldKey, String newKey) {
1546 PerStateBytes perStateBytes = map.get(oldKey);
1547 if (perStateBytes != null) {
1548 map.put(newKey, perStateBytes);
1549 map.remove(oldKey);
1550 }
1551 }
1552
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001553 private static int toNumDays(@CarWatchdogManager.StatsPeriod int maxStatsPeriod) {
1554 switch(maxStatsPeriod) {
1555 case STATS_PERIOD_CURRENT_DAY:
1556 return 0;
1557 case STATS_PERIOD_PAST_3_DAYS:
1558 return 3;
1559 case STATS_PERIOD_PAST_7_DAYS:
1560 return 7;
1561 case STATS_PERIOD_PAST_15_DAYS:
1562 return 15;
1563 case STATS_PERIOD_PAST_30_DAYS:
1564 return 30;
1565 default:
1566 throw new IllegalArgumentException(
1567 "Invalid max stats period provided: " + maxStatsPeriod);
1568 }
1569 }
1570
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001571 private final class PackageResourceUsage {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001572 public final String genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001573 public @UserIdInt final int userId;
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001574 @GuardedBy("mLock")
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001575 public final PackageIoUsage ioUsage = new PackageIoUsage();
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001576 @GuardedBy("mLock")
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001577 private @KillableState int mKillableState;
1578
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001579 /** Must be called only after acquiring {@link mLock} */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001580 PackageResourceUsage(@UserIdInt int userId, String genericPackageName) {
1581 this.genericPackageName = genericPackageName;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001582 this.userId = userId;
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001583 this.mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001584 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001585 }
1586
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001587 public boolean isSharedPackage() {
1588 return this.genericPackageName.startsWith(SHARED_PACKAGE_PREFIX);
1589 }
1590
1591 @GuardedBy("mLock")
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001592 public void updateLocked(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001593 if (!internalStats.killableOnOveruse) {
1594 /*
1595 * Killable value specified in the internal stats is provided by the native daemon.
1596 * This value reflects whether or not an application is safe-to-kill on overuse.
1597 * This setting is from the I/O overuse configuration specified by the system and
1598 * vendor services and doesn't reflect the user choices. Thus if the internal stats
1599 * specify the application is not killable, the application is not safe-to-kill.
1600 */
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001601 mKillableState = KILLABLE_STATE_NEVER;
1602 } else if (mKillableState == KILLABLE_STATE_NEVER) {
1603 /*
1604 * This case happens when a previously unsafe to kill system/vendor package was
1605 * recently marked as safe-to-kill so update the old state to the default value.
1606 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001607 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001608 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001609 }
1610 ioUsage.update(internalStats);
1611 }
1612
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001613 public ResourceOveruseStats.Builder getResourceOveruseStatsBuilder() {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001614 return new ResourceOveruseStats.Builder(genericPackageName, UserHandle.of(userId));
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001615 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001616
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001617 @GuardedBy("mLock")
1618 public IoOveruseStats getIoOveruseStatsLocked() {
Lakshman Annadoraif5c1d142021-04-15 12:51:46 -07001619 if (!ioUsage.hasUsage()) {
1620 return null;
1621 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001622 return ioUsage.getIoOveruseStats(mKillableState != KILLABLE_STATE_NEVER);
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001623 }
1624
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001625 @GuardedBy("mLock")
1626 public @KillableState int getKillableStateLocked() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001627 return mKillableState;
1628 }
1629
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001630 @GuardedBy("mLock")
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001631 public void setKillableStateLocked(@KillableState int killableState) {
1632 mKillableState = killableState;
1633 }
1634
1635 @GuardedBy("mLock")
1636 public boolean verifyAndSetKillableStateLocked(boolean isKillable) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001637 if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001638 return false;
1639 }
1640 mKillableState = isKillable ? KILLABLE_STATE_YES : KILLABLE_STATE_NO;
1641 return true;
1642 }
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001643
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001644 @GuardedBy("mLock")
1645 public int syncAndFetchKillableStateLocked(int myComponentType, boolean isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001646 /*
1647 * The killable state goes out-of-sync:
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001648 * 1. When the on-device safe-to-kill list was recently updated and the user package
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001649 * didn't have any resource usage so the native daemon didn't update the killable state.
1650 * 2. When a package has no resource usage and is initialized outside of processing the
1651 * latest resource usage stats.
1652 */
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001653 if (myComponentType != ComponentType.THIRD_PARTY && !isSafeToKill) {
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001654 mKillableState = KILLABLE_STATE_NEVER;
1655 } else if (mKillableState == KILLABLE_STATE_NEVER) {
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001656 mKillableState = mDefaultNotKillableGenericPackages.contains(genericPackageName)
Lakshman Annadorai5cdf80c2021-04-14 13:07:55 -07001657 ? KILLABLE_STATE_NO : KILLABLE_STATE_YES;
1658 }
1659 return mKillableState;
1660 }
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001661
Lakshman Annadorai36ec22b2021-07-14 16:51:40 -07001662 @GuardedBy("mLock")
1663 public void resetStatsLocked() {
Lakshman Annadoraiec4d08a2021-04-30 08:29:10 -07001664 ioUsage.resetStats();
1665 }
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001666 }
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001667 /** Defines I/O usage fields for a package. */
1668 public static final class PackageIoUsage {
Jahdiel Alvarezc209a322021-08-31 23:52:43 +00001669 private static final android.automotive.watchdog.PerStateBytes DEFAULT_PER_STATE_BYTES =
1670 new android.automotive.watchdog.PerStateBytes();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001671 private android.automotive.watchdog.IoOveruseStats mIoOveruseStats;
1672 private android.automotive.watchdog.PerStateBytes mForgivenWriteBytes;
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001673 private int mTotalTimesKilled;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001674
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001675 private PackageIoUsage() {
Jahdiel Alvarezc209a322021-08-31 23:52:43 +00001676 mForgivenWriteBytes = DEFAULT_PER_STATE_BYTES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001677 mTotalTimesKilled = 0;
1678 }
1679
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001680 public PackageIoUsage(android.automotive.watchdog.IoOveruseStats ioOveruseStats,
1681 android.automotive.watchdog.PerStateBytes forgivenWriteBytes,
1682 int totalTimesKilled) {
1683 mIoOveruseStats = ioOveruseStats;
1684 mForgivenWriteBytes = forgivenWriteBytes;
1685 mTotalTimesKilled = totalTimesKilled;
1686 }
1687
1688 public android.automotive.watchdog.IoOveruseStats getInternalIoOveruseStats() {
1689 return mIoOveruseStats;
1690 }
1691
1692 public android.automotive.watchdog.PerStateBytes getForgivenWriteBytes() {
1693 return mForgivenWriteBytes;
1694 }
1695
1696 public int getTotalTimesKilled() {
1697 return mTotalTimesKilled;
1698 }
1699
1700 boolean hasUsage() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001701 return mIoOveruseStats != null;
1702 }
1703
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001704 void overwrite(PackageIoUsage ioUsage) {
1705 mIoOveruseStats = ioUsage.mIoOveruseStats;
1706 mForgivenWriteBytes = ioUsage.mForgivenWriteBytes;
1707 mTotalTimesKilled = ioUsage.mTotalTimesKilled;
1708 }
1709
1710 void update(android.automotive.watchdog.IoOveruseStats internalStats) {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001711 mIoOveruseStats = internalStats;
1712 if (exceedsThreshold()) {
1713 /*
1714 * Forgive written bytes on overuse as the package is either forgiven or killed on
1715 * overuse. When the package is killed, the user may opt to open the corresponding
1716 * app and the package should be forgiven anyways.
1717 * NOTE: If this logic is updated, update the daemon side logic as well.
1718 */
1719 mForgivenWriteBytes = internalStats.writtenBytes;
1720 }
1721 }
1722
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001723 IoOveruseStats getIoOveruseStats(boolean isKillable) {
1724 return toIoOveruseStatsBuilder(mIoOveruseStats, mTotalTimesKilled, isKillable).build();
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001725 }
1726
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001727 boolean exceedsThreshold() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001728 if (!hasUsage()) {
1729 return false;
1730 }
1731 android.automotive.watchdog.PerStateBytes remaining =
1732 mIoOveruseStats.remainingWriteBytes;
1733 return remaining.foregroundBytes == 0 || remaining.backgroundBytes == 0
1734 || remaining.garageModeBytes == 0;
1735 }
1736
Lakshman Annadorai0d3b1992021-08-17 09:11:24 -07001737 void killed() {
1738 ++mTotalTimesKilled;
1739 }
1740
1741 void resetStats() {
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001742 mIoOveruseStats = null;
Jahdiel Alvarezc209a322021-08-31 23:52:43 +00001743 mForgivenWriteBytes = DEFAULT_PER_STATE_BYTES;
Lakshman Annadoraicf5f3a92021-04-02 15:26:16 -07001744 mTotalTimesKilled = 0;
1745 }
1746 }
1747
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001748 private final class ResourceOveruseListenerInfo implements IBinder.DeathRecipient {
1749 public final IResourceOveruseListener listener;
1750 public final @CarWatchdogManager.ResourceOveruseFlag int flag;
1751 public final int pid;
1752 public final int uid;
1753 public final boolean isListenerForSystem;
1754
1755 ResourceOveruseListenerInfo(IResourceOveruseListener listener,
1756 @CarWatchdogManager.ResourceOveruseFlag int flag, int pid, int uid,
1757 boolean isListenerForSystem) {
1758 this.listener = listener;
1759 this.flag = flag;
1760 this.pid = pid;
1761 this.uid = uid;
1762 this.isListenerForSystem = isListenerForSystem;
1763 }
1764
1765 @Override
1766 public void binderDied() {
Lakshman Annadorai94323ff2021-04-29 12:14:34 -07001767 Slogf.w(TAG, "Resource overuse listener%s (pid: %d) died",
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001768 isListenerForSystem ? " for system" : "", pid);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001769 Consumer<SparseArray<ArrayList<ResourceOveruseListenerInfo>>> removeListenerInfo =
1770 listenerInfosByUid -> {
1771 ArrayList<ResourceOveruseListenerInfo> listenerInfos =
1772 listenerInfosByUid.get(uid);
1773 if (listenerInfos == null) {
1774 return;
1775 }
1776 listenerInfos.remove(this);
1777 if (listenerInfos.isEmpty()) {
1778 listenerInfosByUid.remove(uid);
1779 }
1780 };
1781 if (isListenerForSystem) {
1782 removeListenerInfo.accept(mOveruseSystemListenerInfosByUid);
1783 } else {
1784 removeListenerInfo.accept(mOveruseListenerInfosByUid);
1785 }
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001786 unlinkToDeath();
1787 }
1788
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001789 public void notifyListener(@CarWatchdogManager.ResourceOveruseFlag int resourceType,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001790 int overusingUid, String overusingGenericPackageName,
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001791 ResourceOveruseStats resourceOveruseStats) {
1792 if ((flag & resourceType) == 0) {
1793 return;
1794 }
1795 try {
1796 listener.onOveruse(resourceOveruseStats);
1797 } catch (RemoteException e) {
1798 Slogf.e(TAG, "Failed to notify %s (uid %d, pid: %d) of resource overuse by "
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001799 + "package(uid %d, generic package name '%s'): %s",
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001800 (isListenerForSystem ? "system listener" : "listener"), uid, pid,
Lakshman Annadorai7ecdace2021-07-08 17:05:31 -07001801 overusingUid, overusingGenericPackageName, e);
Lakshman Annadorai16999d22021-05-25 08:33:30 -07001802 }
1803 }
1804
Lakshman Annadorai016127e2021-03-18 09:11:43 -07001805 private void linkToDeath() throws RemoteException {
1806 listener.asBinder().linkToDeath(this, 0);
1807 }
1808
1809 private void unlinkToDeath() {
1810 listener.asBinder().unlinkToDeath(this, 0);
1811 }
1812 }
1813}