blob: a00da46e0194e36aa3dfe63c15505ee1f51c168b [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.watchdog;
import static android.app.StatsManager.PULL_SKIP;
import static android.app.StatsManager.PULL_SUCCESS;
import static android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_BASELINE;
import static android.car.settings.CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE;
import static android.car.test.mocks.AndroidMockitoHelper.mockAmGetCurrentUser;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetAllUsers;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserHandles;
import static android.car.watchdog.CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO;
import static android.car.watchdog.CarWatchdogManager.RETURN_CODE_SUCCESS;
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__KILL_REASON__KILLED_ON_IO_OVERUSE;
import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__GARAGE_MODE;
import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE;
import static com.android.car.CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED__UID_STATE__UNKNOWN_UID_STATE;
import static com.android.car.CarStatsLog.CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY;
import static com.android.car.CarStatsLog.CAR_WATCHDOG_UID_IO_USAGE_SUMMARY;
import static com.android.car.internal.NotificationHelperBase.RESOURCE_OVERUSE_NOTIFICATION_BASE_ID;
import static com.android.car.internal.NotificationHelperBase.RESOURCE_OVERUSE_NOTIFICATION_MAX_OFFSET;
import static com.android.car.watchdog.TimeSource.ZONE_OFFSET;
import static com.android.car.watchdog.WatchdogPerfHandler.MAX_WAIT_TIME_MILLS;
import static com.android.car.watchdog.WatchdogPerfHandler.PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR;
import static com.android.car.watchdog.WatchdogPerfHandler.USER_PACKAGE_SEPARATOR;
import static com.android.car.watchdog.WatchdogStorage.RETENTION_PERIOD;
import static com.android.car.watchdog.WatchdogStorage.WatchdogDbHelper.DATABASE_NAME;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.AdditionalMatchers.or;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.StatsManager;
import android.automotive.watchdog.internal.ApplicationCategoryType;
import android.automotive.watchdog.internal.ComponentType;
import android.automotive.watchdog.internal.GarageMode;
import android.automotive.watchdog.internal.IoUsageStats;
import android.automotive.watchdog.internal.PackageIoOveruseStats;
import android.automotive.watchdog.internal.PackageMetadata;
import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
import android.automotive.watchdog.internal.UserPackageIoUsageStats;
import android.car.builtin.content.pm.PackageManagerHelper;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.ICarUxRestrictionsChangeListener;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
import android.car.test.mocks.MockSettings;
import android.car.watchdog.CarWatchdogManager;
import android.car.watchdog.IResourceOveruseListener;
import android.car.watchdog.IoOveruseAlertThreshold;
import android.car.watchdog.IoOveruseConfiguration;
import android.car.watchdog.IoOveruseStats;
import android.car.watchdog.PackageKillableState;
import android.car.watchdog.PerStateBytes;
import android.car.watchdog.ResourceOveruseConfiguration;
import android.car.watchdog.ResourceOveruseStats;
import android.car.watchdoglib.CarWatchdogDaemonHelper;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.StatsEvent;
import android.view.Display;
import com.android.car.BuiltinPackageDependency;
import com.android.car.CarLocalServices;
import com.android.car.CarServiceUtils;
import com.android.car.CarStatsLog;
import com.android.car.CarUxRestrictionsManagerService;
import com.android.car.admin.NotificationHelper;
import com.google.common.truth.Correspondence;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import java.io.File;
import java.nio.file.Files;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
public class WatchdogPerfHandlerUnitTest extends AbstractExtendedMockitoTestCase {
private static final int UID_IO_USAGE_SUMMARY_TOP_COUNT = 3;
private static final int RECURRING_OVERUSE_TIMES = 2;
private static final int RECURRING_OVERUSE_PERIOD_IN_DAYS = 2;
private static final int IO_USAGE_SUMMARY_MIN_SYSTEM_TOTAL_WRITTEN_BYTES = 500 * 1024 * 1024;
private static final long STATS_DURATION_SECONDS = 3 * 60 * 60;
private static final long OVERUSE_HANDLING_DELAY_MILLS = 1000;
private static final int PACKAGE_KILLABLE_STATE_RESET_DAYS = 90;
private static final long SYSTEM_DAILY_IO_USAGE_SUMMARY_MULTIPLIER = 10_000;
private static final int CURRENT_USER_ID = 100;
private static final String WATCHDOG_DIR_NAME = "watchdog";
private static final String CANONICAL_NAME =
WatchdogPerfHandlerUnitTest.class.getCanonicalName();
private static final String CAR_WATCHDOG_SERVICE_NAME =
CarWatchdogService.class.getSimpleName();
@Mock
private Context mMockContext;
@Mock
private Context mMockBuiltinPackageContext;
@Mock
private CarWatchdogDaemonHelper mMockCarWatchdogDaemonHelper;
@Mock
private CarUxRestrictionsManagerService mMockCarUxRestrictionsManagerService;
@Mock
private Resources mMockResources;
@Mock
private PackageManager mMockPackageManager;
@Mock private ClassLoader mMockClassLoader;
@Mock
private UserManager mMockUserManager;
@Mock
private StatsManager mMockStatsManager;
@Mock
private NotificationHelper mMockNotificationHelper;
@Captor
private ArgumentCaptor<ICarUxRestrictionsChangeListener>
mICarUxRestrictionsChangeListenerCaptor;
@Captor
private ArgumentCaptor<StatsManager.StatsPullAtomCallback> mStatsPullAtomCallbackCaptor;
@Captor
private ArgumentCaptor<List<
android.automotive.watchdog.internal.ResourceOveruseConfiguration>>
mResourceOveruseConfigurationsCaptor;
@Captor private ArgumentCaptor<SparseArray<List<String>>> mPackagesByUserIdCaptor;
@Captor private ArgumentCaptor<byte[]> mOveruseStatsCaptor;
@Captor private ArgumentCaptor<byte[]> mKilledStatsCaptor;
@Captor private ArgumentCaptor<Integer> mOverusingUidCaptor;
@Captor private ArgumentCaptor<Integer> mKilledUidCaptor;
@Captor private ArgumentCaptor<Integer> mUidStateCaptor;
@Captor private ArgumentCaptor<Integer> mSystemStateCaptor;
@Captor private ArgumentCaptor<Integer> mKillReasonCaptor;
@Captor private ArgumentCaptor<UserHandle> mUserHandleCaptor;
@Captor private ArgumentCaptor<SparseArray<String>> mHeadsUpPackagesCaptor;
@Captor private ArgumentCaptor<SparseArray<String>> mNotificationCenterPackagesCaptor;
private ICarUxRestrictionsChangeListener mCarUxRestrictionsChangeListener;
private StatsManager.StatsPullAtomCallback mStatsPullAtomCallback;
private WatchdogStorage mSpiedWatchdogStorage;
private WatchdogPerfHandler mWatchdogPerfHandler;
private File mTempSystemCarDir;
// Not used directly, but sets proper mockStatic() expectations on Settings
@SuppressWarnings("UnusedVariable")
private MockSettings mMockSettings;
private final SparseArray<String> mGenericPackageNameByUid = new SparseArray<>();
private final CarWatchdogServiceUnitTest.TestTimeSource mTimeSource =
new CarWatchdogServiceUnitTest.TestTimeSource();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final SparseArray<List<String>> mPackagesBySharedUid = new SparseArray<>();
private final ArrayMap<String, android.content.pm.PackageInfo> mPmPackageInfoByUserPackage =
new ArrayMap<>();
private final ArraySet<String> mDisabledUserPackages = new ArraySet<>();
private final SparseArray<String> mDisabledPackagesSettingsStringByUserid = new SparseArray<>();
private final IPackageManager mSpiedPackageManager = spy(ActivityThread.getPackageManager());
private final Set<WatchdogStorage.UserPackageSettingsEntry> mUserPackageSettingsEntries =
new ArraySet<>();
private final List<WatchdogStorage.IoUsageStatsEntry> mIoUsageStatsEntries = new ArrayList<>();
private final List<AtomsProto.CarWatchdogSystemIoUsageSummary> mPulledSystemIoUsageSummaries =
new ArrayList<>();
private final List<AtomsProto.CarWatchdogUidIoUsageSummary> mPulledUidIoUsageSummaries =
new ArrayList<>();
public WatchdogPerfHandlerUnitTest() {
super(CarWatchdogService.TAG);
}
@Override
protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
mMockSettings = new MockSettings(builder);
builder.spyStatic(PackageManagerHelper.class)
.spyStatic(CarServiceUtils.class)
.spyStatic(ActivityManager.class)
.spyStatic(ActivityThread.class)
.spyStatic(CarStatsLog.class)
.spyStatic(BuiltinPackageDependency.class)
.spyStatic(CarWatchdogService.class)
.spyStatic(CarLocalServices.class);
}
@Before
public void setUp() throws Exception {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockContext.getSystemService(StatsManager.class)).thenReturn(mMockStatsManager);
when(mMockContext.getPackageName()).thenReturn(CANONICAL_NAME);
when(mMockResources.getInteger(
com.android.car.R.integer.watchdogUserPackageSettingsResetDays))
.thenReturn(PACKAGE_KILLABLE_STATE_RESET_DAYS);
when(mMockResources.getInteger(
com.android.car.R.integer.recurringResourceOverusePeriodInDays))
.thenReturn(RECURRING_OVERUSE_PERIOD_IN_DAYS);
when(mMockResources.getInteger(
com.android.car.R.integer.recurringResourceOveruseTimes))
.thenReturn(RECURRING_OVERUSE_TIMES);
when(mMockResources.getInteger(
com.android.car.R.integer.uidIoUsageSummaryTopCount))
.thenReturn(UID_IO_USAGE_SUMMARY_TOP_COUNT);
when(mMockResources.getInteger(
com.android.car.R.integer.ioUsageSummaryMinSystemTotalWrittenBytes))
.thenReturn(IO_USAGE_SUMMARY_MIN_SYSTEM_TOTAL_WRITTEN_BYTES);
doReturn(mMockCarUxRestrictionsManagerService)
.when(() -> CarLocalServices.getService(CarUxRestrictionsManagerService.class));
doReturn(mSpiedPackageManager).when(() -> ActivityThread.getPackageManager());
when(mMockCarUxRestrictionsManagerService.getCurrentUxRestrictions())
.thenReturn(new CarUxRestrictions.Builder(/* reqOpt= */ false,
UX_RESTRICTIONS_BASELINE, /* time= */ 0).build());
when(mMockCarWatchdogDaemonHelper.getResourceOveruseConfigurations()).thenReturn(
sampleInternalResourceOveruseConfigurations());
when(mMockBuiltinPackageContext.getClassLoader()).thenReturn(mMockClassLoader);
doReturn(NotificationHelper.class).when(mMockClassLoader).loadClass(any());
doReturn(mMockNotificationHelper)
.when(() -> BuiltinPackageDependency.createNotificationHelper(
mMockBuiltinPackageContext));
mTempSystemCarDir = Files.createTempDirectory("watchdog_test").toFile();
doReturn(new File(mTempSystemCarDir.getAbsolutePath(), WATCHDOG_DIR_NAME)).when(
CarWatchdogService::getWatchdogDirFile);
File tempDbFile = new File(mTempSystemCarDir.getPath(), DATABASE_NAME);
when(mMockContext.createDeviceProtectedStorageContext()).thenReturn(mMockContext);
when(mMockContext.getDatabasePath(DATABASE_NAME)).thenReturn(tempDbFile);
mSpiedWatchdogStorage =
spy(new WatchdogStorage(mMockContext, /* useDataSystemCarDir= */ false,
mTimeSource));
mWatchdogPerfHandler = new WatchdogPerfHandler(mMockContext,
mMockBuiltinPackageContext, mMockCarWatchdogDaemonHelper,
new PackageInfoHandler(mMockContext.getPackageManager()),
mSpiedWatchdogStorage, mTimeSource);
setupUsers();
mockSettingsStringCalls();
mockBuildStatsEventCalls();
mockPackageManager();
mockWatchdogStorage();
initService(/* wantedInvocations= */ 1);
}
/**
* Releases resources.
*/
@After
public void tearDown() throws Exception {
if (mTempSystemCarDir != null) {
FileUtils.deleteContentsAndDir(mTempSystemCarDir);
}
}
@Test
public void testAddResourceOveruseListenerThrowsWithInvalidFlag() throws Exception {
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
assertThrows(IllegalArgumentException.class, () -> {
mWatchdogPerfHandler.addResourceOveruseListener(/* resourceOveruseFlag= */ 0,
mockListener);
});
}
//TODO(b/293374687): Add relevant tests for deleteUser, packageIoOveruseStats,
// processUserNotificationIntent, writeMetadataFile, writeToDatabase, release
@Test
public void testResourceOveruseListener() throws Exception {
mGenericPackageNameByUid.put(Binder.getCallingUid(), CANONICAL_NAME);
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
IBinder mockBinder = mockListener.asBinder();
mWatchdogPerfHandler.addResourceOveruseListener(FLAG_RESOURCE_OVERUSE_IO,
mockListener);
verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>(
Collections.singleton(CANONICAL_NAME)));
verify(mockListener).onOveruse(any());
mWatchdogPerfHandler.removeResourceOveruseListener(mockListener);
verify(mockListener, atLeastOnce()).asBinder();
verify(mockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>(
Collections.singletonList(CANONICAL_NAME)));
verifyNoMoreInteractions(mockListener);
}
@Test
public void testDuplicateAddResourceOveruseListener() throws Exception {
mGenericPackageNameByUid.put(Binder.getCallingUid(), CANONICAL_NAME);
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
IBinder mockBinder = mockListener.asBinder();
mWatchdogPerfHandler.addResourceOveruseListener(FLAG_RESOURCE_OVERUSE_IO,
mockListener);
assertThrows(IllegalStateException.class,
() -> mWatchdogPerfHandler.addResourceOveruseListener(
FLAG_RESOURCE_OVERUSE_IO, mockListener));
verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
mWatchdogPerfHandler.removeResourceOveruseListener(mockListener);
verify(mockListener, atLeastOnce()).asBinder();
verify(mockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
verifyNoMoreInteractions(mockListener);
}
@Test
public void testAddMultipleResourceOveruseListeners() throws Exception {
mGenericPackageNameByUid.put(Binder.getCallingUid(), CANONICAL_NAME);
IResourceOveruseListener firstMockListener = createMockResourceOveruseListener();
IBinder firstMockBinder = firstMockListener.asBinder();
IResourceOveruseListener secondMockListener = createMockResourceOveruseListener();
IBinder secondMockBinder = secondMockListener.asBinder();
mWatchdogPerfHandler.addResourceOveruseListener(FLAG_RESOURCE_OVERUSE_IO,
firstMockListener);
mWatchdogPerfHandler.addResourceOveruseListener(FLAG_RESOURCE_OVERUSE_IO,
secondMockListener);
verify(firstMockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
verify(secondMockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>(
Collections.singleton(CANONICAL_NAME)));
verify(firstMockListener).onOveruse(any());
mWatchdogPerfHandler.removeResourceOveruseListener(firstMockListener);
verify(firstMockListener, atLeastOnce()).asBinder();
verify(firstMockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>(
Collections.singletonList(CANONICAL_NAME)));
verify(secondMockListener, times(2)).onOveruse(any());
mWatchdogPerfHandler.removeResourceOveruseListener(secondMockListener);
verify(secondMockListener, atLeastOnce()).asBinder();
verify(secondMockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>(
Collections.singletonList(CANONICAL_NAME)));
verifyNoMoreInteractions(firstMockListener);
verifyNoMoreInteractions(secondMockListener);
}
@Test
public void testAddResourceOveruseListenerForSystemThrowsWithInvalidFlag() throws Exception {
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
assertThrows(IllegalArgumentException.class, () -> {
mWatchdogPerfHandler.addResourceOveruseListenerForSystem(/* resourceOveruseFlag= */ 0,
mockListener);
});
}
@Test
public void testResourceOveruseListenerForSystem() throws Exception {
int callingUid = Binder.getCallingUid();
mGenericPackageNameByUid.put(callingUid, "system_package.critical");
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
mWatchdogPerfHandler.addResourceOveruseListenerForSystem(FLAG_RESOURCE_OVERUSE_IO,
mockListener);
IBinder mockBinder = mockListener.asBinder();
verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
List<PackageIoOveruseStats> packageIoOveruseStats = Collections.singletonList(
constructPackageIoOveruseStats(callingUid, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(/* fgBytes= */ 80,
/* bgBytes= */ 170, /* gmBytes= */ 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(/* fgBytes= */ 20,
/* bgBytes= */ 20, /* gmBytes= */ 20),
/* writtenBytes= */ constructPerStateBytes(/* fgBytes= */ 100,
/* bgBytes= */ 200, /* gmBytes= */ 300),
/* totalOveruses= */ 3)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verify(mockListener).onOveruse(any());
mWatchdogPerfHandler.removeResourceOveruseListenerForSystem(mockListener);
verify(mockListener, atLeastOnce()).asBinder();
verify(mockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verifyNoMoreInteractions(mockListener);
}
@Test
public void testSetResourceOveruseConfigurations() throws Exception {
assertThat(mWatchdogPerfHandler.setResourceOveruseConfigurations(
sampleResourceOveruseConfigurations(), FLAG_RESOURCE_OVERUSE_IO))
.isEqualTo(RETURN_CODE_SUCCESS);
InternalResourceOveruseConfigurationSubject
.assertThat(captureOnSetResourceOveruseConfigurations(/* wantedInvocations= */ 1))
.containsExactlyElementsIn(sampleInternalResourceOveruseConfigurations());
// CarService fetches and syncs resource overuse configuration on the main thread by posting
// a new message. Wait until this completes.
CarServiceUtils.runOnMainSync(() -> {});
/* Expect two calls, the first is made at car watchdog service init */
verify(mMockCarWatchdogDaemonHelper, times(2)).getResourceOveruseConfigurations();
}
@Test
public void testSetResourceOveruseConfigurationsRetriedWithRepeatedRemoteException()
throws Exception {
doThrow(RemoteException.class)
.when(mMockCarWatchdogDaemonHelper).updateResourceOveruseConfigurations(anyList());
assertThat(mWatchdogPerfHandler.setResourceOveruseConfigurations(
sampleResourceOveruseConfigurations(), FLAG_RESOURCE_OVERUSE_IO))
.isEqualTo(CarWatchdogManager.RETURN_CODE_SUCCESS);
mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ true);
doNothing().when(mMockCarWatchdogDaemonHelper).updateResourceOveruseConfigurations(
anyList());
/* The below final restart should set the resource overuse configurations successfully. */
mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ true);
InternalResourceOveruseConfigurationSubject
.assertThat(captureOnSetResourceOveruseConfigurations(/* wantedInvocations= */ 3))
.containsExactlyElementsIn(sampleInternalResourceOveruseConfigurations());
verify(mMockCarWatchdogDaemonHelper, times(3)).updateResourceOveruseConfigurations(
anyList());
}
@Test
public void testSetResourceOveruseConfigurationsRetriedWithDisconnectedDaemon()
throws Exception {
mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ false);
assertThat(mWatchdogPerfHandler.setResourceOveruseConfigurations(
sampleResourceOveruseConfigurations(), FLAG_RESOURCE_OVERUSE_IO))
.isEqualTo(RETURN_CODE_SUCCESS);
mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ true);
InternalResourceOveruseConfigurationSubject
.assertThat(captureOnSetResourceOveruseConfigurations(/* wantedInvocations= */ 1))
.containsExactlyElementsIn(sampleInternalResourceOveruseConfigurations());
}
@Test
public void testFailsSetResourceOveruseConfigurationsWithPendingRequest()
throws Exception {
mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ false);
assertThat(mWatchdogPerfHandler.setResourceOveruseConfigurations(
sampleResourceOveruseConfigurations(), FLAG_RESOURCE_OVERUSE_IO))
.isEqualTo(RETURN_CODE_SUCCESS);
assertThrows(IllegalStateException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(
sampleResourceOveruseConfigurations(), FLAG_RESOURCE_OVERUSE_IO));
}
@Test
public void testFailsSetResourceOveruseConfigurationsOnInvalidArgs() throws Exception {
assertThrows(NullPointerException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(
/* configurations= */ null, FLAG_RESOURCE_OVERUSE_IO));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(
/* configurations= */ new ArrayList<>(), FLAG_RESOURCE_OVERUSE_IO));
List<ResourceOveruseConfiguration> resourceOveruseConfigs = Collections.singletonList(
sampleResourceOveruseConfigurationBuilder(ComponentType.SYSTEM,
sampleIoOveruseConfigurationBuilder(ComponentType.SYSTEM).build())
.build());
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(resourceOveruseConfigs,
/* resourceOveruseFlag= */ 0));
}
@Test
public void testFailsSetResourceOveruseConfigurationsOnDuplicateComponents() throws Exception {
ResourceOveruseConfiguration config =
sampleResourceOveruseConfigurationBuilder(ComponentType.SYSTEM,
sampleIoOveruseConfigurationBuilder(ComponentType.SYSTEM).build()).build();
List<ResourceOveruseConfiguration> resourceOveruseConfigs = Arrays.asList(config, config);
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(resourceOveruseConfigs,
FLAG_RESOURCE_OVERUSE_IO));
}
@Test
public void testFailsSetResourceOveruseConfigurationsOnZeroComponentLevelIoOveruseThresholds()
throws Exception {
List<ResourceOveruseConfiguration> resourceOveruseConfigs =
Collections.singletonList(
sampleResourceOveruseConfigurationBuilder(ComponentType.SYSTEM,
sampleIoOveruseConfigurationBuilder(ComponentType.SYSTEM)
.setComponentLevelThresholds(
new PerStateBytes(
/* foregroundModeBytes= */ 200,
/* backgroundModeBytes= */ 0,
/* garageModeBytes= */ 200))
.build())
.build());
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(resourceOveruseConfigs,
FLAG_RESOURCE_OVERUSE_IO));
}
@Test
public void testFailsSetResourceOveruseConfigurationsOnNullIoOveruseConfiguration()
throws Exception {
List<ResourceOveruseConfiguration> resourceOveruseConfigs = Collections.singletonList(
sampleResourceOveruseConfigurationBuilder(
ComponentType.SYSTEM, /* ioOveruseConfig= */ null).build());
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(resourceOveruseConfigs,
FLAG_RESOURCE_OVERUSE_IO));
}
@Test
public void testFailsSetResourceOveruseConfigurationsOnEmptyIoOveruseSystemWideThresholds()
throws Exception {
List<ResourceOveruseConfiguration> resourceOveruseConfigs =
Collections.singletonList(
sampleResourceOveruseConfigurationBuilder(ComponentType.SYSTEM,
sampleIoOveruseConfigurationBuilder(ComponentType.SYSTEM)
.setSystemWideThresholds(new ArrayList<>())
.build())
.build());
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(resourceOveruseConfigs,
FLAG_RESOURCE_OVERUSE_IO));
}
@Test
public void testFailsSetResourceOveruseConfigurationsOnIoOveruseInvalidSystemWideThreshold()
throws Exception {
List<ResourceOveruseConfiguration> resourceOveruseConfigs = new ArrayList<>();
resourceOveruseConfigs.add(sampleResourceOveruseConfigurationBuilder(ComponentType.SYSTEM,
sampleIoOveruseConfigurationBuilder(ComponentType.SYSTEM)
.setSystemWideThresholds(Collections.singletonList(
new IoOveruseAlertThreshold(
/* durationInSeconds= */ 30,
/* writtenBytesPerSecond= */ 0)))
.build())
.build());
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(
resourceOveruseConfigs, FLAG_RESOURCE_OVERUSE_IO));
resourceOveruseConfigs.set(0,
sampleResourceOveruseConfigurationBuilder(ComponentType.SYSTEM,
sampleIoOveruseConfigurationBuilder(ComponentType.SYSTEM)
.setSystemWideThresholds(Collections.singletonList(
new IoOveruseAlertThreshold(
/* durationInSeconds= */ 0,
/* writtenBytesPerSecond= */ 300)))
.build())
.build());
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setResourceOveruseConfigurations(
resourceOveruseConfigs, FLAG_RESOURCE_OVERUSE_IO));
}
@Test
public void testGetResourceOveruseConfigurations() throws Exception {
List<ResourceOveruseConfiguration> actualConfigs =
mWatchdogPerfHandler.getResourceOveruseConfigurations(FLAG_RESOURCE_OVERUSE_IO);
ResourceOveruseConfigurationSubject.assertThat(actualConfigs)
.containsExactlyElementsIn(sampleResourceOveruseConfigurations());
}
@Test
public void testGetResourceOveruseConfigurationsWithDisconnectedDaemon() throws Exception {
mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ false);
assertThrows(IllegalStateException.class,
() -> mWatchdogPerfHandler.getResourceOveruseConfigurations(
FLAG_RESOURCE_OVERUSE_IO));
/* Method initially called in WatchdogPerfHandler init */
verify(mMockCarWatchdogDaemonHelper).getResourceOveruseConfigurations();
}
@Test
public void testGetResourceOveruseConfigurationsWithReconnectedDaemon() throws Exception {
crashAndDelayReconnectDaemon();
List<ResourceOveruseConfiguration> actualConfigs =
mWatchdogPerfHandler.getResourceOveruseConfigurations(FLAG_RESOURCE_OVERUSE_IO);
ResourceOveruseConfigurationSubject.assertThat(actualConfigs)
.containsExactlyElementsIn(sampleResourceOveruseConfigurations());
}
@Test
public void testConcurrentSetGetResourceOveruseConfigurationsWithReconnectedDaemon()
throws Exception {
crashAndDelayReconnectDaemon();
/* Capture and respond with the configuration received in the set request. */
List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> internalConfigs =
new ArrayList<>();
doAnswer(args -> {
List<android.automotive.watchdog.internal.ResourceOveruseConfiguration> configs =
args.getArgument(0);
internalConfigs.addAll(configs);
return null;
}).when(mMockCarWatchdogDaemonHelper).updateResourceOveruseConfigurations(anyList());
when(mMockCarWatchdogDaemonHelper.getResourceOveruseConfigurations()).thenReturn(
internalConfigs);
/* Start a set request that will become pending and a blocking get request. */
List<ResourceOveruseConfiguration> setConfigs = sampleResourceOveruseConfigurations();
assertThat(mWatchdogPerfHandler.setResourceOveruseConfigurations(
setConfigs, FLAG_RESOURCE_OVERUSE_IO))
.isEqualTo(CarWatchdogManager.RETURN_CODE_SUCCESS);
List<ResourceOveruseConfiguration> getConfigs =
mWatchdogPerfHandler.getResourceOveruseConfigurations(FLAG_RESOURCE_OVERUSE_IO);
ResourceOveruseConfigurationSubject.assertThat(getConfigs)
.containsExactlyElementsIn(setConfigs);
}
@Test
public void testFailsGetResourceOveruseConfigurationsOnInvalidArgs() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.getResourceOveruseConfigurations(0));
}
private void crashAndDelayReconnectDaemon() {
mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ false);
mMainHandler.postAtTime(
() -> mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ true),
MAX_WAIT_TIME_MILLS - 1000);
}
@Test
public void testAsyncFetchTodayIoUsageStats() throws Exception {
List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = Arrays.asList(
WatchdogStorageUnitTest.constructIoUsageStatsEntry(
/* userId= */ 100, "system_package", /* startTime */ 0,
/* duration= */ 1234,
/* remainingWriteBytes= */ constructPerStateBytes(200, 300, 400),
/* writtenBytes= */ constructPerStateBytes(1000, 2000, 3000),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 2, /* forgivenOveruses= */ 0,
/* totalTimesKilled= */ 1),
WatchdogStorageUnitTest.constructIoUsageStatsEntry(
/* userId= */ 101, "vendor_package", /* startTime */ 0,
/* duration= */ 1234,
/* remainingWriteBytes= */ constructPerStateBytes(500, 600, 700),
/* writtenBytes= */ constructPerStateBytes(1100, 2300, 4300),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 4, /* forgivenOveruses= */ 1,
/* totalTimesKilled= */ 10));
when(mSpiedWatchdogStorage.getTodayIoUsageStats()).thenReturn(ioUsageStatsEntries);
mWatchdogPerfHandler.asyncFetchTodayIoUsageStats();
ArgumentCaptor<List<UserPackageIoUsageStats>> userPackageIoUsageStatsCaptor =
ArgumentCaptor.forClass(List.class);
verify(mMockCarWatchdogDaemonHelper, timeout(MAX_WAIT_TIME_MILLS))
.onTodayIoUsageStatsFetched(userPackageIoUsageStatsCaptor.capture());
List<UserPackageIoUsageStats> expectedStats = Arrays.asList(
constructUserPackageIoUsageStats(/* userId= */ 100, "system_package",
/* writtenBytes= */ constructPerStateBytes(1000, 2000, 3000),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 2),
constructUserPackageIoUsageStats(/* userId= */ 101, "vendor_package",
/* writtenBytes= */ constructPerStateBytes(1100, 2300, 4300),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 4));
assertThat(userPackageIoUsageStatsCaptor.getValue())
.comparingElementsUsing(Correspondence.from(
CarWatchdogServiceUnitTest::isUserPackageIoUsageStatsEquals,
"is user package I/O usage stats equal to"))
.containsExactlyElementsIn(expectedStats);
}
@Test
public void testAsyncFetchTodayIoUsageStatsWithDaemonRemoteException() throws Exception {
List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = Arrays.asList(
WatchdogStorageUnitTest.constructIoUsageStatsEntry(
/* userId= */ 100, "system_package", /* startTime */ 0,
/* duration= */ 1234,
/* remainingWriteBytes= */ constructPerStateBytes(200, 300, 400),
/* writtenBytes= */ constructPerStateBytes(1000, 2000, 3000),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 2, /* forgivenOveruses= */ 0,
/* totalTimesKilled= */ 1),
WatchdogStorageUnitTest.constructIoUsageStatsEntry(
/* userId= */ 101, "vendor_package", /* startTime */ 0,
/* duration= */ 1234,
/* remainingWriteBytes= */ constructPerStateBytes(500, 600, 700),
/* writtenBytes= */ constructPerStateBytes(1100, 2300, 4300),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 4, /* forgivenOveruses= */ 1,
/* totalTimesKilled= */ 10));
when(mSpiedWatchdogStorage.getTodayIoUsageStats()).thenReturn(ioUsageStatsEntries);
doThrow(RemoteException.class)
.when(mMockCarWatchdogDaemonHelper).onTodayIoUsageStatsFetched(any());
mWatchdogPerfHandler.asyncFetchTodayIoUsageStats();
verify(mMockCarWatchdogDaemonHelper,
timeout(MAX_WAIT_TIME_MILLS)).onTodayIoUsageStatsFetched(any());
}
@Test
public void testGetTodayIoUsageStats() throws Exception {
List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = Arrays.asList(
WatchdogStorageUnitTest.constructIoUsageStatsEntry(
/* userId= */ 100, "system_package", /* startTime */ 0,
/* duration= */ 1234,
/* remainingWriteBytes= */ constructPerStateBytes(200, 300, 400),
/* writtenBytes= */ constructPerStateBytes(1000, 2000, 3000),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 2, /* forgivenOveruses= */ 0,
/* totalTimesKilled= */ 1),
WatchdogStorageUnitTest.constructIoUsageStatsEntry(
/* userId= */ 101, "vendor_package", /* startTime */ 0,
/* duration= */ 1234,
/* remainingWriteBytes= */ constructPerStateBytes(500, 600, 700),
/* writtenBytes= */ constructPerStateBytes(1100, 2300, 4300),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 4, /* forgivenOveruses= */ 1,
/* totalTimesKilled= */ 10));
when(mSpiedWatchdogStorage.getTodayIoUsageStats()).thenReturn(ioUsageStatsEntries);
List<UserPackageIoUsageStats> actualStats =
mWatchdogPerfHandler.getTodayIoUsageStats();
List<UserPackageIoUsageStats> expectedStats = Arrays.asList(
constructUserPackageIoUsageStats(/* userId= */ 100, "system_package",
/* writtenBytes= */ constructPerStateBytes(1000, 2000, 3000),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 2),
constructUserPackageIoUsageStats(/* userId= */ 101, "vendor_package",
/* writtenBytes= */ constructPerStateBytes(1100, 2300, 4300),
/* forgivenWriteBytes= */ constructPerStateBytes(100, 100, 100),
/* totalOveruses= */ 4));
assertThat(actualStats).comparingElementsUsing(Correspondence.from(
CarWatchdogServiceUnitTest::isUserPackageIoUsageStatsEquals,
"is user package I/O usage stats equal to"))
.containsExactlyElementsIn(expectedStats);
}
@Test
public void testDisablePackageForUser() throws Exception {
assertWithMessage("Performed resource overuse kill")
.that(mWatchdogPerfHandler.disablePackageForUser("third_party_package",
/* userId= */ 100)).isTrue();
verifyDisabledPackages(/* userPackagesCsv= */ "100:third_party_package");
}
@Test
public void testDisablePackageForUserWithDisabledPackage() throws Exception {
doReturn(COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED).when(() -> PackageManagerHelper
.getApplicationEnabledSettingForUser(anyString(), anyInt()));
assertWithMessage("Performed resource overuse kill")
.that(mWatchdogPerfHandler.disablePackageForUser("third_party_package",
/* userId= */ 100)).isFalse();
verifyNoDisabledPackages();
}
@Test
public void testDisablePackageForUserWithNonexistentPackage() throws Exception {
doThrow(IllegalArgumentException.class).when(mSpiedPackageManager)
.getApplicationEnabledSetting(anyString(), anyInt());
assertWithMessage("Performed resource overuse kill")
.that(mWatchdogPerfHandler.disablePackageForUser("fake_package",
/* userId= */ 100)).isFalse();
verifyNoDisabledPackages();
}
@Test
public void testResetResourceOveruseStatsResetsStats() throws Exception {
UserHandle user = UserHandle.getUserHandleForUid(10003346);
String packageName = mMockContext.getPackageName();
mGenericPackageNameByUid.put(10003346, packageName);
mGenericPackageNameByUid.put(10101278, "vendor_package.critical");
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>());
mWatchdogPerfHandler.resetResourceOveruseStats(Collections.singleton(packageName));
// Resetting resource overuse stats is done on the CarWatchdogService service handler
// thread. Wait until the below message is processed before returning, so the resource
// overuse stats resetting is completed.
CarServiceUtils.runEmptyRunnableOnLooperSync(CAR_WATCHDOG_SERVICE_NAME);
ResourceOveruseStats actualStats =
mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(
packageName, user,
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
ResourceOveruseStats expectedStats = new ResourceOveruseStats.Builder(
packageName, user).build();
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
verify(mSpiedWatchdogStorage).deleteUserPackage(eq(user.getIdentifier()), eq(packageName));
}
@Test
public void testResetResourceOveruseStatsEnablesPackage() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package.A", 10012345,
/* sharedUserId= */ null),
constructPackageManagerPackageInfo("vendor_package.critical.A", 10014567,
"vendor_shared_package.A"),
constructPackageManagerPackageInfo("vendor_package.critical.B", 10014567,
"vendor_shared_package.A"),
constructPackageManagerPackageInfo("system_package.critical.A", 10001278,
"system_shared_package.A"),
constructPackageManagerPackageInfo("third_party_package.B", 10056790,
/* sharedUserId= */ null),
constructPackageManagerPackageInfo("system_package.non_critical.B", 10007345,
"system_shared_package.B")));
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>());
disableUserPackage("third_party_package.A", 100);
disableUserPackage("vendor_package.critical.A", 100);
disableUserPackage("vendor_package.critical.B", 100);
mWatchdogPerfHandler.resetResourceOveruseStats(new ArraySet<>(
Arrays.asList("third_party_package.A", "shared:vendor_shared_package.A",
"shared:system_shared_package.A", "third_party_package.B")));
// Resetting resource overuse stats is done on the CarWatchdogService service handler
// thread. Wait until the below message is processed before returning, so the resource
// overuse stats resetting is completed.
CarServiceUtils.runEmptyRunnableOnLooperSync(CAR_WATCHDOG_SERVICE_NAME);
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package.A", 100);
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("vendor_package.critical.A", 100);
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("vendor_package.critical.B", 100);
verify(mSpiedPackageManager, never())
.getApplicationEnabledSetting("system_package.critical.A", 100);
verify(mSpiedPackageManager, never())
.getApplicationEnabledSetting("third_party_package.B", 100);
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package.A"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(100), anyString());
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("vendor_package.critical.A"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(100), anyString());
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("vendor_package.critical.B"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(100), anyString());
}
@Test
public void testResetResourceOveruseStatsResetsUserPackageSettings() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100, 101);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package.A", 10001278, null),
constructPackageManagerPackageInfo("third_party_package.A", 10101278, null),
constructPackageManagerPackageInfo("third_party_package.B", 10003346, null),
constructPackageManagerPackageInfo("third_party_package.B", 10103346, null)));
injectIoOveruseStatsForPackages(mGenericPackageNameByUid,
/* killablePackages= */ Set.of("third_party_package.A", "third_party_package.B"),
/* shouldNotifyPackages= */ new ArraySet<>());
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A",
UserHandle.ALL, /* isKillable= */ false);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.B",
UserHandle.ALL, /* isKillable= */ false);
mWatchdogPerfHandler.resetResourceOveruseStats(
Collections.singleton("third_party_package.A"));
// Resetting resource overuse stats is done on the CarWatchdogService service handler
// thread. Wait until the below message is processed before returning, so the resource
// overuse stats resetting is completed.
CarServiceUtils.runEmptyRunnableOnLooperSync(CAR_WATCHDOG_SERVICE_NAME);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package.A", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.A", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.B", 100,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.B", 101,
PackageKillableState.KILLABLE_STATE_NO)
);
verify(mSpiedWatchdogStorage, times(2)).deleteUserPackage(anyInt(),
eq("third_party_package.A"));
}
@Test
public void testSetKillablePackageAsUser() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 10103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 10101278, null),
constructPackageManagerPackageInfo("third_party_package", 10203456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 10201278, null)));
UserHandle userHandle = UserHandle.of(101);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package", userHandle,
/* isKillable= */ false);
mWatchdogPerfHandler.setKillablePackageAsUser("vendor_package.critical",
userHandle, /* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("vendor_package.critical", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package", 102,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.critical", 102,
PackageKillableState.KILLABLE_STATE_NEVER));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setKillablePackageAsUser("vendor_package.critical",
userHandle, /* isKillable= */ true));
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102, 103);
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo("third_party_package", 10303456, null)));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("vendor_package.critical", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package", 102,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.critical", 102,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package", 103,
PackageKillableState.KILLABLE_STATE_YES));
verify(mSpiedWatchdogStorage, times(11)).markDirty();
}
@Test
public void testSetKillablePackageAsUserWithSharedUids() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package.A", 10103456, "third_party_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.B", 10103456, "third_party_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.C", 10101356, "third_party_shared_package.B"),
constructPackageManagerPackageInfo(
"third_party_package.D", 10101356, "third_party_shared_package.B")));
UserHandle userHandle = UserHandle.of(101);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A", userHandle,
/* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package.A", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.B", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.C", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.D", 101,
PackageKillableState.KILLABLE_STATE_YES));
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.B", userHandle,
/* isKillable= */ true);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.C", userHandle,
/* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package.A", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.B", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.C", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.D", 101,
PackageKillableState.KILLABLE_STATE_NO));
verify(mSpiedWatchdogStorage, times(7)).markDirty();
}
@Test
public void testSetKillablePackageAsUserForAllUsers() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 10103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 10101278, null),
constructPackageManagerPackageInfo("third_party_package", 10203456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 10201278, null)));
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package", UserHandle.ALL,
/* isKillable= */ false);
mWatchdogPerfHandler.setKillablePackageAsUser("vendor_package.critical",
UserHandle.ALL, /* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("vendor_package.critical", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package", 102,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("vendor_package.critical", 102,
PackageKillableState.KILLABLE_STATE_NEVER));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.setKillablePackageAsUser("vendor_package.critical",
UserHandle.ALL, /* isKillable= */ true));
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102, 103);
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo("third_party_package", 10303456, null)));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("vendor_package.critical", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package", 102,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("vendor_package.critical", 102,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package", 103,
PackageKillableState.KILLABLE_STATE_NO));
verify(mSpiedWatchdogStorage, times(11)).markDirty();
}
@Test
public void testSetKillablePackageAsUsersForAllUsersWithSharedUids() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package.A", 10103456, "third_party_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.B", 10103456, "third_party_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.C", 10101356, "third_party_shared_package.B"),
constructPackageManagerPackageInfo(
"third_party_package.D", 10101356, "third_party_shared_package.B"),
constructPackageManagerPackageInfo(
"third_party_package.A", 10203456, "third_party_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.B", 10203456, "third_party_shared_package.A")));
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A", UserHandle.ALL,
/* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package.A", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.B", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.C", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.D", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.A", 102,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.B", 102,
PackageKillableState.KILLABLE_STATE_NO));
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102, 103);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package.A", 10303456, "third_party_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.B", 10303456, "third_party_shared_package.A")));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.of(103)))
.containsExactly(
new PackageKillableState("third_party_package.A", 103,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.B", 103,
PackageKillableState.KILLABLE_STATE_NO));
verify(mSpiedWatchdogStorage, times(5)).markDirty();
}
@Test
public void testSetKillablePackageAsUserReenablesPackage() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101);
disableUserPackage("third_party_package", 101);
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo("third_party_package", 10103456, null)));
UserHandle userHandle = UserHandle.of(101);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package", userHandle,
/* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle))
.containsExactly(new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO));
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package", 101);
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(101), anyString());
}
@Test
public void testSetKillablePackageAsUserReenablesPackagesWithSharedUids() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101);
disableUserPackage("third_party_package.A", 101);
disableUserPackage("third_party_package.B", 101);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package.A", 10103456, "third_party_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.B", 10103456, "third_party_shared_package.A")));
UserHandle userHandle = UserHandle.of(101);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A", userHandle,
/* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle)).containsExactly(
new PackageKillableState("third_party_package.A", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.B", 101,
PackageKillableState.KILLABLE_STATE_NO));
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package.A", 101);
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package.B", 101);
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package.A"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(101), anyString());
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package.B"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(101), anyString());
}
@Test
public void testSetKillablePackageAsUserForAllUsersReenablesPackages() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
disableUserPackage("third_party_package", 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 10103456, null),
constructPackageManagerPackageInfo("third_party_package", 10203456, null)));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(10103456, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 1)),
constructPackageIoOveruseStats(10203456, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 1)));
// Push stats in order to create and cache the usages of the third_party_package
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package", UserHandle.ALL,
/* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package", 102,
PackageKillableState.KILLABLE_STATE_NO));
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package", 101);
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package", 102);
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(101), anyString());
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(102), anyString());
}
@Test
public void testSetKillablePackageAsUsersForAllUsersReenablesPackagesWithSharedUids()
throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
disableUserPackage("third_party_package.A", 101, 102);
disableUserPackage("third_party_package.B", 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package.A", 10103456, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.B", 10103456, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.A", 10203456, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.B", 10203456, "third_party_shared_package")));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(10103456, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 1)),
constructPackageIoOveruseStats(10203456, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 1)));
// Push stats in order to create and cache the usages of the third_party_shared_package
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A", UserHandle.ALL,
/* isKillable= */ false);
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package.A", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.B", 101,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.A", 102,
PackageKillableState.KILLABLE_STATE_NO),
new PackageKillableState("third_party_package.B", 102,
PackageKillableState.KILLABLE_STATE_NO));
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package.A", 101);
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package.B", 101);
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package.A", 102);
verify(mSpiedPackageManager, times(2))
.getApplicationEnabledSetting("third_party_package.B", 102);
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package.A"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(101), anyString());
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package.B"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(101), anyString());
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package.A"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(102), anyString());
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq("third_party_package.B"),
eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(), eq(102), anyString());
}
@Test
public void testResetPackageKillableStateDuringBootup() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101);
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo("third_party_package",
10103456, /* sharedUserId= */ null)));
mTimeSource.updateNow(PACKAGE_KILLABLE_STATE_RESET_DAYS);
UserHandle userHandle = UserHandle.of(101);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package", userHandle,
/* isKillable= */ false);
PackageKillableStateSubject
.assertThat(mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle))
.containsExactly(new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO));
mTimeSource.updateNow(PACKAGE_KILLABLE_STATE_RESET_DAYS / 2);
restartService(/* totalRestarts= */ 1, /* wantedDbWrites= */ 1,
/* isWriteIoStats= */ false);
PackageKillableStateSubject
.assertThat(mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle))
.containsExactly(new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO));
mTimeSource.updateNow(/* numDaysAgo= */ 0);
restartService(/* totalRestarts= */ 2, /* wantedDbWrites= */ 2,
/* isWriteIoStats= */ false);
PackageKillableStateSubject
.assertThat(mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle))
.containsExactly(new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_YES));
}
@Test
public void testResetPackageKillableStateDuringDateChange() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101);
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo("third_party_package",
10103456, /* sharedUserId= */ null)));
UserHandle userHandle = UserHandle.of(101);
// Reset the latest reported date
mTimeSource.updateNow(PACKAGE_KILLABLE_STATE_RESET_DAYS);
restartService(/* totalRestarts= */ 1, /* wantedDbWrites= */ 0, /* isWriteIoStats= */ true);
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package", userHandle,
/* isKillable= */ false);
PackageKillableStateSubject
.assertThat(mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle))
.containsExactly(new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NO));
// Random I/O overuse stats
List<PackageIoOveruseStats> packageIoOveruseStats = Collections.singletonList(
constructPackageIoOveruseStats(123456, /* shouldNotify= */ true,
/* forgivenWriteBytes= */
constructPerStateBytes(/* fgBytes= */ 80, /* bgBytes= */ 170,
/* gmBytes= */ 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */
constructPerStateBytes(/* fgBytes= */ 20, /* bgBytes= */ 20,
/* gmBytes= */ 20),
/* writtenBytes= */
constructPerStateBytes(/* fgBytes= */ 100, /* bgBytes= */ 200,
/* gmBytes= */ 300),
/* totalOveruses= */ 3)));
mTimeSource.updateNow(/* numDaysAgo= */ 0);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
PackageKillableStateSubject
.assertThat(mWatchdogPerfHandler.getPackageKillableStatesAsUser(userHandle))
.containsExactly(new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_YES));
}
@Test
public void testGetPackageKillableStatesAsUser() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 10103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 10101278, null),
constructPackageManagerPackageInfo("third_party_package", 10203456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 10201278, null)));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.of(101)))
.containsExactly(
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.critical", 101,
PackageKillableState.KILLABLE_STATE_NEVER));
verify(mSpiedWatchdogStorage, times(2)).markDirty();
}
@Test
public void testGetPackageKillableStatesAsUserWithSafeToKillPackages() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100, 101);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package.non_critical.A", 10002459, null),
constructPackageManagerPackageInfo("third_party_package", 10003456, null),
constructPackageManagerPackageInfo("vendor_package.critical.B", 10001278, null),
constructPackageManagerPackageInfo("vendor_package.non_critical.A", 10005573, null),
constructPackageManagerPackageInfo("third_party_package", 10103456, null),
constructPackageManagerPackageInfo("vendor_package.critical.B", 10101278, null)));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.ALL))
.containsExactly(
new PackageKillableState("system_package.non_critical.A", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.critical.B", 100,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("vendor_package.non_critical.A", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.critical.B", 101,
PackageKillableState.KILLABLE_STATE_NEVER));
verify(mSpiedWatchdogStorage, times(6)).markDirty();
}
@Test
public void testGetPackageKillableStatesAsUserWithVendorPackagePrefixes() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100);
injectPackageInfos(Collections.singletonList(constructPackageManagerPackageInfo(
"some_pkg_as_vendor_pkg", 10002459, /* sharedUserId= */ null, /* flags= */ 0,
ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)));
List<PackageKillableState> killableStates =
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.of(100));
// The vendor package prefixes in the resource overuse configs help identify vendor
// packages. The safe-to-kill list in the vendor configs helps identify safe-to-kill vendor
// packages. |system_package_as_vendor| is a critical system package by default but with
// the resource overuse configs, this package should be classified as a safe-to-kill vendor
// package.
PackageKillableStateSubject.assertThat(killableStates)
.containsExactly(new PackageKillableState("some_pkg_as_vendor_pkg", 100,
PackageKillableState.KILLABLE_STATE_YES));
verify(mSpiedWatchdogStorage).markDirty();
}
@Test
public void testGetPackageKillableStatesAsUserWithSharedUids() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"system_package.A", 10103456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"vendor_package.B", 10103456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.C", 10105678, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.D", 10105678, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"system_package.A", 10203456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"vendor_package.B", 10203456, "vendor_shared_package.A")));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.of(101)))
.containsExactly(
new PackageKillableState("system_package.A", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("vendor_package.B", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package.C", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.D", 101,
PackageKillableState.KILLABLE_STATE_YES));
verify(mSpiedWatchdogStorage, times(2)).markDirty();
}
@Test
public void testGetPackageKillableStatesAsUserWithSharedUidsAndSafeToKillPackages()
throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"vendor_package.non_critical.A", 10003456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"system_package.A", 10003456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"vendor_package.B", 10003456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.C", 10005678, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.D", 10005678, "third_party_shared_package")));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.of(100)))
.containsExactly(
new PackageKillableState("vendor_package.non_critical.A", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("system_package.A", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.B", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.C", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.D", 100,
PackageKillableState.KILLABLE_STATE_YES));
verify(mSpiedWatchdogStorage, times(2)).markDirty();
}
@Test
public void testGetPackageKillableStatesAsUserWithSharedUidsAndSafeToKillSharedPackage()
throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"vendor_package.A", 10003456, "vendor_shared_package.non_critical.B"),
constructPackageManagerPackageInfo(
"system_package.A", 10003456, "vendor_shared_package.non_critical.B"),
constructPackageManagerPackageInfo(
"vendor_package.B", 10003456, "vendor_shared_package.non_critical.B")));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.of(100)))
.containsExactly(
new PackageKillableState("vendor_package.A", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("system_package.A", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.B", 100,
PackageKillableState.KILLABLE_STATE_YES));
verify(mSpiedWatchdogStorage).markDirty();
}
@Test
public void testGetPackageKillableStatesAsUserForAllUsers() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 10103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 10101278, null),
constructPackageManagerPackageInfo("third_party_package", 10203456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 10201278, null)));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(
UserHandle.ALL)).containsExactly(
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.critical", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package", 102,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("vendor_package.critical", 102,
PackageKillableState.KILLABLE_STATE_NEVER));
verify(mSpiedWatchdogStorage, times(4)).markDirty();
}
@Test
public void testGetPackageKillableStatesAsUserForAllUsersWithSharedUids() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"system_package.A", 10103456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"vendor_package.B", 10103456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"third_party_package.C", 10105678, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.D", 10105678, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"system_package.A", 10203456, "vendor_shared_package.A"),
constructPackageManagerPackageInfo(
"vendor_package.B", 10203456, "vendor_shared_package.A")));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.ALL))
.containsExactly(
new PackageKillableState("system_package.A", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("vendor_package.B", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package.C", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.D", 101,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("system_package.A", 102,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("vendor_package.B", 102,
PackageKillableState.KILLABLE_STATE_NEVER));
verify(mSpiedWatchdogStorage, times(3)).markDirty();
}
@Test
public void testGetAllResourceOveruseStatsWithNoMinimum() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1201278, null)));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(1103456,
/* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
constructPackageIoOveruseStats(1201278,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(5000, 6000, 9000),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(450, 120, 340),
/* writtenBytes= */ constructPerStateBytes(5000, 6000, 9000),
/* totalOveruses= */ 3)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
List<ResourceOveruseStats> expectedStats = Arrays.asList(
constructResourceOveruseStats(1103456, "third_party_package",
packageIoOveruseStats.get(0).ioOveruseStats),
constructResourceOveruseStats(1201278, "vendor_package.critical",
packageIoOveruseStats.get(1).ioOveruseStats));
List<ResourceOveruseStats> actualStats = mWatchdogPerfHandler.getAllResourceOveruseStats(
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO, /* minimumStatsFlag= */ 0,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
ResourceOveruseStatsSubject.assertThat(actualStats)
.containsExactlyElementsIn(expectedStats);
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testGetAllResourceOveruseStatsWithNoMinimumForPast7days() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1201278, null)));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(1103456,
/* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
constructPackageIoOveruseStats(1201278,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(0, 0, 0),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(450, 120, 340),
/* writtenBytes= */ constructPerStateBytes(5000, 6000, 9000),
/* totalOveruses= */ 0)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
ZonedDateTime now = mTimeSource.getCurrentDateTime();
long startTime = now.minusDays(4).toEpochSecond();
IoOveruseStats thirdPartyPkgOldStats = new IoOveruseStats.Builder(
startTime, now.toEpochSecond() - startTime).setTotalOveruses(5)
.setTotalTimesKilled(2).setTotalBytesWritten(24_000).build();
doReturn(thirdPartyPkgOldStats).when(mSpiedWatchdogStorage)
.getHistoricalIoOveruseStats(11, "third_party_package", 6);
startTime = now.minusDays(6).toEpochSecond();
IoOveruseStats vendorPkgOldStats = new IoOveruseStats.Builder(
startTime, now.toEpochSecond() - startTime).setTotalOveruses(2)
.setTotalTimesKilled(0).setTotalBytesWritten(35_000).build();
doReturn(vendorPkgOldStats).when(mSpiedWatchdogStorage)
.getHistoricalIoOveruseStats(12, "vendor_package.critical", 6);
List<ResourceOveruseStats> actualStats = mWatchdogPerfHandler.getAllResourceOveruseStats(
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO, /* minimumStatsFlag= */ 0,
CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS);
IoOveruseStats thirdPartyIoStats = new IoOveruseStats.Builder(
thirdPartyPkgOldStats.getStartTime(),
thirdPartyPkgOldStats.getDurationInSeconds() + STATS_DURATION_SECONDS)
.setKillableOnOveruse(true).setTotalOveruses(8).setTotalBytesWritten(24_600)
.setTotalTimesKilled(2).setRemainingWriteBytes(new PerStateBytes(0, 0, 0))
.build();
IoOveruseStats vendorIoStats = new IoOveruseStats.Builder(
vendorPkgOldStats.getStartTime(),
vendorPkgOldStats.getDurationInSeconds() + STATS_DURATION_SECONDS)
.setKillableOnOveruse(false).setTotalOveruses(2).setTotalBytesWritten(55_000)
.setTotalTimesKilled(0).setRemainingWriteBytes(new PerStateBytes(450, 120, 340))
.build();
List<ResourceOveruseStats> expectedStats = Arrays.asList(
new ResourceOveruseStats.Builder("third_party_package", UserHandle.of(11))
.setIoOveruseStats(thirdPartyIoStats).build(),
new ResourceOveruseStats.Builder("vendor_package.critical", UserHandle.of(12))
.setIoOveruseStats(vendorIoStats).build());
ResourceOveruseStatsSubject.assertThat(actualStats)
.containsExactlyElementsIn(expectedStats);
}
@Test
public void testGetAllResourceOveruseStatsForSharedPackage() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"vendor_package.A", 1103456, "vendor_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.B", 1103456, "vendor_shared_package"),
constructPackageManagerPackageInfo(
"system_package.C", 1201000, "system_shared_package"),
constructPackageManagerPackageInfo(
"system_package.D", 1201000, "system_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.A", 1303456, "vendor_shared_package"),
constructPackageManagerPackageInfo(
"vendor_package.B", 1303456, "vendor_shared_package")));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(1103456,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(50, 100, 150),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
constructPackageIoOveruseStats(1201000,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(0, 0, 0),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(450, 120, 340),
/* writtenBytes= */ constructPerStateBytes(5000, 6000, 9000),
/* totalOveruses= */ 0)),
constructPackageIoOveruseStats(1303456,
/* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(80, 170, 260),
/* totalOveruses= */ 1)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
List<ResourceOveruseStats> expectedStats = Arrays.asList(
constructResourceOveruseStats(1103456, "shared:vendor_shared_package",
packageIoOveruseStats.get(0).ioOveruseStats),
constructResourceOveruseStats(1201278, "shared:system_shared_package",
packageIoOveruseStats.get(1).ioOveruseStats),
constructResourceOveruseStats(1303456, "shared:vendor_shared_package",
packageIoOveruseStats.get(2).ioOveruseStats));
List<ResourceOveruseStats> actualStats = mWatchdogPerfHandler.getAllResourceOveruseStats(
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO, /* minimumStatsFlag= */ 0,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
ResourceOveruseStatsSubject.assertThat(actualStats)
.containsExactlyElementsIn(expectedStats);
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testFailsGetAllResourceOveruseStatsWithInvalidArgs() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.getAllResourceOveruseStats(0, /* minimumStatsFlag= */ 0,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.getAllResourceOveruseStats(
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB
| CarWatchdogManager.FLAG_MINIMUM_STATS_IO_100_MB,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.getAllResourceOveruseStats(
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO, /* minimumStatsFlag= */ 1 << 5,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.getAllResourceOveruseStats(
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO, /* minimumStatsFlag= */ 0,
/* maxStatsPeriod= */ 0));
}
@Test
public void testGetAllResourceOveruseStatsWithMinimum() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1201278, null)));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(1103456, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
constructPackageIoOveruseStats(1201278, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(5_070_000, 4500, 7000),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(450, 120, 340),
/* writtenBytes= */ constructPerStateBytes(7_000_000, 6000, 9000),
/* totalOveruses= */ 3)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
List<ResourceOveruseStats> expectedStats = Collections.singletonList(
constructResourceOveruseStats(1201278, "vendor_package.critical",
packageIoOveruseStats.get(1).ioOveruseStats));
List<ResourceOveruseStats> actualStats = mWatchdogPerfHandler.getAllResourceOveruseStats(
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
ResourceOveruseStatsSubject.assertThat(actualStats)
.containsExactlyElementsIn(expectedStats);
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testGetAllResourceOveruseStatsWithMinimumForPast7days() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1201278, null)));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(1103456,
/* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
constructPackageIoOveruseStats(1201278,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(0, 0, 0),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(450, 120, 340),
/* writtenBytes= */ constructPerStateBytes(100_000, 6000, 9000),
/* totalOveruses= */ 0)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
ZonedDateTime now = mTimeSource.getCurrentDateTime();
long startTime = now.minusDays(4).toEpochSecond();
IoOveruseStats thirdPartyPkgOldStats = new IoOveruseStats.Builder(
startTime, now.toEpochSecond() - startTime).setTotalOveruses(5)
.setTotalTimesKilled(2).setTotalBytesWritten(24_000).build();
doReturn(thirdPartyPkgOldStats).when(mSpiedWatchdogStorage)
.getHistoricalIoOveruseStats(11, "third_party_package", 6);
startTime = now.minusDays(6).toEpochSecond();
IoOveruseStats vendorPkgOldStats = new IoOveruseStats.Builder(
startTime, now.toEpochSecond() - startTime).setTotalOveruses(2)
.setTotalTimesKilled(0).setTotalBytesWritten(6_900_000).build();
doReturn(vendorPkgOldStats).when(mSpiedWatchdogStorage)
.getHistoricalIoOveruseStats(12, "vendor_package.critical", 6);
List<ResourceOveruseStats> actualStats = mWatchdogPerfHandler.getAllResourceOveruseStats(
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB,
CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS);
IoOveruseStats vendorIoStats = new IoOveruseStats.Builder(
vendorPkgOldStats.getStartTime(),
vendorPkgOldStats.getDurationInSeconds() + STATS_DURATION_SECONDS)
.setKillableOnOveruse(false).setTotalOveruses(2).setTotalBytesWritten(7_015_000)
.setTotalTimesKilled(0).setRemainingWriteBytes(new PerStateBytes(450, 120, 340))
.build();
List<ResourceOveruseStats> expectedStats = Collections.singletonList(
new ResourceOveruseStats.Builder("vendor_package.critical", UserHandle.of(12))
.setIoOveruseStats(vendorIoStats).build());
ResourceOveruseStatsSubject.assertThat(actualStats)
.containsExactlyElementsIn(expectedStats);
}
@Test
public void testGetResourceOveruseStats() throws Exception {
int uid = Binder.getCallingUid();
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo(
mMockContext.getPackageName(), uid, null, ApplicationInfo.FLAG_SYSTEM, 0)));
SparseArray<PackageIoOveruseStats> packageIoOveruseStatsByUid =
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>());
ResourceOveruseStats expectedStats =
constructResourceOveruseStats(uid, mMockContext.getPackageName(),
packageIoOveruseStatsByUid.get(uid).ioOveruseStats);
ResourceOveruseStats actualStats = mWatchdogPerfHandler.getResourceOveruseStats(
FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testGetResourceOveruseStatsForPast7days() throws Exception {
int uid = Binder.getCallingUid();
String packageName = mMockContext.getPackageName();
injectPackageInfos(Collections.singletonList(constructPackageManagerPackageInfo(
packageName, uid, null, ApplicationInfo.FLAG_SYSTEM, 0)));
long startTime = mTimeSource.getCurrentDateTime().minusDays(4).toEpochSecond();
long duration = mTimeSource.now().getEpochSecond() - startTime;
doReturn(new IoOveruseStats.Builder(startTime, duration).setTotalOveruses(5)
.setTotalTimesKilled(2).setTotalBytesWritten(24_000).build())
.when(mSpiedWatchdogStorage)
.getHistoricalIoOveruseStats(UserHandle.getUserId(uid), packageName, 6);
injectIoOveruseStatsForPackages(mGenericPackageNameByUid,
/* killablePackages= */ Collections.singleton(packageName),
/* shouldNotifyPackages= */ new ArraySet<>());
ResourceOveruseStats actualStats = mWatchdogPerfHandler.getResourceOveruseStats(
FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS);
IoOveruseStats ioOveruseStats =
new IoOveruseStats.Builder(startTime, duration + STATS_DURATION_SECONDS)
.setKillableOnOveruse(true).setTotalOveruses(8).setTotalBytesWritten(24_600)
.setTotalTimesKilled(2)
.setRemainingWriteBytes(new PerStateBytes(20, 20, 20)).build();
ResourceOveruseStats expectedStats =
new ResourceOveruseStats.Builder(packageName, UserHandle.getUserHandleForUid(uid))
.setIoOveruseStats(ioOveruseStats).build();
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
}
@Test
public void testGetResourceOveruseStatsForPast7daysWithNoHistory() throws Exception {
int uid = Binder.getCallingUid();
String packageName = mMockContext.getPackageName();
injectPackageInfos(Collections.singletonList(constructPackageManagerPackageInfo(
packageName, uid, null, ApplicationInfo.FLAG_SYSTEM, 0)));
doReturn(null).when(mSpiedWatchdogStorage)
.getHistoricalIoOveruseStats(UserHandle.getUserId(uid), packageName, 6);
injectIoOveruseStatsForPackages(mGenericPackageNameByUid,
/* killablePackages= */ Collections.singleton(packageName),
/* shouldNotifyPackages= */ new ArraySet<>());
ResourceOveruseStats actualStats = mWatchdogPerfHandler.getResourceOveruseStats(
FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS);
ResourceOveruseStats expectedStats =
new ResourceOveruseStats.Builder(packageName, UserHandle.getUserHandleForUid(uid))
.setIoOveruseStats(new IoOveruseStats.Builder(
mTimeSource.now().getEpochSecond(), STATS_DURATION_SECONDS)
.setKillableOnOveruse(true).setTotalOveruses(3)
.setTotalBytesWritten(600)
.setRemainingWriteBytes(new PerStateBytes(20, 20, 20)).build())
.build();
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
}
@Test
public void testGetResourceOveruseStatsForPast7daysWithNoCurrentStats() throws Exception {
int uid = Binder.getCallingUid();
String packageName = mMockContext.getPackageName();
injectPackageInfos(Collections.singletonList(constructPackageManagerPackageInfo(
packageName, uid, null, ApplicationInfo.FLAG_SYSTEM, 0)));
long startTime = mTimeSource.getCurrentDateTime().minusDays(4).toEpochSecond();
long duration = mTimeSource.now().getEpochSecond() - startTime;
doReturn(new IoOveruseStats.Builder(startTime, duration).setTotalOveruses(5)
.setTotalTimesKilled(2).setTotalBytesWritten(24_000).build())
.when(mSpiedWatchdogStorage)
.getHistoricalIoOveruseStats(UserHandle.getUserId(uid), packageName, 6);
ResourceOveruseStats actualStats = mWatchdogPerfHandler.getResourceOveruseStats(
FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS);
ResourceOveruseStats expectedStats =
new ResourceOveruseStats.Builder(packageName, UserHandle.getUserHandleForUid(uid))
.build();
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
}
@Test
public void testGetResourceOveruseStatsForSharedUid() throws Exception {
int sharedUid = Binder.getCallingUid();
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo(
mMockContext.getPackageName(), sharedUid, "system_shared_package",
ApplicationInfo.FLAG_SYSTEM, 0)));
SparseArray<PackageIoOveruseStats> packageIoOveruseStatsByUid =
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(),
/* shouldNotifyPackages= */ new ArraySet<>());
ResourceOveruseStats expectedStats =
constructResourceOveruseStats(sharedUid, "shared:system_shared_package",
packageIoOveruseStatsByUid.get(sharedUid).ioOveruseStats);
ResourceOveruseStats actualStats = mWatchdogPerfHandler.getResourceOveruseStats(
FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
}
@Test
public void testGetResourceOveruseStatsForUserPackage() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1201278, null)));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(1103456,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
constructPackageIoOveruseStats(1201278,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(300, 400, 700),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(450, 120, 340),
/* writtenBytes= */ constructPerStateBytes(500, 600, 900),
/* totalOveruses= */ 3)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
ResourceOveruseStats expectedStats =
constructResourceOveruseStats(1201278, "vendor_package.critical",
packageIoOveruseStats.get(1).ioOveruseStats);
ResourceOveruseStats actualStats =
mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(
"vendor_package.critical", UserHandle.of(12),
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
}
@Test
public void testGetResourceOveruseStatsForUserPackageForPast7days() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1201278, null)));
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
constructPackageIoOveruseStats(1103456,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(80, 170, 260),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 20, 20),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
constructPackageIoOveruseStats(1201278,
/* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(300, 400, 700),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(450, 120, 340),
/* writtenBytes= */ constructPerStateBytes(500, 600, 900),
/* totalOveruses= */ 3)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
ZonedDateTime now = mTimeSource.getCurrentDateTime();
long startTime = now.minusDays(4).toEpochSecond();
IoOveruseStats vendorPkgOldStats = new IoOveruseStats.Builder(
startTime, now.toEpochSecond() - startTime).setTotalOveruses(2)
.setTotalTimesKilled(0).setTotalBytesWritten(6_900_000).build();
doReturn(vendorPkgOldStats).when(mSpiedWatchdogStorage)
.getHistoricalIoOveruseStats(12, "vendor_package.critical", 6);
ResourceOveruseStats actualStats =
mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(
"vendor_package.critical", UserHandle.of(12),
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_PAST_7_DAYS);
IoOveruseStats vendorIoStats = new IoOveruseStats.Builder(
vendorPkgOldStats.getStartTime(),
vendorPkgOldStats.getDurationInSeconds() + STATS_DURATION_SECONDS)
.setKillableOnOveruse(false).setTotalOveruses(5).setTotalBytesWritten(6_902_000)
.setTotalTimesKilled(0).setRemainingWriteBytes(new PerStateBytes(450, 120, 340))
.build();
ResourceOveruseStats expectedStats = new ResourceOveruseStats.Builder(
"vendor_package.critical", UserHandle.of(12)).setIoOveruseStats(vendorIoStats)
.build();
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
}
@Test
public void testGetResourceOveruseStatsForUserPackageWithSharedUids() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package", 1103456, "vendor_shared_package"),
constructPackageManagerPackageInfo(
"vendor_package", 1103456, "vendor_shared_package"),
constructPackageManagerPackageInfo("system_package", 1101100,
"shared_system_package")));
SparseArray<PackageIoOveruseStats> packageIoOveruseStatsByUid =
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid, /* killablePackages= */ new ArraySet<>(
Collections.singleton("shared:vendor_shared_package")),
/* shouldNotifyPackages= */ new ArraySet<>());
ResourceOveruseStats expectedStats =
constructResourceOveruseStats(1103456, "shared:vendor_shared_package",
packageIoOveruseStatsByUid.get(1103456).ioOveruseStats);
ResourceOveruseStats actualStats =
mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(
"vendor_package", UserHandle.of(11),
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
ResourceOveruseStatsSubject.assertEquals(actualStats, expectedStats);
}
@Test
public void testFailsGetResourceOveruseStatsForUserPackageWithInvalidArgs() throws Exception {
assertThrows(NullPointerException.class,
() -> mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage(
/* packageName= */ null, UserHandle.of(100),
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
assertThrows(NullPointerException.class,
() -> mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage("some_package",
/* userHandle= */ null, CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage("some_package",
UserHandle.ALL, CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage("some_package",
UserHandle.of(100), /* resourceOveruseFlag= */ 0,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
assertThrows(IllegalArgumentException.class,
() -> mWatchdogPerfHandler.getResourceOveruseStatsForUserPackage("some_package",
UserHandle.of(100), CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
/* maxStatsPeriod= */ 0));
}
@Test
public void testNoDisableRecurrentlyOverusingAppWhenDisplayEnabled() throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(false);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED), anyInt(),
anyInt(), anyInt(), anyInt(), any(), any()), never());
verifyNoDisabledPackages();
}
@Test
public void testDisableRecurrentlyOverusingAppAfterDisplayDisabled() throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verifyNoDisabledPackages();
setRequiresDistractionOptimization(false);
verifyNoDisabledPackages();
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
captureAndVerifyKillStatsReported(sampleReportedKillStats(
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE,
/* killedUids= */ new int[]{10010004, 10110004, 10010005, 10110005}));
verifyDisabledPackages(/* userPackagesCsv= */ "100:vendor_package.non_critical,"
+ "101:vendor_package.non_critical,100:third_party_package.A,"
+ "101:third_party_package.A,100:third_party_package.B,"
+ "101:third_party_package.B");
}
@Test
public void testImmediateDisableRecurrentlyOverusingAppDuringDisabledDisplay()
throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(false);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
captureAndVerifyKillStatsReported(sampleReportedKillStats(
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE,
/* killedUids= */ new int[]{10010004, 10110004, 10010005, 10110005}));
verifyDisabledPackages(/* userPackagesCsv= */ "100:vendor_package.non_critical,"
+ "101:vendor_package.non_critical,100:third_party_package.A,"
+ "101:third_party_package.A,100:third_party_package.B,"
+ "101:third_party_package.B");
}
@Test
public void testDisableRecurrentlyOverusingAppWhenDisplayDisabledAfterDateChange()
throws Exception {
mTimeSource.updateNow(/* numDaysAgo= */ 1);
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verifyNoDisabledPackages();
mTimeSource.updateNow(/* numDaysAgo= */ 0);
setRequiresDistractionOptimization(false);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
captureAndVerifyKillStatsReported(sampleReportedKillStats(
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE,
/* killedUids= */ new int[]{10010004, 10110004, 10010005, 10110005}));
verifyDisabledPackages(/* userPackagesCsv= */ "100:vendor_package.non_critical,"
+ "101:vendor_package.non_critical,100:third_party_package.A,"
+ "101:third_party_package.A,100:third_party_package.B,"
+ "101:third_party_package.B");
}
@Test
public void testNoDisableRecurrentlyOverusingPrePrioritizedApp() throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
mWatchdogPerfHandler.setKillablePackageAsUser(
"vendor_package.non_critical", new UserHandle(100), /* isKillable= */ false);
mWatchdogPerfHandler.setKillablePackageAsUser(
"third_party_package.A", new UserHandle(101), /* isKillable= */ false);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verifyNoDisabledPackages();
setRequiresDistractionOptimization(false);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
captureAndVerifyKillStatsReported(sampleReportedKillStats(
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE,
/* killedUids= */ new int[]{10110004, 10010005}));
verifyDisabledPackages(/* userPackagesCsv= */ "101:vendor_package.non_critical,"
+ "100:third_party_package.A,100:third_party_package.B");
}
@Test
public void testNoDisableRecurrentlyOverusingPostPrioritizedApp() throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verifyNoDisabledPackages();
mWatchdogPerfHandler.setKillablePackageAsUser(
"vendor_package.non_critical", new UserHandle(100), /* isKillable= */ false);
mWatchdogPerfHandler.setKillablePackageAsUser(
"third_party_package.A", new UserHandle(101), /* isKillable= */ false);
setRequiresDistractionOptimization(false);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
captureAndVerifyKillStatsReported(sampleReportedKillStats(
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE,
/* killedUids= */ new int[]{10110004, 10010005}));
verifyDisabledPackages(/* userPackagesCsv= */ "101:vendor_package.non_critical,"
+ "100:third_party_package.A,100:third_party_package.B");
}
@Test
public void testDisableRecurrentlyOverusingPriorityResettedApp() throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
mWatchdogPerfHandler.setKillablePackageAsUser(
"vendor_package.non_critical", new UserHandle(100), /* isKillable= */ false);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verifyNoDisabledPackages();
mWatchdogPerfHandler.setKillablePackageAsUser(
"vendor_package.non_critical", new UserHandle(100), /* isKillable= */ true);
setRequiresDistractionOptimization(false);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
captureAndVerifyKillStatsReported(sampleReportedKillStats(
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE,
/* killedUids= */ new int[]{10010004, 10110004, 10010005, 10110005}));
verifyDisabledPackages(/* userPackagesCsv= */ "100:vendor_package.non_critical,"
+ "101:vendor_package.non_critical,100:third_party_package.A,"
+ "101:third_party_package.A,100:third_party_package.B,"
+ "101:third_party_package.B");
}
@Test
public void testImmediateDisableRecurrentlyOverusingAppDuringGarageMode()
throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(false);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
mWatchdogPerfHandler.onGarageModeChange(GarageMode.GARAGE_MODE_ON);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
captureAndVerifyKillStatsReported(sampleReportedKillStats(
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__GARAGE_MODE,
/* killedUids= */ new int[]{10010004, 10110004, 10010005, 10110005}));
verifyDisabledPackages(/* userPackagesCsv= */ "100:vendor_package.non_critical,"
+ "101:vendor_package.non_critical,100:third_party_package.A,"
+ "101:third_party_package.A,100:third_party_package.B,"
+ "101:third_party_package.B");
}
@Test
public void testDisableHistoricalRecurrentlyOverusingApp() throws Exception {
doReturn(Arrays.asList(new WatchdogStorage.NotForgivenOverusesEntry(100,
"third_party_package", 2))).when(mSpiedWatchdogStorage)
.getNotForgivenHistoricalIoOveruses(RECURRING_OVERUSE_PERIOD_IN_DAYS);
// Force CarWatchdogService to fetch historical not forgiven overuses.
restartService(/* totalRestarts= */ 1, /* wantedDbWrites= */ 0);
setRequiresDistractionOptimization(true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
int thirdPartyPkgUid = UserHandle.getUid(100, 10005);
injectPackageInfos(Collections.singletonList(constructPackageManagerPackageInfo(
"third_party_package", thirdPartyPkgUid, null)));
pushLatestIoOveruseStatsAndWait(
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ false));
// Third party package is disabled given the two historical overuses and one current
// overuse.
verifyDisabledPackages(/* message= */ "after recurring overuse with history",
/* userPackagesCsv= */ "100:third_party_package");
// Package was enabled again.
doReturn(COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED).when(mSpiedPackageManager)
.getApplicationEnabledSetting("third_party_package", 100);
enableUserPackage("third_party_package", 100, true);
PackageIoOveruseStats packageIoOveruseStats =
constructPackageIoOveruseStats(thirdPartyPkgUid, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(200, 400, 600),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(200, 400, 600),
/* totalOveruses= */ 3));
pushLatestIoOveruseStatsAndWait(Collections.singletonList(packageIoOveruseStats));
// From the 3 total overuses, one overuse was forgiven previously.
verifyNoDisabledPackages(/* message= */ "after non-recurring overuse");
// Add one overuse.
packageIoOveruseStats.ioOveruseStats.totalOveruses = 4;
pushLatestIoOveruseStatsAndWait(Collections.singletonList(packageIoOveruseStats));
// Third party package is disabled again given the three current overuses. From the 4 total
// overuses, one overuse was forgiven previously.
verifyDisabledPackages(/* message= */ "after recurring overuse from the same day",
/* userPackagesCsv= */ "100:third_party_package");
// Force write to database
restartService(/* totalRestarts= */ 2, /* wantedDbWrites= */ 1);
verify(mSpiedWatchdogStorage).forgiveHistoricalOveruses(mPackagesByUserIdCaptor.capture(),
eq(RECURRING_OVERUSE_PERIOD_IN_DAYS));
assertWithMessage("Forgiven packages")
.that(mPackagesByUserIdCaptor.getValue().get(100))
.containsExactlyElementsIn(Arrays.asList("third_party_package"));
}
@Test
public void testDisableHistoricalRecurrentlyOverusingAppAfterDateChange() throws Exception {
doReturn(Arrays.asList(new WatchdogStorage.NotForgivenOverusesEntry(100,
"third_party_package", 2))).when(mSpiedWatchdogStorage)
.getNotForgivenHistoricalIoOveruses(RECURRING_OVERUSE_PERIOD_IN_DAYS);
mTimeSource.updateNow(/* numDaysAgo= */ 1);
setRequiresDistractionOptimization(true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
int thirdPartyPkgUid = UserHandle.getUid(100, 10005);
injectPackageInfos(Collections.singletonList(constructPackageManagerPackageInfo(
"third_party_package", thirdPartyPkgUid, /* sharedUserId */ null)));
List<PackageIoOveruseStats> ioOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ false);
pushLatestIoOveruseStatsAndWait(ioOveruseStats);
// Third party package is disabled given the two historical overuses and one current
// overuse.
verifyDisabledPackages(/* userPackagesCsv= */ "100:third_party_package");
// Force write to database by pushing non-overusing I/O overuse stats.
mTimeSource.updateNow(/* numDaysAgo= */ 0);
pushLatestIoOveruseStatsAndWait(Collections.singletonList(ioOveruseStats.get(0)));
verify(mSpiedWatchdogStorage).forgiveHistoricalOveruses(mPackagesByUserIdCaptor.capture(),
eq(RECURRING_OVERUSE_PERIOD_IN_DAYS));
assertWithMessage("Forgiven packages")
.that(mPackagesByUserIdCaptor.getValue().get(100))
.containsExactlyElementsIn(Arrays.asList("third_party_package"));
}
@Test
public void testLatestIoOveruseStats() throws Exception {
setRequiresDistractionOptimization(/* isRequires= */ true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
int criticalSysPkgUid = Binder.getCallingUid();
int nonCriticalSysPkgUid = 10001056;
int nonCriticalVndrPkgUid = 10002564;
int thirdPartyPkgUid = 10002044;
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"system_package.critical", criticalSysPkgUid, null),
constructPackageManagerPackageInfo(
"system_package.non_critical", nonCriticalSysPkgUid, null),
constructPackageManagerPackageInfo(
"vendor_package.non_critical", nonCriticalVndrPkgUid, null),
constructPackageManagerPackageInfo(
"third_party_package", thirdPartyPkgUid, null)));
IResourceOveruseListener mockSystemListener = createMockResourceOveruseListener();
mWatchdogPerfHandler.addResourceOveruseListenerForSystem(
FLAG_RESOURCE_OVERUSE_IO, mockSystemListener);
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
mWatchdogPerfHandler.addResourceOveruseListener(
FLAG_RESOURCE_OVERUSE_IO, mockListener);
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
/* Overuse occurred but cannot be killed/disabled. */
constructPackageIoOveruseStats(criticalSysPkgUid, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
/* No overuse occurred but should be notified. */
constructPackageIoOveruseStats(nonCriticalSysPkgUid, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(50, 100, 150),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(20, 30, 40),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
/* Neither overuse occurred nor be notified. */
constructPackageIoOveruseStats(nonCriticalVndrPkgUid, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(25, 50, 75),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(200, 300, 400),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
/* Overuse occurred and can be killed/disabled. */
constructPackageIoOveruseStats(thirdPartyPkgUid, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(300, 600, 900),
/* totalOveruses= */ 3)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verifyDisabledPackages(/* userPackagesCsv= */ "100:third_party_package");
List<ResourceOveruseStats> expectedStats = new ArrayList<>();
expectedStats.add(constructResourceOveruseStats(criticalSysPkgUid,
"system_package.critical", packageIoOveruseStats.get(0).ioOveruseStats));
verifyOnOveruseCalled(expectedStats, mockListener);
expectedStats.add(constructResourceOveruseStats(nonCriticalSysPkgUid,
"system_package.non_critical", packageIoOveruseStats.get(1).ioOveruseStats));
/*
* When the package receives overuse notification, the package is not yet killed so the
* totalTimesKilled counter is not yet incremented.
*/
expectedStats.add(constructResourceOveruseStats(thirdPartyPkgUid, "third_party_package",
packageIoOveruseStats.get(3).ioOveruseStats));
verifyOnOveruseCalled(expectedStats, mockSystemListener);
List<AtomsProto.CarWatchdogIoOveruseStatsReported> expectedReportedOveruseStats =
new ArrayList<>();
expectedReportedOveruseStats.add(constructIoOveruseStatsReported(criticalSysPkgUid,
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(10, 20, 30),
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(100, 200, 300)));
expectedReportedOveruseStats.add(constructIoOveruseStatsReported(thirdPartyPkgUid,
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(30, 60, 90),
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(300, 600, 900)));
captureAndVerifyIoOveruseStatsReported(expectedReportedOveruseStats);
List<AtomsProto.CarWatchdogKillStatsReported> expectedReportedKillStats =
Collections.singletonList(constructIoOveruseKillStatsReported(thirdPartyPkgUid,
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE,
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(30, 60, 90),
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(300, 600, 900)));
captureAndVerifyKillStatsReported(expectedReportedKillStats);
}
@Test
public void testLatestIoOveruseStatsWithSharedUid() throws Exception {
setRequiresDistractionOptimization(/* isRequires= */ true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
int criticalSysSharedUid = Binder.getCallingUid();
int nonCriticalVndrSharedUid = 10002564;
int thirdPartySharedUid = 10002044;
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"system_package.A", criticalSysSharedUid, "system_shared_package"),
constructPackageManagerPackageInfo(
"system_package.B", criticalSysSharedUid, "system_shared_package"),
constructPackageManagerPackageInfo("vendor_package.non_critical",
nonCriticalVndrSharedUid, "vendor_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.A", thirdPartySharedUid, "third_party_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.B", thirdPartySharedUid, "third_party_shared_package")
));
IResourceOveruseListener mockSystemListener = createMockResourceOveruseListener();
mWatchdogPerfHandler.addResourceOveruseListenerForSystem(
FLAG_RESOURCE_OVERUSE_IO, mockSystemListener);
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
mWatchdogPerfHandler.addResourceOveruseListener(
FLAG_RESOURCE_OVERUSE_IO, mockListener);
List<PackageIoOveruseStats> packageIoOveruseStats = Arrays.asList(
/* Overuse occurred but cannot be killed/disabled. */
constructPackageIoOveruseStats(criticalSysSharedUid, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ false,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
/* No overuse occurred but should be notified. */
constructPackageIoOveruseStats(nonCriticalVndrSharedUid, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(50, 100, 150),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(200, 300, 400),
/* writtenBytes= */ constructPerStateBytes(100, 200, 300),
/* totalOveruses= */ 3)),
/* Overuse occurred and can be killed/disabled. */
constructPackageIoOveruseStats(thirdPartySharedUid, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(300, 600, 900),
/* totalOveruses= */ 3)));
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
verifyDisabledPackages(
/* userPackagesCsv= */ "100:third_party_package.A,100:third_party_package.B");
List<ResourceOveruseStats> expectedStats = new ArrayList<>();
expectedStats.add(constructResourceOveruseStats(criticalSysSharedUid,
"shared:system_shared_package", packageIoOveruseStats.get(0).ioOveruseStats));
verifyOnOveruseCalled(expectedStats, mockListener);
expectedStats.add(constructResourceOveruseStats(nonCriticalVndrSharedUid,
"shared:vendor_shared_package", packageIoOveruseStats.get(1).ioOveruseStats));
/*
* When the package receives overuse notification, the package is not yet killed so the
* totalTimesKilled counter is not yet incremented.
*/
expectedStats.add(constructResourceOveruseStats(thirdPartySharedUid,
"shared:third_party_shared_package", packageIoOveruseStats.get(2).ioOveruseStats));
verifyOnOveruseCalled(expectedStats, mockSystemListener);
List<AtomsProto.CarWatchdogIoOveruseStatsReported> expectedReportedOveruseStats =
new ArrayList<>();
expectedReportedOveruseStats.add(constructIoOveruseStatsReported(criticalSysSharedUid,
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(10, 20, 30),
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(100, 200, 300)));
expectedReportedOveruseStats.add(constructIoOveruseStatsReported(thirdPartySharedUid,
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(30, 60, 90),
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(300, 600, 900)));
captureAndVerifyIoOveruseStatsReported(expectedReportedOveruseStats);
List<AtomsProto.CarWatchdogKillStatsReported> expectedReportedKillStats =
Collections.singletonList(constructIoOveruseKillStatsReported(thirdPartySharedUid,
CAR_WATCHDOG_KILL_STATS_REPORTED__SYSTEM_STATE__USER_NO_INTERACTION_MODE,
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(30, 60, 90),
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(300, 600, 900)));
captureAndVerifyKillStatsReported(expectedReportedKillStats);
}
@Test
public void testPersistStatsOnRestart() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100, 101, 102);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package", 10103456, "vendor_shared_package.critical"),
constructPackageManagerPackageInfo(
"vendor_package", 10103456, "vendor_shared_package.critical"),
constructPackageManagerPackageInfo("third_party_package.A", 10001100, null),
constructPackageManagerPackageInfo("third_party_package.A", 10201100, null)));
SparseArray<PackageIoOveruseStats> packageIoOveruseStatsByUid =
injectIoOveruseStatsForPackages(
mGenericPackageNameByUid,
/* killablePackages= */ new ArraySet<>(Collections.singletonList(
"third_party_package.A")),
/* shouldNotifyPackages= */ new ArraySet<>());
mWatchdogPerfHandler.setKillablePackageAsUser(
"third_party_package.A", UserHandle.of(102), /* isKillable= */ false);
restartService(/* totalRestarts= */ 1, /* wantedDbWrites= */ 1);
List<ResourceOveruseStats> actualStats = mWatchdogPerfHandler.getAllResourceOveruseStats(
FLAG_RESOURCE_OVERUSE_IO, /* minimumStatsFlag= */ 0,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
List<ResourceOveruseStats> expectedStats = Arrays.asList(
constructResourceOveruseStats(
/* uid= */ 10103456, "shared:vendor_shared_package.critical",
packageIoOveruseStatsByUid.get(10103456).ioOveruseStats),
constructResourceOveruseStats(/* uid= */ 10001100, "third_party_package.A",
packageIoOveruseStatsByUid.get(10001100).ioOveruseStats),
constructResourceOveruseStats(/* uid= */ 10201100, "third_party_package.A",
packageIoOveruseStatsByUid.get(10201100).ioOveruseStats));
PackageKillableStateSubject.assertThat(
mWatchdogPerfHandler.getPackageKillableStatesAsUser(UserHandle.ALL))
.containsExactly(
new PackageKillableState("third_party_package", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("vendor_package", 101,
PackageKillableState.KILLABLE_STATE_NEVER),
new PackageKillableState("third_party_package.A", 100,
PackageKillableState.KILLABLE_STATE_YES),
new PackageKillableState("third_party_package.A", 102,
PackageKillableState.KILLABLE_STATE_NO));
// Changing and getting package killable states marks the database as dirty
verify(mSpiedWatchdogStorage, times(5)).markDirty();
ResourceOveruseStatsSubject.assertThat(actualStats)
.containsExactlyElementsIn(expectedStats);
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testWriteToDbOnDateChange() throws Exception {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package", 10011200, null),
constructPackageManagerPackageInfo("third_party_package", 10001100, null)));
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
mTimeSource.updateNow(/* numDaysAgo= */ 1);
List<PackageIoOveruseStats> prevDayStats = Arrays.asList(
constructPackageIoOveruseStats(10011200, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(600, 700, 800),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(600, 700, 800),
/* totalOveruses= */ 3)),
constructPackageIoOveruseStats(10001100, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(1050, 1100, 1200),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(50, 60, 70),
/* writtenBytes= */ constructPerStateBytes(1100, 1200, 1300),
/* totalOveruses= */ 5)));
pushLatestIoOveruseStatsAndWait(prevDayStats);
List<WatchdogStorage.UserPackageSettingsEntry> expectedSavedUserPackageEntries =
Arrays.asList(
new WatchdogStorage.UserPackageSettingsEntry(/* userId= */ 100,
"system_package",
/* killableState= */ PackageKillableState.KILLABLE_STATE_YES,
/* lastModifiedKillableStateEpoch= */ 123456789),
new WatchdogStorage.UserPackageSettingsEntry(/* userId= */ 100,
"third_party_package",
/* killableState= */ PackageKillableState.KILLABLE_STATE_YES,
/* lastModifiedKillableStateEpoch= */ 123456789));
List<WatchdogStorage.IoUsageStatsEntry> expectedSavedIoUsageEntries = Arrays.asList(
new WatchdogStorage.IoUsageStatsEntry(/* userId= */ 100, "system_package",
new WatchdogPerfHandler.PackageIoUsage(prevDayStats.get(0).ioOveruseStats,
/* forgivenWriteBytes= */ constructPerStateBytes(600, 700, 800),
/* forgivenOveruses= */ 3, /* totalTimesKilled= */ 1)),
new WatchdogStorage.IoUsageStatsEntry(/* userId= */ 100, "third_party_package",
new WatchdogPerfHandler.PackageIoUsage(prevDayStats.get(1).ioOveruseStats,
/* forgivenWriteBytes= */ constructPerStateBytes(1050, 1100, 1200),
/* forgivenOveruses= */ 0, /* totalTimesKilled= */ 0)));
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
mTimeSource.updateNow(/* numDaysAgo= */ 0);
List<PackageIoOveruseStats> currentDayStats = Arrays.asList(
constructPackageIoOveruseStats(10011200, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(0, 0, 0),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(500, 550, 600),
/* writtenBytes= */ constructPerStateBytes(100, 150, 200),
/* totalOveruses= */ 0)),
constructPackageIoOveruseStats(10001100, /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(0, 0, 0),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(250, 360, 470),
/* writtenBytes= */ constructPerStateBytes(900, 900, 900),
/* totalOveruses= */ 0)));
pushLatestIoOveruseStatsAndWait(currentDayStats);
assertWithMessage("Saved user package setting entries")
.that(mUserPackageSettingsEntries)
.containsExactlyElementsIn(expectedSavedUserPackageEntries);
IoUsageStatsEntrySubject.assertThat(mIoUsageStatsEntries)
.containsExactlyElementsIn(expectedSavedIoUsageEntries);
List<ResourceOveruseStats> actualCurrentDayStats =
mWatchdogPerfHandler.getAllResourceOveruseStats(
FLAG_RESOURCE_OVERUSE_IO, /* minimumStatsFlag= */ 0,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
List<ResourceOveruseStats> expectedCurrentDayStats = Arrays.asList(
constructResourceOveruseStats(/* uid= */ 10011200, "system_package",
currentDayStats.get(0).ioOveruseStats),
constructResourceOveruseStats(/* uid= */ 10001100, "third_party_package",
currentDayStats.get(1).ioOveruseStats));
ResourceOveruseStatsSubject.assertThat(actualCurrentDayStats)
.containsExactlyElementsIn(expectedCurrentDayStats);
}
@Test
public void testNoWriteToDbOnDateChangeWithNoStats() throws Exception {
mTimeSource.updateNow(/* numDaysAgo= */ 1);
// Since no I/O overuse stats where sent by watchdog daemon, no stats are written to
// database.
restartService(/* totalRestarts= */ 1, /* wantedDbWrites= */ 0);
mTimeSource.updateNow(/* numDaysAgo= */ 0);
pushLatestIoOveruseStatsAndWait(
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ false));
verify(mSpiedWatchdogStorage, never()).saveUserPackageSettings(any());
verify(mSpiedWatchdogStorage, never()).saveIoUsageStats(any());
verify(mSpiedWatchdogStorage, never()).forgiveHistoricalOveruses(any(), anyInt());
}
@Test
public void testNoUserNotificationWithNoRecurrentOveruse() throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(false);
setUpSampleUserAndPackages();
pushLatestIoOveruseStatsAndWait(
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ false));
// Verify no notification is sent
captureAndVerifyUserNotifications(Collections.emptyList());
}
@Test
public void testNoUserNotificationOnRecurrentOveruseWithDistractionOptimization()
throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(true);
setUpSampleUserAndPackages();
pushLatestIoOveruseStatsAndWait(
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true));
// Verify no notification is sent
captureAndVerifyUserNotifications(Collections.emptyList());
}
@Test
public void testUserNotificationOnRecurrentOveruseAfterNoDistractionOptimization()
throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(true);
setUpSampleUserAndPackages();
pushLatestIoOveruseStatsAndWait(
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true));
setRequiresDistractionOptimization(false);
captureAndVerifyUserNotifications(
Collections.singletonList(
new UserNotificationReflectionCall(
UserHandle.of(100),
constructPackagesByNotificationId(/* idOffset= */ 0,
"vendor_package.non_critical", "third_party_package.A",
"third_party_package.B"), /* hasHeadsUpNotification= */
true)));
}
@Test
public void testNoDuplicateUserNotificationOnRepeatedRecurrentOveruse()
throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(false);
setUpSampleUserAndPackages();
List<PackageIoOveruseStats> ioOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(ioOveruseStats);
// Should not produce resource overuse notifications.
pushLatestIoOveruseStatsAndWait(ioOveruseStats);
captureAndVerifyUserNotifications(
Collections.singletonList(
new UserNotificationReflectionCall(
UserHandle.of(100),
constructPackagesByNotificationId(/* idOffset= */ 0,
"vendor_package.non_critical", "third_party_package.A",
"third_party_package.B"), /* hasHeadsUpNotification= */
true)));
}
@Test
public void testImmediateUserNotificationOnRecurrentOveruseWhenNoDistractionOptimization()
throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(false);
setUpSampleUserAndPackages();
pushLatestIoOveruseStatsAndWait(
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true));
pushLatestIoOveruseStatsAndWait(Collections.singletonList(
constructPackageIoOveruseStats(/* uid= */ 10010002, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(300, 600, 900),
/* totalOveruses= */ 3))));
List<UserNotificationReflectionCall>
userNotificationReflectionCalls = Arrays.asList(
new UserNotificationReflectionCall(UserHandle.of(100),
constructPackagesByNotificationId(/* idOffset= */ 0,
"vendor_package.non_critical", "third_party_package.A",
"third_party_package.B"), /* hasHeadsUpNotification= */ true),
new UserNotificationReflectionCall(UserHandle.of(100),
constructPackagesByNotificationId(/* idOffset= */ 3,
"system_package.non_critical"),
/* hasHeadsUpNotification= */ false));
captureAndVerifyUserNotifications(userNotificationReflectionCalls);
}
@Test
public void testNoUserNotificationOnRecurrentOveruseByPrePrioritizedApp() throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(true);
setUpSampleUserAndPackages();
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A",
UserHandle.of(100), /* isKillable= */ false);
pushLatestIoOveruseStatsAndWait(Collections.singletonList(
constructPackageIoOveruseStats(/* uid= */ 10010005, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(300, 600, 900),
/* totalOveruses= */ 3))));
setRequiresDistractionOptimization(false);
// Verify no notification is sent
captureAndVerifyUserNotifications(Collections.emptyList());
}
@Test
public void testNoUserNotificationOnRecurrentOveruseByPostPrioritizedApp() throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(true);
setUpSampleUserAndPackages();
pushLatestIoOveruseStatsAndWait(Collections.singletonList(
constructPackageIoOveruseStats(/* uid= */ 10010005, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(300, 600, 900),
/* totalOveruses= */ 3))));
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A",
UserHandle.of(100), /* isKillable= */ false);
setRequiresDistractionOptimization(false);
// Verify no notification is sent
captureAndVerifyUserNotifications(Collections.emptyList());
}
@Test
public void testUserNotificationOnRecurrentOveruseByPriorityResettedApp() throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(true);
setUpSampleUserAndPackages();
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A", UserHandle.of(100),
/* isKillable= */ false);
pushLatestIoOveruseStatsAndWait(Collections.singletonList(
constructPackageIoOveruseStats(/* uid= */ 10010005, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(300, 600, 900),
/* totalOveruses= */ 3))));
mWatchdogPerfHandler.setKillablePackageAsUser("third_party_package.A", UserHandle.of(100),
/* isKillable= */ true);
setRequiresDistractionOptimization(false);
captureAndVerifyUserNotifications(Collections.singletonList(
new UserNotificationReflectionCall(UserHandle.of(100),
constructPackagesByNotificationId(/* idOffset= */ 0,
"third_party_package.A", "third_party_package.B"),
/* hasHeadsUpNotification= */ true)));
}
@Test
public void testUserNotificationOnHistoricalRecurrentOveruse() throws Exception {
doReturn(Arrays.asList(new WatchdogStorage.NotForgivenOverusesEntry(100,
"system_package.non_critical", 2)))
.when(mSpiedWatchdogStorage)
.getNotForgivenHistoricalIoOveruses(RECURRING_OVERUSE_PERIOD_IN_DAYS);
// Force CarWatchdogService to fetch historical not forgiven overuses.
restartService(/* totalRestarts= */ 1, /* wantedDbWrites= */ 0);
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
setRequiresDistractionOptimization(false);
setUpSampleUserAndPackages();
pushLatestIoOveruseStatsAndWait(Collections.singletonList(
constructPackageIoOveruseStats(/* uid= */ 10010002, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(300, 600, 900),
/* totalOveruses= */ 1))));
captureAndVerifyUserNotifications(Collections.singletonList(
new UserNotificationReflectionCall(UserHandle.of(100),
constructPackagesByNotificationId(/* idOffset= */ 0,
"system_package.non_critical"),
/* hasHeadsUpNotification= */ true)));
}
@Test
public void testUserNotificationWithDisabledDisplay() throws Exception {
mockAmGetCurrentUser(CURRENT_USER_ID);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
setRequiresDistractionOptimization(false);
setUpSampleUserAndPackages();
pushLatestIoOveruseStatsAndWait(Collections.singletonList(
constructPackageIoOveruseStats(/* uid= */ 10010002, /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(100, 200, 300),
constructInternalIoOveruseStats(/* killableOnOveruse= */ true,
/* remainingWriteBytes= */ constructPerStateBytes(0, 0, 0),
/* writtenBytes= */ constructPerStateBytes(300, 600, 900),
/* totalOveruses= */ 3))));
captureAndVerifyUserNotifications(Collections.singletonList(
new UserNotificationReflectionCall(UserHandle.of(100),
constructPackagesByNotificationId(/* idOffset= */ 0,
"system_package.non_critical"),
/* hasHeadsUpNotification= */ false)));
}
@Test
public void testNoDisableWithNoRecurrentOveruse() throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(false);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ false);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ false);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED),
anyInt(), anyInt(), anyInt(), anyInt(), any(), any()), never());
verifyNoDisabledPackages();
}
@Test
public void testNoDisableRecurrentlyOverusingAppWithDistractionOptimization() throws Exception {
setUpSampleUserAndPackages();
setRequiresDistractionOptimization(true);
mWatchdogPerfHandler.onDisplayStateChanged(/* isEnabled= */ true);
List<PackageIoOveruseStats> packageIoOveruseStats =
sampleIoOveruseStats(/* requireRecurrentOveruseStats= */ true);
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
captureAndVerifyIoOveruseStatsReported(sampleReportedOveruseStats());
verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED),
anyInt(), anyInt(), anyInt(), anyInt(), any(), any()), never());
verifyNoDisabledPackages();
}
@Test
public void testPullUidIoUsageSummaryAtomsWithDateChange() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package.critical", 10010001, null),
constructPackageManagerPackageInfo("vendor_package.non_critical", 10110004, null),
constructPackageManagerPackageInfo("third_party_package.A", 10110005,
"third_party_shared_package")));
mTimeSource.updateNow(/* numDaysAgo= */ 7);
List<StatsEvent> events = new ArrayList<>();
assertWithMessage("Stats pull atom callback status")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
List<AtomsProto.CarWatchdogUidIoUsageSummary> expectedSummaries =
verifyAndGetUidIoUsageSummaries(
mTimeSource.getCurrentDate().minus(RETENTION_PERIOD),
/* expectUids= */ Arrays.asList(10010001, 10110004, 10110005));
assertWithMessage("First pulled uid I/O usage summary atoms")
.that(mPulledUidIoUsageSummaries).containsExactlyElementsIn(expectedSummaries);
mPulledUidIoUsageSummaries.clear();
mTimeSource.updateNow(/* numDaysAgo= */ 6);
assertWithMessage("Status of stats pull atom callback within the same week")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
assertWithMessage("Pulled uid I/O usage summary atoms within the same week")
.that(mPulledUidIoUsageSummaries).isEmpty();
mTimeSource.updateNow(/* numDaysAgo= */ 0);
assertWithMessage("Status of stats pull atom callback after a week")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
expectedSummaries = verifyAndGetUidIoUsageSummaries(
mTimeSource.getCurrentDate().minus(1, ChronoUnit.WEEKS),
/* expectUids= */ Arrays.asList(10010001, 10110004, 10110005));
assertWithMessage("Pulled uid I/O usage summary atoms after a week")
.that(mPulledUidIoUsageSummaries).containsExactlyElementsIn(expectedSummaries);
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testPullSystemIoUsageSummaryAtomsWithRestart() throws Exception {
List<StatsEvent> events = new ArrayList<>();
assertWithMessage("Stats pull atom callback status")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
List<AtomsProto.CarWatchdogSystemIoUsageSummary> expectedSummaries =
verifyAndGetSystemIoUsageSummaries(
mTimeSource.getCurrentDate().minus(RETENTION_PERIOD));
assertWithMessage("First pulled system I/O usage summary atoms")
.that(mPulledSystemIoUsageSummaries).containsExactlyElementsIn(expectedSummaries);
mPulledSystemIoUsageSummaries.clear();
restartService(/* totalRestarts= */ 1, /* wantedDbWrites= */ 0);
assertWithMessage("Status of stats pull atom callback after restart")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
assertWithMessage("Pulled system I/O usage summary atoms after restart")
.that(mPulledSystemIoUsageSummaries).isEmpty();
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testPullSystemIoUsageSummaryAtomsWithDateChange() throws Exception {
mTimeSource.updateNow(/* numDaysAgo= */ 7);
List<StatsEvent> events = new ArrayList<>();
assertWithMessage("Stats pull atom callback status")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
List<AtomsProto.CarWatchdogSystemIoUsageSummary> expectedSummaries =
verifyAndGetSystemIoUsageSummaries(
mTimeSource.getCurrentDate().minus(RETENTION_PERIOD));
assertWithMessage("First pulled system I/O usage summary atoms")
.that(mPulledSystemIoUsageSummaries).containsExactlyElementsIn(expectedSummaries);
mPulledSystemIoUsageSummaries.clear();
mTimeSource.updateNow(/* numDaysAgo= */ 6);
assertWithMessage("Status of stats pull atom callback within the same week")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
assertWithMessage("Pulled system I/O usage summary atoms within the same week")
.that(mPulledSystemIoUsageSummaries).isEmpty();
mTimeSource.updateNow(/* numDaysAgo= */ 0);
assertWithMessage("Status of stats pull atom callback after a week")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
expectedSummaries = verifyAndGetSystemIoUsageSummaries(
mTimeSource.getCurrentDate().minus(1, ChronoUnit.WEEKS));
assertWithMessage("Pulled system I/O usage summary atoms after a week")
.that(mPulledSystemIoUsageSummaries).containsExactlyElementsIn(expectedSummaries);
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testPullUidIoUsageSummaryAtomsForTopUids() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package.critical.A", 10000345, null),
constructPackageManagerPackageInfo("third_party_package.B", 10004675, null),
constructPackageManagerPackageInfo("system_package.critical.B", 10010001, null),
constructPackageManagerPackageInfo("vendor_package.non_critical", 10110004, null),
constructPackageManagerPackageInfo("third_party_package.A", 10110005,
"third_party_shared_package")));
List<StatsEvent> events = new ArrayList<>();
assertWithMessage("Stats pull atom callback status")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
List<AtomsProto.CarWatchdogUidIoUsageSummary> expectedSummaries =
verifyAndGetUidIoUsageSummaries(
mTimeSource.getCurrentDate().minus(RETENTION_PERIOD),
/* expectUids= */ Arrays.asList(10010001, 10110004, 10110005));
assertWithMessage(String.format("Pulled uid I/O usage summary atoms for top %d UIDs",
UID_IO_USAGE_SUMMARY_TOP_COUNT)).that(mPulledUidIoUsageSummaries)
.containsExactlyElementsIn(expectedSummaries);
}
@Test
public void testPullUidIoUsageSummaryAtomsWithRestart() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package.critical", 10010001, null),
constructPackageManagerPackageInfo("vendor_package.non_critical", 10110004, null),
constructPackageManagerPackageInfo("third_party_package.A", 10110005,
"third_party_shared_package")));
List<StatsEvent> events = new ArrayList<>();
assertWithMessage("Stats pull atom callback status")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
List<AtomsProto.CarWatchdogUidIoUsageSummary> expectedSummaries =
verifyAndGetUidIoUsageSummaries(
mTimeSource.getCurrentDate().minus(RETENTION_PERIOD),
/* expectUids= */ Arrays.asList(10010001, 10110004, 10110005));
assertWithMessage("First pulled uid I/O usage summary atoms")
.that(mPulledUidIoUsageSummaries).containsExactlyElementsIn(expectedSummaries);
mPulledUidIoUsageSummaries.clear();
restartService(/* totalRestarts= */ 1, /* wantedDbWrites= */ 0);
assertWithMessage("Status of stats pull atom callback after restart")
.that(mStatsPullAtomCallback.onPullAtom(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY,
events)).isEqualTo(PULL_SUCCESS);
assertWithMessage("Pulled uid I/O usage summary atoms after restart")
.that(mPulledUidIoUsageSummaries).isEmpty();
verifyNoMoreInteractions(mSpiedWatchdogStorage);
}
@Test
public void testPullInvalidAtoms() throws Exception {
List<StatsEvent> actualEvents = new ArrayList<>();
assertWithMessage("Stats pull atom callback status").that(mStatsPullAtomCallback.onPullAtom(
0, actualEvents)).isEqualTo(PULL_SKIP);
assertWithMessage("Pulled stats events").that(actualEvents).isEmpty();
}
@Test
public void testProcessPackageChangedIntentForEnabledPackage() throws Exception {
String packageName = "system_package";
int userId = 100;
disableUserPackage("system_package", 100, 101);
disableUserPackage("vendor_package", 100);
disableUserPackage("third_party_package", 100);
doReturn(COMPONENT_ENABLED_STATE_ENABLED).when(mSpiedPackageManager)
.getApplicationEnabledSetting(or(eq("system_package"),
eq("irrelevant_random_package")), eq(100));
mWatchdogPerfHandler.processPackageChangedIntent(new Intent(ACTION_PACKAGE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, userId)
.setData(Uri.parse("package:" + packageName)));
mWatchdogPerfHandler.processPackageChangedIntent(new Intent(ACTION_PACKAGE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, userId)
.setData(Uri.parse("package:irrelevant_random_package")));
verifyDisabledPackagesSettingsKey(
/* message= */ " after enabling system_package for user 100",
/* userPackagesCsv= */
"100:vendor_package,100:third_party_package,101:system_package");
}
@Test
public void testProcessPackageChangedIntentForDisabledPackage() throws Exception {
String packageName = "system_package";
int userId = 100;
disableUserPackage("system_package", 100, 101);
disableUserPackage("vendor_package", 100);
doReturn(COMPONENT_ENABLED_STATE_DISABLED).when(mSpiedPackageManager)
.getApplicationEnabledSetting("system_package", 100);
mWatchdogPerfHandler.processPackageChangedIntent(new Intent(ACTION_PACKAGE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, userId)
.setData(Uri.parse("package:" + packageName)));
verifyDisabledPackagesSettingsKey(
/* message= */ "",
/* userPackagesCsv= */ "100:vendor_package,100:system_package,101:system_package");
}
private List<AtomsProto.CarWatchdogSystemIoUsageSummary> verifyAndGetSystemIoUsageSummaries(
ZonedDateTime beginReportDate) {
ZonedDateTime beginWeekStartDate = beginReportDate.with(ChronoField.DAY_OF_WEEK, 1);
ZonedDateTime endWeekStartDate = mTimeSource.getCurrentDate()
.with(ChronoField.DAY_OF_WEEK, 1);
List<AtomsProto.CarWatchdogSystemIoUsageSummary> expectedSummaries = new ArrayList<>();
while (!beginWeekStartDate.equals(endWeekStartDate)) {
long startEpochSecond = beginWeekStartDate.toEpochSecond();
verify(mSpiedWatchdogStorage).getDailySystemIoUsageSummaries(
IO_USAGE_SUMMARY_MIN_SYSTEM_TOTAL_WRITTEN_BYTES, startEpochSecond,
beginWeekStartDate.plusWeeks(1).toEpochSecond());
expectedSummaries.add(AtomsProto.CarWatchdogSystemIoUsageSummary.newBuilder()
.setIoUsageSummary(constructCarWatchdogIoUsageSummary(
sampleDailyIoUsageSummariesForAWeek(startEpochSecond,
SYSTEM_DAILY_IO_USAGE_SUMMARY_MULTIPLIER)))
.setStartTimeMillis(startEpochSecond * 1000)
.build());
beginWeekStartDate = beginWeekStartDate.plusWeeks(1);
}
return expectedSummaries;
}
private void mockBuildStatsEventCalls() {
when(CarStatsLog.buildStatsEvent(eq(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY),
any(byte[].class), anyLong())).thenAnswer(args -> {
mPulledSystemIoUsageSummaries.add(AtomsProto.CarWatchdogSystemIoUsageSummary
.newBuilder()
.setIoUsageSummary(AtomsProto.CarWatchdogIoUsageSummary.parseFrom(
(byte[]) args.getArgument(1)))
.setStartTimeMillis(args.getArgument(2))
.build());
// Returned event is not used in tests, so return an empty event.
return StatsEvent.newBuilder().build();
});
when(CarStatsLog.buildStatsEvent(eq(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY), anyInt(),
any(byte[].class), anyLong())).thenAnswer(args -> {
mPulledUidIoUsageSummaries.add(AtomsProto.CarWatchdogUidIoUsageSummary
.newBuilder()
.setUid(args.getArgument(1))
.setIoUsageSummary(AtomsProto.CarWatchdogIoUsageSummary.parseFrom(
(byte[]) args.getArgument(2)))
.setStartTimeMillis(args.getArgument(3))
.build());
// Returned event is not used in tests, so return an empty event.
return StatsEvent.newBuilder().build();
});
}
private List<AtomsProto.CarWatchdogUidIoUsageSummary> verifyAndGetUidIoUsageSummaries(
ZonedDateTime beginReportDate, List<Integer> expectUids) {
ZonedDateTime beginWeekStartDate = beginReportDate.with(ChronoField.DAY_OF_WEEK, 1);
ZonedDateTime endWeekStartDate = mTimeSource.getCurrentDate()
.with(ChronoField.DAY_OF_WEEK, 1);
List<AtomsProto.CarWatchdogUidIoUsageSummary> expectedSummaries = new ArrayList<>();
while (!beginWeekStartDate.equals(endWeekStartDate)) {
long startEpochSecond = beginWeekStartDate.toEpochSecond();
verify(mSpiedWatchdogStorage).getTopUsersDailyIoUsageSummaries(
UID_IO_USAGE_SUMMARY_TOP_COUNT * 2,
IO_USAGE_SUMMARY_MIN_SYSTEM_TOTAL_WRITTEN_BYTES, startEpochSecond,
beginWeekStartDate.plusWeeks(1).toEpochSecond());
for (Integer uid : expectUids) {
expectedSummaries.add(AtomsProto.CarWatchdogUidIoUsageSummary.newBuilder()
.setUid(uid)
.setIoUsageSummary(constructCarWatchdogIoUsageSummary(
sampleDailyIoUsageSummariesForAWeek(startEpochSecond, uid)))
.setStartTimeMillis(startEpochSecond * 1000)
.build());
}
beginWeekStartDate = beginWeekStartDate.plusWeeks(1);
}
return expectedSummaries;
}
private static AtomsProto.CarWatchdogIoUsageSummary constructCarWatchdogIoUsageSummary(
List<AtomsProto.CarWatchdogDailyIoUsageSummary> dailySummaries) {
return AtomsProto.CarWatchdogIoUsageSummary.newBuilder()
.setEventTimePeriod(AtomsProto.CarWatchdogEventTimePeriod.newBuilder()
.setPeriod(AtomsProto.CarWatchdogEventTimePeriod.Period.WEEKLY).build())
.addAllDailyIoUsageSummary(dailySummaries)
.build();
}
private static SparseArray<String> constructPackagesByNotificationId(int idOffset,
String... packages) {
SparseArray<String> packagesById = new SparseArray<>();
idOffset = idOffset < 0 ? 0 : idOffset % RESOURCE_OVERUSE_NOTIFICATION_MAX_OFFSET;
for (String packageName : packages) {
packagesById.put(RESOURCE_OVERUSE_NOTIFICATION_BASE_ID + idOffset, packageName);
idOffset = ++idOffset % RESOURCE_OVERUSE_NOTIFICATION_MAX_OFFSET;
}
return packagesById;
}
private void captureAndVerifyUserNotifications(
List<UserNotificationReflectionCall> expectedNotifications) {
// Recurring overuse notification handling task is posted on a separate handler thread and
// this task sends the user notifications. Wait for this task to complete.
CarServiceUtils.runEmptyRunnableOnLooperSync(CarWatchdogService.class.getSimpleName());
verify(mMockNotificationHelper, times(expectedNotifications.size()))
.showResourceOveruseNotificationsAsUser(mUserHandleCaptor.capture(),
mHeadsUpPackagesCaptor.capture(),
mNotificationCenterPackagesCaptor.capture());
if (expectedNotifications.isEmpty()) {
return;
}
assertWithMessage("Number of notification does not match").that(
mUserHandleCaptor.getAllValues().size()).isEqualTo(expectedNotifications.size());
for (int i = 0; i < expectedNotifications.size(); i++) {
UserNotificationReflectionCall expectedNotification =
expectedNotifications.get(i);
UserHandle userHandle = mUserHandleCaptor.getAllValues().get(i);
SparseArray<String> actualHeadsUpNotificationPackagesById = mHeadsUpPackagesCaptor
.getAllValues().get(i);
SparseArray<String> actualPackagesById = mNotificationCenterPackagesCaptor
.getAllValues().get(i);
assertWithMessage("Current user id for resource overuse notifications")
.that(userHandle).isEqualTo(expectedNotification.userHandle);
int expectedHeadsUpSize = expectedNotification.hasHeadsUpNotification ? 1 : 0;
assertWithMessage("Resource overuse heads up packages size")
.that(actualHeadsUpNotificationPackagesById.size())
.isEqualTo(expectedHeadsUpSize);
if (expectedNotification.hasHeadsUpNotification) {
int headsUpNotificationId = actualHeadsUpNotificationPackagesById.keyAt(0);
actualPackagesById.put(headsUpNotificationId,
actualHeadsUpNotificationPackagesById.valueAt(0));
}
int expectedSize = expectedNotification.packagesById.size();
assertWithMessage("Resource overuse notification size")
.that(actualPackagesById.size()).isEqualTo(expectedSize);
ArraySet<String> expectedPackages = new ArraySet<>(expectedSize);
ArraySet<String> actualPackages = new ArraySet<>(expectedSize);
for (int j = 0; j < expectedNotification.packagesById.size(); j++) {
int expectedNotificationId = expectedNotification.packagesById.keyAt(j);
assertWithMessage("Resource overuse notification id")
.that(actualPackagesById.get(expectedNotificationId)).isNotNull();
expectedPackages.add(expectedNotification.packagesById.valueAt(j));
actualPackages.add(actualPackagesById.valueAt(j));
}
assertWithMessage("Resource overuse notification package names")
.that(actualPackages).isEqualTo(expectedPackages);
}
}
private static void verifyOnOveruseCalled(List<ResourceOveruseStats> expectedStats,
IResourceOveruseListener mockListener) throws Exception {
ArgumentCaptor<ResourceOveruseStats> resourceOveruseStatsCaptor =
ArgumentCaptor.forClass(ResourceOveruseStats.class);
verify(mockListener, times(expectedStats.size()))
.onOveruse(resourceOveruseStatsCaptor.capture());
ResourceOveruseStatsSubject.assertThat(resourceOveruseStatsCaptor.getAllValues())
.containsExactlyElementsIn(expectedStats);
}
private void setupUsers() {
when(mMockContext.getSystemService(UserManager.class)).thenReturn(mMockUserManager);
mockUmGetAllUsers(mMockUserManager, new UserHandle[0]);
}
private void initService(int wantedInvocations) throws Exception {
mWatchdogPerfHandler.setOveruseHandlingDelay(OVERUSE_HANDLING_DELAY_MILLS);
mWatchdogPerfHandler.init();
captureCarUxRestrictionsChangeListener(wantedInvocations);
verifyDatabaseInit(wantedInvocations);
captureStatsPullAtomCallback(wantedInvocations);
mWatchdogPerfHandler.onDaemonConnectionChange(/* isConnected= */ true);
}
private void restartService(int totalRestarts, int wantedDbWrites) throws Exception {
restartService(totalRestarts, wantedDbWrites, /* isWriteIoStats= */ true);
}
private void restartService(int totalRestarts, int wantedDbWrites, boolean isWriteIoStats)
throws Exception {
mWatchdogPerfHandler.writeMetadataFile();
mWatchdogPerfHandler.writeToDatabase();
mWatchdogPerfHandler.release();
mSpiedWatchdogStorage.release();
verify(mSpiedWatchdogStorage, times(totalRestarts)).startWrite();
verify(mSpiedWatchdogStorage, times(isWriteIoStats ? wantedDbWrites : 0))
.saveIoUsageStats(any());
verify(mSpiedWatchdogStorage, times(wantedDbWrites)).saveUserPackageSettings(any());
verify(mSpiedWatchdogStorage, times(wantedDbWrites)).markWriteSuccessful();
verify(mSpiedWatchdogStorage, times(wantedDbWrites)).endWrite();
verify(mSpiedWatchdogStorage, times(Math.max(totalRestarts, 1))).release();
mWatchdogPerfHandler = new WatchdogPerfHandler(mMockContext,
mMockBuiltinPackageContext, mMockCarWatchdogDaemonHelper,
new PackageInfoHandler(mMockContext.getPackageManager()),
mSpiedWatchdogStorage, mTimeSource);
initService(/* wantedInvocations= */ totalRestarts + 1);
}
private void setRequiresDistractionOptimization(boolean isRequires) throws Exception {
CarUxRestrictions.Builder builder = new CarUxRestrictions.Builder(
isRequires, UX_RESTRICTIONS_BASELINE, /* time= */ 0);
mCarUxRestrictionsChangeListener.onUxRestrictionsChanged(builder.build());
}
private List<PackageIoOveruseStats> sampleIoOveruseStats(boolean requireRecurrentOveruseStats)
throws Exception {
int[] users = new int[]{100, 101};
int totalOveruses = requireRecurrentOveruseStats ? RECURRING_OVERUSE_TIMES + 1 : 1;
List<PackageIoOveruseStats> packageIoOveruseStats = new ArrayList<>();
android.automotive.watchdog.PerStateBytes zeroRemainingBytes =
constructPerStateBytes(0, 0, 0);
android.automotive.watchdog.PerStateBytes nonZeroRemainingBytes =
constructPerStateBytes(20, 30, 40);
android.automotive.watchdog.PerStateBytes writtenBytes =
constructPerStateBytes(100, 200, 300);
for (int i = 0; i < users.length; ++i) {
// Overuse occurred but cannot be killed/disabled.
packageIoOveruseStats.add(constructPackageIoOveruseStats(
UserHandle.getUid(users[i], 10001), /* shouldNotify= */ true,
/* forgivenWriteBytes= */ writtenBytes,
constructInternalIoOveruseStats(
/* killableOnOveruse= */ false, zeroRemainingBytes, writtenBytes,
totalOveruses)));
// No overuse occurred but the package should be notified.
packageIoOveruseStats.add(constructPackageIoOveruseStats(
UserHandle.getUid(users[i], 10002), /* shouldNotify= */ true,
/* forgivenWriteBytes= */ constructPerStateBytes(0, 0, 0),
constructInternalIoOveruseStats(
/* killableOnOveruse= */ true, nonZeroRemainingBytes, writtenBytes,
totalOveruses)));
// Neither overuse occurred nor be notified.
packageIoOveruseStats.add(constructPackageIoOveruseStats(
UserHandle.getUid(users[i], 10003), /* shouldNotify= */ false,
/* forgivenWriteBytes= */ constructPerStateBytes(0, 0, 0),
constructInternalIoOveruseStats(
/* killableOnOveruse= */ false, nonZeroRemainingBytes, writtenBytes,
totalOveruses)));
// Overuse occurred and can be killed/disabled.
packageIoOveruseStats.add(constructPackageIoOveruseStats(
UserHandle.getUid(users[i], 10004), /* shouldNotify= */ false,
/* forgivenWriteBytes= */ writtenBytes,
constructInternalIoOveruseStats(
/* killableOnOveruse= */ true, zeroRemainingBytes, writtenBytes,
totalOveruses)));
// Overuse occurred and can be killed/disabled.
packageIoOveruseStats.add(constructPackageIoOveruseStats(
UserHandle.getUid(users[i], 10005), /* shouldNotify= */ true,
/* forgivenWriteBytes= */ writtenBytes,
constructInternalIoOveruseStats(
/* killableOnOveruse= */ true, zeroRemainingBytes, writtenBytes,
totalOveruses)));
}
return packageIoOveruseStats;
}
private void setUpSampleUserAndPackages() {
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100, 101);
int[] users = new int[]{100, 101};
List<android.content.pm.PackageInfo> packageInfos = new ArrayList<>();
for (int i = 0; i < users.length; ++i) {
packageInfos.add(constructPackageManagerPackageInfo(
"system_package.critical", UserHandle.getUid(users[i], 10001), null));
packageInfos.add(constructPackageManagerPackageInfo(
"system_package.non_critical", UserHandle.getUid(users[i], 10002), null));
packageInfos.add(constructPackageManagerPackageInfo(
"vendor_package.critical", UserHandle.getUid(users[i], 10003), null));
packageInfos.add(constructPackageManagerPackageInfo(
"vendor_package.non_critical", UserHandle.getUid(users[i], 10004), null));
packageInfos.add(constructPackageManagerPackageInfo(
"third_party_package.A", UserHandle.getUid(users[i], 10005),
"third_party_shared_package"));
packageInfos.add(constructPackageManagerPackageInfo(
"third_party_package.B", UserHandle.getUid(users[i], 10005),
"third_party_shared_package"));
}
injectPackageInfos(packageInfos);
}
private static List<AtomsProto.CarWatchdogIoOveruseStatsReported> sampleReportedOveruseStats() {
// The below thresholds are from {@link sampleInternalResourceOveruseConfiguration} and
// UID/stat are from {@link sampleIoOveruseStats}.
AtomsProto.CarWatchdogPerStateBytes systemThreshold =
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(10, 20, 30);
AtomsProto.CarWatchdogPerStateBytes vendorThreshold =
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(20, 40, 60);
AtomsProto.CarWatchdogPerStateBytes thirdPartyThreshold =
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(30, 60, 90);
AtomsProto.CarWatchdogPerStateBytes writtenBytes =
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(100, 200, 300);
List<AtomsProto.CarWatchdogIoOveruseStatsReported> reportedOveruseStats = new ArrayList<>();
reportedOveruseStats.add(constructIoOveruseStatsReported(
10010001, systemThreshold, writtenBytes));
reportedOveruseStats.add(constructIoOveruseStatsReported(
10110001, systemThreshold, writtenBytes));
reportedOveruseStats.add(constructIoOveruseStatsReported(
10010004, vendorThreshold, writtenBytes));
reportedOveruseStats.add(constructIoOveruseStatsReported(
10110004, vendorThreshold, writtenBytes));
reportedOveruseStats.add(constructIoOveruseStatsReported(
10010005, thirdPartyThreshold, writtenBytes));
reportedOveruseStats.add(constructIoOveruseStatsReported(
10110005, thirdPartyThreshold, writtenBytes));
return reportedOveruseStats;
}
private static List<AtomsProto.CarWatchdogKillStatsReported> sampleReportedKillStats(
int systemState, int[] killedUids) {
// The below thresholds are from {@link sampleInternalResourceOveruseConfiguration} and
// UID/stat are from {@link sampleIoOveruseStats}.
AtomsProto.CarWatchdogPerStateBytes vendorThreshold =
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(20, 40, 60);
AtomsProto.CarWatchdogPerStateBytes thirdPartyThreshold =
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(30, 60, 90);
AtomsProto.CarWatchdogPerStateBytes writtenBytes =
WatchdogPerfHandler.constructCarWatchdogPerStateBytes(100, 200, 300);
List<AtomsProto.CarWatchdogKillStatsReported> reportedKillStats = new ArrayList<>();
for (int uid : killedUids) {
AtomsProto.CarWatchdogPerStateBytes threshold =
UserHandle.getAppId(uid) == 10004 ? vendorThreshold : thirdPartyThreshold;
reportedKillStats.add(constructIoOveruseKillStatsReported(
uid, systemState, threshold, writtenBytes));
}
return reportedKillStats;
}
private static AtomsProto.CarWatchdogIoOveruseStatsReported
constructIoOveruseStatsReported(int uid, AtomsProto.CarWatchdogPerStateBytes threshold,
AtomsProto.CarWatchdogPerStateBytes writtenBytes) {
return constructCarWatchdogIoOveruseStatsReported(
uid, WatchdogPerfHandler.constructCarWatchdogIoOveruseStats(
AtomsProto.CarWatchdogIoOveruseStats.Period.DAILY, threshold, writtenBytes)
);
}
private void captureAndVerifyIoOveruseStatsReported(
List<AtomsProto.CarWatchdogIoOveruseStatsReported> expected) throws Exception {
verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_WATCHDOG_IO_OVERUSE_STATS_REPORTED),
mOverusingUidCaptor.capture(), mOveruseStatsCaptor.capture()),
times(expected.size()));
List<Integer> allUidValues = mOverusingUidCaptor.getAllValues();
List<byte[]> allOveruseStatsValues = mOveruseStatsCaptor.getAllValues();
List<AtomsProto.CarWatchdogIoOveruseStatsReported> actual = new ArrayList<>();
for (int i = 0; i < expected.size(); ++i) {
actual.add(constructCarWatchdogIoOveruseStatsReported(allUidValues.get(i),
AtomsProto.CarWatchdogIoOveruseStats.parseFrom(allOveruseStatsValues.get(i))));
}
assertWithMessage("I/O overuse stats reported to statsd").that(actual)
.containsExactlyElementsIn(expected);
}
private void captureAndVerifyKillStatsReported(
List<AtomsProto.CarWatchdogKillStatsReported> expected) throws Exception {
// Overuse handling task is posted on the main thread and this task performs disabling and
// uploading metrics. Wait for this task to complete.
CarServiceUtils.runOnMainSync(() -> {});
verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_WATCHDOG_KILL_STATS_REPORTED),
mKilledUidCaptor.capture(), mUidStateCaptor.capture(),
mSystemStateCaptor.capture(), mKillReasonCaptor.capture(), eq(null),
mKilledStatsCaptor.capture()), times(expected.size()));
List<Integer> allUidValues = mKilledUidCaptor.getAllValues();
List<Integer> allUidStateValues = mUidStateCaptor.getAllValues();
List<Integer> allSystemStateValues = mSystemStateCaptor.getAllValues();
List<Integer> allKillReasonValues = mKillReasonCaptor.getAllValues();
List<byte[]> allIoOveruseStatsValues = mKilledStatsCaptor.getAllValues();
List<AtomsProto.CarWatchdogKillStatsReported> actual = new ArrayList<>();
for (int i = 0; i < expected.size(); ++i) {
actual.add(constructCarWatchdogKillStatsReported(allUidValues.get(i),
allUidStateValues.get(i), allSystemStateValues.get(i),
allKillReasonValues.get(i),
AtomsProto.CarWatchdogIoOveruseStats.parseFrom(
allIoOveruseStatsValues.get(i))));
}
assertWithMessage("I/O overuse kill stats reported to statsd").that(actual)
.containsExactlyElementsIn(expected);
}
private static AtomsProto.CarWatchdogIoOveruseStatsReported
constructCarWatchdogIoOveruseStatsReported(
int uid, AtomsProto.CarWatchdogIoOveruseStats ioOveruseStats) {
return AtomsProto.CarWatchdogIoOveruseStatsReported.newBuilder()
.setUid(uid)
.setIoOveruseStats(ioOveruseStats)
.build();
}
private static AtomsProto.CarWatchdogKillStatsReported constructIoOveruseKillStatsReported(
int uid, int systemState, AtomsProto.CarWatchdogPerStateBytes threshold,
AtomsProto.CarWatchdogPerStateBytes writtenBytes) {
return constructCarWatchdogKillStatsReported(uid,
CAR_WATCHDOG_KILL_STATS_REPORTED__UID_STATE__UNKNOWN_UID_STATE, systemState,
CAR_WATCHDOG_KILL_STATS_REPORTED__KILL_REASON__KILLED_ON_IO_OVERUSE,
WatchdogPerfHandler.constructCarWatchdogIoOveruseStats(
AtomsProto.CarWatchdogIoOveruseStats.Period.DAILY, threshold, writtenBytes)
);
}
private static AtomsProto.CarWatchdogKillStatsReported constructCarWatchdogKillStatsReported(
int uid, int uidState, int systemState, int killReason,
AtomsProto.CarWatchdogIoOveruseStats ioOveruseStats) {
return AtomsProto.CarWatchdogKillStatsReported.newBuilder()
.setUid(uid)
.setUidState(AtomsProto.CarWatchdogKillStatsReported.UidState.forNumber(uidState))
.setSystemState(AtomsProto.CarWatchdogKillStatsReported.SystemState.forNumber(
systemState))
.setKillReason(AtomsProto.CarWatchdogKillStatsReported.KillReason.forNumber(
killReason))
.setIoOveruseStats(ioOveruseStats)
.build();
}
private void enableUserPackage(String packageName, int userId, boolean isKillable)
throws Exception {
UserHandle userHandle = UserHandle.of(userId);
// Set package killable state to not killable, which enable the user package
mWatchdogPerfHandler.setKillablePackageAsUser(packageName, userHandle,
/* isKillable= */ false);
if (isKillable) {
mWatchdogPerfHandler.setKillablePackageAsUser(packageName, userHandle,
/* isKillable= */ true);
}
verify(mSpiedPackageManager, atLeastOnce())
.getApplicationEnabledSetting(packageName, userId);
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq(packageName),
eq(COMPONENT_ENABLED_STATE_ENABLED), eq(0),
eq(userId), anyString());
assertThat(mDisabledUserPackages).doesNotContain(userId + ":" + packageName);
doReturn(COMPONENT_ENABLED_STATE_ENABLED).when(mSpiedPackageManager)
.getApplicationEnabledSetting(eq(packageName), eq(userId));
}
private void disableUserPackage(String packageName, int... userIds) throws Exception {
for (int i = 0; i < userIds.length; i++) {
int userId = userIds[i];
mWatchdogPerfHandler.disablePackageForUser(packageName, userId);
verify(mSpiedPackageManager, atLeastOnce())
.getApplicationEnabledSetting(packageName, userId);
verify(mSpiedPackageManager).setApplicationEnabledSetting(eq(packageName),
eq(COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED), eq(0),
eq(userId), anyString());
assertThat(mDisabledUserPackages).contains(userId + ":" + packageName);
doReturn(COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED).when(mSpiedPackageManager)
.getApplicationEnabledSetting(eq(packageName), eq(userId));
}
}
static android.content.pm.PackageInfo constructPackageManagerPackageInfo(
String packageName, int uid, String sharedUserId) {
if (packageName.startsWith("system")) {
return constructPackageManagerPackageInfo(
packageName, uid, sharedUserId, ApplicationInfo.FLAG_SYSTEM, 0);
}
if (packageName.startsWith("vendor")) {
return constructPackageManagerPackageInfo(
packageName, uid, sharedUserId, ApplicationInfo.FLAG_SYSTEM,
ApplicationInfo.PRIVATE_FLAG_OEM);
}
return constructPackageManagerPackageInfo(packageName, uid, sharedUserId, 0, 0);
}
private static android.content.pm.PackageInfo constructPackageManagerPackageInfo(
String packageName, int uid, String sharedUserId, int flags, int privateFlags) {
android.content.pm.PackageInfo packageInfo = new android.content.pm.PackageInfo();
packageInfo.packageName = packageName;
packageInfo.sharedUserId = sharedUserId;
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.packageName = packageName;
packageInfo.applicationInfo.uid = uid;
packageInfo.applicationInfo.flags = flags;
packageInfo.applicationInfo.privateFlags = privateFlags;
return packageInfo;
}
private void injectPackageInfos(List<android.content.pm.PackageInfo> packageInfos) {
for (android.content.pm.PackageInfo packageInfo : packageInfos) {
String genericPackageName = packageInfo.packageName;
int uid = packageInfo.applicationInfo.uid;
int userId = UserHandle.getUserId(uid);
if (packageInfo.sharedUserId != null) {
genericPackageName =
PackageInfoHandler.SHARED_PACKAGE_PREFIX + packageInfo.sharedUserId;
List<String> packages = mPackagesBySharedUid.get(uid);
if (packages == null) {
packages = new ArrayList<>();
}
packages.add(packageInfo.packageName);
mPackagesBySharedUid.put(uid, packages);
}
String userPackageId = userId + USER_PACKAGE_SEPARATOR + packageInfo.packageName;
assertWithMessage("Duplicate package infos provided for user package id: %s",
userPackageId).that(mPmPackageInfoByUserPackage.containsKey(userPackageId))
.isFalse();
assertWithMessage("Mismatch generic package names for the same uid '%s'",
uid).that(mGenericPackageNameByUid.get(uid, genericPackageName))
.isEqualTo(genericPackageName);
mPmPackageInfoByUserPackage.put(userPackageId, packageInfo);
mGenericPackageNameByUid.put(uid, genericPackageName);
}
}
private void verifyDisabledPackages(String userPackagesCsv) {
verifyDisabledPackages(/* message= */ "", userPackagesCsv);
}
private void verifyDisabledPackages(String message, String userPackagesCsv) {
assertWithMessage("Disabled user packages %s", message).that(mDisabledUserPackages)
.containsExactlyElementsIn(userPackagesCsv.split(","));
verifyDisabledPackagesSettingsKey(message, userPackagesCsv);
}
private void verifyDisabledPackagesSettingsKey(String message, String userPackagesCsv) {
List<String> userPackagesFromSettingsString = new ArrayList<>();
for (int i = 0; i < mDisabledPackagesSettingsStringByUserid.size(); ++i) {
int userId = mDisabledPackagesSettingsStringByUserid.keyAt(i);
String value = mDisabledPackagesSettingsStringByUserid.valueAt(i);
List<String> packages = TextUtils.isEmpty(value) ? new ArrayList<>()
: new ArrayList<>(Arrays.asList(value.split(
PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR)));
packages.forEach(element ->
userPackagesFromSettingsString.add(userId + USER_PACKAGE_SEPARATOR + element));
}
assertWithMessage(
"KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE settings string user packages %s",
message).that(userPackagesFromSettingsString)
.containsExactlyElementsIn(userPackagesCsv.split(","));
}
private void verifyNoDisabledPackages() {
verifyNoDisabledPackages(/* message= */ "");
}
private void verifyNoDisabledPackages(String message) {
assertWithMessage("Disabled user packages %s", message).that(mDisabledUserPackages)
.isEmpty();
assertWithMessage(
"KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE settings string user packages %s",
message).that(mDisabledPackagesSettingsStringByUserid.size()).isEqualTo(0);
}
private void mockSettingsStringCalls() {
doAnswer(args -> {
ContentResolver contentResolver = mock(ContentResolver.class);
when(contentResolver.getUserId()).thenReturn(args.getArgument(1));
return contentResolver;
}).when(() -> CarServiceUtils.getContentResolverForUser(any(), anyInt()));
when(Settings.Secure.getString(any(ContentResolver.class),
eq(KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE))).thenAnswer(
args -> {
ContentResolver contentResolver = args.getArgument(0);
int userId = contentResolver.getUserId();
return mDisabledPackagesSettingsStringByUserid.get(userId);
});
// Use any() instead of anyString() to consider when string arg is null.
when(Settings.Secure.putString(any(ContentResolver.class),
eq(KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE), any())).thenAnswer(args -> {
ContentResolver contentResolver = args.getArgument(0);
int userId = contentResolver.getUserId();
String packageSettings = args.getArgument(2);
if (packageSettings == null) {
mDisabledPackagesSettingsStringByUserid.remove(userId);
} else {
mDisabledPackagesSettingsStringByUserid.put(userId, args.getArgument(2));
}
return null;
});
}
// TODO(b/262301082): Move to PackageInfoHandlerUnitTest.
private void mockPackageManager() throws Exception {
when(mMockPackageManager.getNamesForUids(any())).thenAnswer(args -> {
int[] uids = args.getArgument(0);
String[] names = new String[uids.length];
for (int i = 0; i < uids.length; ++i) {
names[i] = mGenericPackageNameByUid.get(uids[i], null);
}
return names;
});
when(mMockPackageManager.getPackagesForUid(anyInt())).thenAnswer(args -> {
int uid = args.getArgument(0);
List<String> packages = mPackagesBySharedUid.get(uid);
return packages.toArray(new String[0]);
});
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
.thenAnswer(args -> {
int userId = ((UserHandle) args.getArgument(2)).getIdentifier();
String userPackageId = userId + USER_PACKAGE_SEPARATOR + args.getArgument(0);
android.content.pm.PackageInfo packageInfo =
mPmPackageInfoByUserPackage.get(userPackageId);
if (packageInfo == null) {
throw new PackageManager.NameNotFoundException(
"User package id '" + userPackageId + "' not found");
}
return packageInfo.applicationInfo;
});
when(mMockPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt()))
.thenAnswer(args -> {
String userPackageId = args.getArgument(2) + USER_PACKAGE_SEPARATOR
+ args.getArgument(0);
android.content.pm.PackageInfo packageInfo =
mPmPackageInfoByUserPackage.get(userPackageId);
if (packageInfo == null) {
throw new PackageManager.NameNotFoundException(
"User package id '" + userPackageId + "' not found");
}
return packageInfo;
});
when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
.thenAnswer(args -> {
int userId = args.getArgument(1);
List<android.content.pm.PackageInfo> packageInfos = new ArrayList<>();
for (android.content.pm.PackageInfo packageInfo :
mPmPackageInfoByUserPackage.values()) {
if (UserHandle.getUserId(packageInfo.applicationInfo.uid) == userId) {
packageInfos.add(packageInfo);
}
}
return packageInfos;
});
when(mMockPackageManager.getPackageUidAsUser(anyString(), anyInt()))
.thenAnswer(args -> {
String userPackageId = args.getArgument(1) + USER_PACKAGE_SEPARATOR
+ args.getArgument(0);
android.content.pm.PackageInfo packageInfo =
mPmPackageInfoByUserPackage.get(userPackageId);
if (packageInfo == null) {
throw new PackageManager.NameNotFoundException(
"User package id '" + userPackageId + "' not found");
}
return packageInfo.applicationInfo.uid;
});
doAnswer((args) -> {
String value = args.getArgument(3) + USER_PACKAGE_SEPARATOR
+ args.getArgument(0);
mDisabledUserPackages.add(value);
return null;
}).when(mSpiedPackageManager).setApplicationEnabledSetting(
anyString(), eq(COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED), anyInt(),
anyInt(), anyString());
doAnswer((args) -> {
String value = args.getArgument(3) + USER_PACKAGE_SEPARATOR
+ args.getArgument(0);
mDisabledUserPackages.remove(value);
return null;
}).when(mSpiedPackageManager).setApplicationEnabledSetting(
anyString(), eq(COMPONENT_ENABLED_STATE_ENABLED), anyInt(),
anyInt(), anyString());
doReturn(COMPONENT_ENABLED_STATE_ENABLED).when(mSpiedPackageManager)
.getApplicationEnabledSetting(anyString(), anyInt());
}
private void mockWatchdogStorage() {
doAnswer((args) -> {
mUserPackageSettingsEntries.addAll(args.getArgument(0));
return true;
}).when(mSpiedWatchdogStorage).saveUserPackageSettings(any());
doAnswer((args) -> {
List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = args.getArgument(0);
for (WatchdogStorage.IoUsageStatsEntry entry : ioUsageStatsEntries) {
mIoUsageStatsEntries.add(
new WatchdogStorage.IoUsageStatsEntry(entry.userId, entry.packageName,
new WatchdogPerfHandler.PackageIoUsage(
entry.ioUsage.getInternalIoOveruseStats(),
entry.ioUsage.getForgivenWriteBytes(),
entry.ioUsage.getForgivenOveruses(),
entry.ioUsage.getTotalTimesKilled())));
}
return ioUsageStatsEntries.size();
}).when(mSpiedWatchdogStorage).saveIoUsageStats(any());
doAnswer((args) -> {
List<WatchdogStorage.UserPackageSettingsEntry> entries =
new ArrayList<>(mUserPackageSettingsEntries.size());
entries.addAll(mUserPackageSettingsEntries);
return entries;
}).when(mSpiedWatchdogStorage).getUserPackageSettings();
doReturn(mIoUsageStatsEntries).when(mSpiedWatchdogStorage).getTodayIoUsageStats();
doReturn(List.of()).when(mSpiedWatchdogStorage)
.getNotForgivenHistoricalIoOveruses(RECURRING_OVERUSE_PERIOD_IN_DAYS);
doAnswer(args -> sampleDailyIoUsageSummariesForAWeek(args.getArgument(1),
SYSTEM_DAILY_IO_USAGE_SUMMARY_MULTIPLIER))
.when(mSpiedWatchdogStorage)
.getDailySystemIoUsageSummaries(anyLong(), anyLong(), anyLong());
doAnswer(args -> {
ArrayList<WatchdogStorage.UserPackageDailySummaries> summaries =
new ArrayList<>();
for (int i = 0; i < mGenericPackageNameByUid.size(); ++i) {
int uid = mGenericPackageNameByUid.keyAt(i);
summaries.add(new WatchdogStorage.UserPackageDailySummaries(
UserHandle.getUserId(uid), mGenericPackageNameByUid.valueAt(i),
sampleDailyIoUsageSummariesForAWeek(args.getArgument(2),
/* sysOrUidMultiplier= */ uid)));
}
summaries.sort(Comparator.comparingLong(WatchdogStorage
.UserPackageDailySummaries::getTotalWrittenBytes).reversed());
return summaries;
}).when(mSpiedWatchdogStorage)
.getTopUsersDailyIoUsageSummaries(anyInt(), anyLong(), anyLong(), anyLong());
}
private List<AtomsProto.CarWatchdogDailyIoUsageSummary> sampleDailyIoUsageSummariesForAWeek(
long startEpochSeconds, long sysOrUidMultiplier) {
List<AtomsProto.CarWatchdogDailyIoUsageSummary> summaries = new ArrayList<>();
long weekMultiplier = ChronoUnit.WEEKS.between(
ZonedDateTime.ofInstant(Instant.ofEpochSecond(startEpochSeconds), ZONE_OFFSET),
mTimeSource.getCurrentDate());
for (int i = 1; i < 8; ++i) {
summaries.add(CarWatchdogServiceUnitTest.constructCarWatchdogDailyIoUsageSummary(
/* fgWrBytes= */ 100 * i * weekMultiplier * sysOrUidMultiplier,
/* bgWrBytes= */ 200 * i * weekMultiplier * sysOrUidMultiplier,
/* gmWrBytes= */ 300 * i * weekMultiplier * sysOrUidMultiplier,
/* overuseCount= */ 2 * i));
}
return summaries;
}
private static ResourceOveruseStats constructResourceOveruseStats(int uid, String packageName,
android.automotive.watchdog.IoOveruseStats internalIoOveruseStats) {
IoOveruseStats ioOveruseStats = WatchdogPerfHandler.toIoOveruseStatsBuilder(
internalIoOveruseStats, /* totalTimesKilled= */ 0,
internalIoOveruseStats.killableOnOveruse).build();
return new ResourceOveruseStats.Builder(packageName, UserHandle.getUserHandleForUid(uid))
.setIoOveruseStats(ioOveruseStats).build();
}
static List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
sampleInternalResourceOveruseConfigurations() {
return Arrays.asList(
sampleInternalResourceOveruseConfiguration(ComponentType.SYSTEM,
sampleInternalIoOveruseConfiguration(ComponentType.SYSTEM)),
sampleInternalResourceOveruseConfiguration(ComponentType.VENDOR,
sampleInternalIoOveruseConfiguration(ComponentType.VENDOR)),
sampleInternalResourceOveruseConfiguration(ComponentType.THIRD_PARTY,
sampleInternalIoOveruseConfiguration(ComponentType.THIRD_PARTY)));
}
private static android.automotive.watchdog.internal.ResourceOveruseConfiguration
sampleInternalResourceOveruseConfiguration(@ComponentType int componentType,
android.automotive.watchdog.internal.IoOveruseConfiguration ioOveruseConfig) {
String prefix = WatchdogPerfHandler.toComponentTypeStr(componentType).toLowerCase();
android.automotive.watchdog.internal.ResourceOveruseConfiguration config =
new android.automotive.watchdog.internal.ResourceOveruseConfiguration();
config.componentType = componentType;
config.safeToKillPackages = Arrays.asList(prefix + "_package.non_critical.A",
prefix + "_pkg.non_critical.B",
"shared:" + prefix + "_shared_package.non_critical.B",
"some_pkg_as_" + prefix + "_pkg");
config.vendorPackagePrefixes = Arrays.asList(
prefix + "_package", "some_pkg_as_" + prefix + "_pkg");
config.packageMetadata = Arrays.asList(
constructPackageMetadata("system_package.MEDIA", ApplicationCategoryType.MEDIA),
constructPackageMetadata("system_package.A", ApplicationCategoryType.MAPS),
constructPackageMetadata("vendor_package.MEDIA", ApplicationCategoryType.MEDIA),
constructPackageMetadata("vendor_package.A", ApplicationCategoryType.MAPS),
constructPackageMetadata("third_party_package.MAPS", ApplicationCategoryType.MAPS));
ResourceSpecificConfiguration resourceSpecificConfig = new ResourceSpecificConfiguration();
resourceSpecificConfig.setIoOveruseConfiguration(ioOveruseConfig);
config.resourceSpecificConfigurations = Collections.singletonList(resourceSpecificConfig);
return config;
}
private static PackageMetadata constructPackageMetadata(
String packageName, @ApplicationCategoryType int appCategoryType) {
PackageMetadata metadata = new PackageMetadata();
metadata.packageName = packageName;
metadata.appCategoryType = appCategoryType;
return metadata;
}
private static UserPackageIoUsageStats constructUserPackageIoUsageStats(
int userId, String packageName, android.automotive.watchdog.PerStateBytes writtenBytes,
android.automotive.watchdog.PerStateBytes forgivenWriteBytes, int totalOveruses) {
UserPackageIoUsageStats stats = new UserPackageIoUsageStats();
stats.userId = userId;
stats.packageName = packageName;
stats.ioUsageStats = new IoUsageStats();
stats.ioUsageStats.writtenBytes = writtenBytes;
stats.ioUsageStats.forgivenWriteBytes = forgivenWriteBytes;
stats.ioUsageStats.totalOveruses = totalOveruses;
return stats;
}
private static android.automotive.watchdog.internal.IoOveruseConfiguration
sampleInternalIoOveruseConfiguration(@ComponentType int componentType) {
String prefix = WatchdogPerfHandler.toComponentTypeStr(componentType).toLowerCase();
android.automotive.watchdog.internal.IoOveruseConfiguration config =
new android.automotive.watchdog.internal.IoOveruseConfiguration();
config.componentLevelThresholds = constructPerStateIoOveruseThreshold(
WatchdogPerfHandler.toComponentTypeStr(componentType),
/* fgBytes= */ componentType * 10L, /* bgBytes= */ componentType * 20L,
/*gmBytes= */ componentType * 30L);
config.packageSpecificThresholds = Collections.singletonList(
constructPerStateIoOveruseThreshold(prefix + "_package.A",
/* fgBytes= */ componentType * 40L, /* bgBytes= */ componentType * 50L,
/* gmBytes= */ componentType * 60L));
config.categorySpecificThresholds = Arrays.asList(
constructPerStateIoOveruseThreshold(
WatchdogPerfHandler.INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA,
/* fgBytes= */ componentType * 100L, /* bgBytes= */ componentType * 200L,
/* gmBytes= */ componentType * 300L),
constructPerStateIoOveruseThreshold(
WatchdogPerfHandler.INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS,
/* fgBytes= */ componentType * 1100L, /* bgBytes= */ componentType * 2200L,
/* gmBytes= */ componentType * 3300L));
config.systemWideThresholds = Collections.singletonList(
constructInternalIoOveruseAlertThreshold(
/* duration= */ componentType * 10L, /* writeBPS= */ componentType * 200L));
return config;
}
private static android.automotive.watchdog.internal.IoOveruseAlertThreshold
constructInternalIoOveruseAlertThreshold(long duration, long writeBPS) {
android.automotive.watchdog.internal.IoOveruseAlertThreshold threshold =
new android.automotive.watchdog.internal.IoOveruseAlertThreshold();
threshold.durationInSeconds = duration;
threshold.writtenBytesPerSecond = writeBPS;
return threshold;
}
private static PerStateIoOveruseThreshold constructPerStateIoOveruseThreshold(String name,
long fgBytes, long bgBytes, long gmBytes) {
PerStateIoOveruseThreshold threshold = new PerStateIoOveruseThreshold();
threshold.name = name;
threshold.perStateWriteBytes = new android.automotive.watchdog.PerStateBytes();
threshold.perStateWriteBytes.foregroundBytes = fgBytes;
threshold.perStateWriteBytes.backgroundBytes = bgBytes;
threshold.perStateWriteBytes.garageModeBytes = gmBytes;
return threshold;
}
private List<android.automotive.watchdog.internal.ResourceOveruseConfiguration>
captureOnSetResourceOveruseConfigurations(int wantedInvocations) throws Exception {
verify(mMockCarWatchdogDaemonHelper, times(wantedInvocations))
.updateResourceOveruseConfigurations(
mResourceOveruseConfigurationsCaptor.capture());
return mResourceOveruseConfigurationsCaptor.getValue();
}
static List<ResourceOveruseConfiguration> sampleResourceOveruseConfigurations() {
return Arrays.asList(
sampleResourceOveruseConfigurationBuilder(ComponentType.SYSTEM,
sampleIoOveruseConfigurationBuilder(ComponentType.SYSTEM).build()).build(),
sampleResourceOveruseConfigurationBuilder(ComponentType.VENDOR,
sampleIoOveruseConfigurationBuilder(ComponentType.VENDOR).build()).build(),
sampleResourceOveruseConfigurationBuilder(ComponentType.THIRD_PARTY,
sampleIoOveruseConfigurationBuilder(ComponentType.THIRD_PARTY).build())
.build());
}
private static IoOveruseConfiguration.Builder sampleIoOveruseConfigurationBuilder(
@ComponentType int componentType) {
String prefix = WatchdogPerfHandler.toComponentTypeStr(componentType).toLowerCase();
PerStateBytes componentLevelThresholds = new PerStateBytes(
/* foregroundModeBytes= */ componentType * 10L,
/* backgroundModeBytes= */ componentType * 20L,
/* garageModeBytes= */ componentType * 30L);
Map<String, PerStateBytes> packageSpecificThresholds = new ArrayMap<>();
packageSpecificThresholds.put(prefix + "_package.A", new PerStateBytes(
/* foregroundModeBytes= */ componentType * 40L,
/* backgroundModeBytes= */ componentType * 50L,
/* garageModeBytes= */ componentType * 60L));
Map<String, PerStateBytes> appCategorySpecificThresholds = new ArrayMap<>();
appCategorySpecificThresholds.put(
ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MEDIA,
new PerStateBytes(/* foregroundModeBytes= */ componentType * 100L,
/* backgroundModeBytes= */ componentType * 200L,
/* garageModeBytes= */ componentType * 300L));
appCategorySpecificThresholds.put(
ResourceOveruseConfiguration.APPLICATION_CATEGORY_TYPE_MAPS,
new PerStateBytes(/* foregroundModeBytes= */ componentType * 1100L,
/* backgroundModeBytes= */ componentType * 2200L,
/* garageModeBytes= */ componentType * 3300L));
List<IoOveruseAlertThreshold> systemWideThresholds = Collections.singletonList(
new IoOveruseAlertThreshold(/* durationInSeconds= */ componentType * 10L,
/* writtenBytesPerSecond= */ componentType * 200L));
return new IoOveruseConfiguration.Builder(componentLevelThresholds,
packageSpecificThresholds, appCategorySpecificThresholds, systemWideThresholds);
}
private static ResourceOveruseConfiguration.Builder sampleResourceOveruseConfigurationBuilder(
@ComponentType int componentType, IoOveruseConfiguration ioOveruseConfig) {
String prefix = WatchdogPerfHandler.toComponentTypeStr(componentType).toLowerCase();
List<String> safeToKill = Arrays.asList(prefix + "_package.non_critical.A",
prefix + "_pkg.non_critical.B",
"shared:" + prefix + "_shared_package.non_critical.B",
"some_pkg_as_" + prefix + "_pkg");
List<String> vendorPrefixes = Arrays.asList(
prefix + "_package", "some_pkg_as_" + prefix + "_pkg");
Map<String, String> pkgToAppCategory = new ArrayMap<>();
pkgToAppCategory.put("system_package.MEDIA", "android.car.watchdog.app.category.MEDIA");
pkgToAppCategory.put("system_package.A", "android.car.watchdog.app.category.MAPS");
pkgToAppCategory.put("vendor_package.MEDIA", "android.car.watchdog.app.category.MEDIA");
pkgToAppCategory.put("vendor_package.A", "android.car.watchdog.app.category.MAPS");
pkgToAppCategory.put("third_party_package.MAPS", "android.car.watchdog.app.category.MAPS");
ResourceOveruseConfiguration.Builder configBuilder =
new ResourceOveruseConfiguration.Builder(componentType, safeToKill,
vendorPrefixes, pkgToAppCategory);
configBuilder.setIoOveruseConfiguration(ioOveruseConfig);
return configBuilder;
}
private void captureCarUxRestrictionsChangeListener(int wantedInvocations) {
verify(mMockCarUxRestrictionsManagerService, times(wantedInvocations))
.getCurrentUxRestrictions();
verify(mMockCarUxRestrictionsManagerService, times(wantedInvocations))
.registerUxRestrictionsChangeListener(
mICarUxRestrictionsChangeListenerCaptor.capture(),
eq(Display.DEFAULT_DISPLAY));
mCarUxRestrictionsChangeListener = mICarUxRestrictionsChangeListenerCaptor.getValue();
assertWithMessage("UX restrictions change listener").that(mCarUxRestrictionsChangeListener)
.isNotNull();
}
private void captureStatsPullAtomCallback(int wantedInvocations) {
verify(mMockStatsManager, times(wantedInvocations)).setPullAtomCallback(
eq(CAR_WATCHDOG_SYSTEM_IO_USAGE_SUMMARY), any(StatsManager.PullAtomMetadata.class),
any(Executor.class), mStatsPullAtomCallbackCaptor.capture());
verify(mMockStatsManager, times(wantedInvocations)).setPullAtomCallback(
eq(CAR_WATCHDOG_UID_IO_USAGE_SUMMARY), any(StatsManager.PullAtomMetadata.class),
any(Executor.class), mStatsPullAtomCallbackCaptor.capture());
// The same callback is set in the above calls, so fetch the latest captured callback.
mStatsPullAtomCallback = mStatsPullAtomCallbackCaptor.getValue();
assertWithMessage("Stats pull atom callback").that(mStatsPullAtomCallback).isNotNull();
}
private void verifyDatabaseInit(int wantedInvocations) throws Exception {
/*
* Database read is posted on a separate handler thread. Wait until the handler thread has
* processed the database read request before verifying.
*/
CarServiceUtils.runEmptyRunnableOnLooperSync(CAR_WATCHDOG_SERVICE_NAME);
verify(mSpiedWatchdogStorage, times(wantedInvocations)).syncUsers(any());
verify(mSpiedWatchdogStorage, times(wantedInvocations)).getUserPackageSettings();
verify(mSpiedWatchdogStorage, times(wantedInvocations)).getTodayIoUsageStats();
verify(mSpiedWatchdogStorage, times(wantedInvocations)).getNotForgivenHistoricalIoOveruses(
RECURRING_OVERUSE_PERIOD_IN_DAYS);
}
static IResourceOveruseListener createMockResourceOveruseListener() {
IResourceOveruseListener listener = mock(IResourceOveruseListener.Stub.class);
when(listener.asBinder()).thenCallRealMethod();
return listener;
}
private SparseArray<PackageIoOveruseStats> injectIoOveruseStatsForPackages(
SparseArray<String> genericPackageNameByUid, Set<String> killablePackages,
Set<String> shouldNotifyPackages) throws Exception {
SparseArray<PackageIoOveruseStats> packageIoOveruseStatsByUid = new SparseArray<>();
List<PackageIoOveruseStats> packageIoOveruseStats = new ArrayList<>();
for (int i = 0; i < genericPackageNameByUid.size(); ++i) {
String name = genericPackageNameByUid.valueAt(i);
int uid = genericPackageNameByUid.keyAt(i);
PackageIoOveruseStats stats = constructPackageIoOveruseStats(uid,
shouldNotifyPackages.contains(name),
constructPerStateBytes(80, 147, 213),
constructInternalIoOveruseStats(killablePackages.contains(name),
/* remainingWriteBytes= */
constructPerStateBytes(/* fgBytes= */ 20, /* bgBytes= */
20, /* gmBytes= */ 20),
/* writtenBytes= */
constructPerStateBytes(/* fgBytes= */ 100, /* bgBytes= */
200, /* gmBytes= */ 300),
/* totalOveruses= */ 3));
packageIoOveruseStatsByUid.put(uid, stats);
packageIoOveruseStats.add(stats);
}
pushLatestIoOveruseStatsAndWait(packageIoOveruseStats);
return packageIoOveruseStatsByUid;
}
static PackageIoOveruseStats constructPackageIoOveruseStats(int uid,
boolean shouldNotify, android.automotive.watchdog.PerStateBytes forgivenWriteBytes,
android.automotive.watchdog.IoOveruseStats ioOveruseStats) {
PackageIoOveruseStats stats = new PackageIoOveruseStats();
stats.uid = uid;
stats.shouldNotify = shouldNotify;
stats.forgivenWriteBytes = forgivenWriteBytes;
stats.ioOveruseStats = ioOveruseStats;
return stats;
}
private android.automotive.watchdog.IoOveruseStats constructInternalIoOveruseStats(
boolean killableOnOveruse,
android.automotive.watchdog.PerStateBytes remainingWriteBytes,
android.automotive.watchdog.PerStateBytes writtenBytes, int totalOveruses) {
return constructInternalIoOveruseStats(killableOnOveruse, STATS_DURATION_SECONDS,
remainingWriteBytes, writtenBytes, totalOveruses);
}
public static android.automotive.watchdog.PerStateBytes constructPerStateBytes(
long fgBytes, long bgBytes, long gmBytes) {
android.automotive.watchdog.PerStateBytes perStateBytes =
new android.automotive.watchdog.PerStateBytes();
perStateBytes.foregroundBytes = fgBytes;
perStateBytes.backgroundBytes = bgBytes;
perStateBytes.garageModeBytes = gmBytes;
return perStateBytes;
}
private android.automotive.watchdog.IoOveruseStats constructInternalIoOveruseStats(
boolean killableOnOveruse, long durationInSecs,
android.automotive.watchdog.PerStateBytes remainingWriteBytes,
android.automotive.watchdog.PerStateBytes writtenBytes, int totalOveruses) {
android.automotive.watchdog.IoOveruseStats stats =
new android.automotive.watchdog.IoOveruseStats();
stats.startTime = mTimeSource.now().getEpochSecond();
stats.durationInSeconds = durationInSecs;
stats.killableOnOveruse = killableOnOveruse;
stats.remainingWriteBytes = remainingWriteBytes;
stats.writtenBytes = writtenBytes;
stats.totalOveruses = totalOveruses;
return stats;
}
private void pushLatestIoOveruseStatsAndWait(List<PackageIoOveruseStats> packageIoOveruseStats)
throws Exception {
mWatchdogPerfHandler.latestIoOveruseStats(packageIoOveruseStats);
// Handling latest I/O overuse stats is done on the CarWatchdogService service handler
// thread. Wait until the below message is processed before returning, so the
// latestIoOveruseStats call is completed.
CarServiceUtils.runEmptyRunnableOnLooperSync(CAR_WATCHDOG_SERVICE_NAME);
// Resource overuse handling is done on the main thread by posting a new message with
// OVERUSE_HANDLING_DELAY_MILLS delay. Wait until the below message is processed before
// returning, so the resource overuse handling is completed.
CarServiceUtils.runOnMainSyncDelayed(() -> {
}, OVERUSE_HANDLING_DELAY_MILLS * 2);
if (mGenericPackageNameByUid.size() > 0) {
verify(mSpiedWatchdogStorage, atLeastOnce()).markDirty();
}
}
private static final class UserNotificationReflectionCall {
public final UserHandle userHandle;
public final SparseArray<String> packagesById;
public final boolean hasHeadsUpNotification;
UserNotificationReflectionCall(UserHandle userHandle, SparseArray<String> packagesById,
boolean hasHeadsUpNotification) {
this.userHandle = userHandle;
this.packagesById = packagesById;
this.hasHeadsUpNotification = hasHeadsUpNotification;
}
}
}