blob: e2f9fb2ba86a747299965d2dba8bb0974de96707 [file] [log] [blame]
/*
* Copyright (C) 2020 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.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.mockUmGetAllUsers;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserHandles;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmIsUserRunning;
import static android.car.test.util.AndroidHelper.assertFilterHasActions;
import static android.car.test.util.AndroidHelper.assertFilterHasDataScheme;
import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED;
import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKING;
import static android.car.watchdog.CarWatchdogManager.FLAG_MINIMUM_STATS_IO_1_MB;
import static android.car.watchdog.CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO;
import static android.car.watchdog.CarWatchdogManager.STATS_PERIOD_CURRENT_DAY;
import static android.car.watchdog.CarWatchdogManager.TIMEOUT_CRITICAL;
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_USER_REMOVED;
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_SYSTEM_IO_USAGE_SUMMARY;
import static com.android.car.CarStatsLog.CAR_WATCHDOG_UID_IO_USAGE_SUMMARY;
import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION;
import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS;
import static com.android.car.internal.NotificationHelperBase.CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP;
import static com.android.car.internal.NotificationHelperBase.RESOURCE_OVERUSE_NOTIFICATION_BASE_ID;
import static com.android.car.watchdog.CarWatchdogService.ACTION_GARAGE_MODE_OFF;
import static com.android.car.watchdog.CarWatchdogService.ACTION_GARAGE_MODE_ON;
import static com.android.car.watchdog.CarWatchdogService.MISSING_ARG_VALUE;
import static com.android.car.watchdog.TimeSource.ZONE_OFFSET;
import static com.android.car.watchdog.WatchdogPerfHandler.INTENT_EXTRA_NOTIFICATION_ID;
import static com.android.car.watchdog.WatchdogPerfHandler.USER_PACKAGE_SEPARATOR;
import static com.android.car.watchdog.WatchdogPerfHandlerUnitTest.constructPackageIoOveruseStats;
import static com.android.car.watchdog.WatchdogPerfHandlerUnitTest.createMockResourceOveruseListener;
import static com.android.car.watchdog.WatchdogPerfHandlerUnitTest.sampleInternalResourceOveruseConfigurations;
import static com.android.car.watchdog.WatchdogPerfHandlerUnitTest.sampleResourceOveruseConfigurations;
import static com.android.car.watchdog.WatchdogProcessHandler.MISSING_INT_PROPERTY_VALUE;
import static com.android.car.watchdog.WatchdogProcessHandler.PROPERTY_RO_CLIENT_HEALTHCHECK_INTERVAL;
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.verify;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
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.ICarWatchdog;
import android.automotive.watchdog.internal.ICarWatchdogServiceForSystem;
import android.automotive.watchdog.internal.PackageIdentifier;
import android.automotive.watchdog.internal.PackageInfo;
import android.automotive.watchdog.internal.PackageIoOveruseStats;
import android.automotive.watchdog.internal.PowerCycle;
import android.automotive.watchdog.internal.ResourceStats;
import android.automotive.watchdog.internal.StateType;
import android.automotive.watchdog.internal.UidType;
import android.automotive.watchdog.internal.UserPackageIoUsageStats;
import android.automotive.watchdog.internal.UserState;
import android.car.drivingstate.CarUxRestrictions;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.power.ICarPowerPolicyListener;
import android.car.hardware.power.ICarPowerStateListener;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
import android.car.test.mocks.MockSettings;
import android.car.user.CarUserManager;
import android.car.watchdog.CarWatchdogManager;
import android.car.watchdog.ICarWatchdogServiceCallback;
import android.car.watchdog.IResourceOveruseListener;
import android.car.watchdog.ResourceOveruseConfiguration;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
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.IBinder;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.StatsEvent;
import com.android.car.BuiltinPackageDependency;
import com.android.car.CarLocalServices;
import com.android.car.CarServiceHelperWrapper;
import com.android.car.CarServiceUtils;
import com.android.car.CarStatsLog;
import com.android.car.CarUxRestrictionsManagerService;
import com.android.car.admin.NotificationHelper;
import com.android.car.internal.ICarServiceHelper;
import com.android.car.power.CarPowerManagementService;
import com.android.car.systeminterface.SystemInterface;
import com.android.car.user.CarUserService;
import com.google.common.truth.Correspondence;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.nio.file.Files;
import java.time.Instant;
import java.time.ZonedDateTime;
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.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
/**
* <p>This class contains unit tests for the {@link CarWatchdogService}.
*/
@RunWith(MockitoJUnitRunner.class)
public final class CarWatchdogServiceUnitTest extends AbstractExtendedMockitoTestCase {
private static final String CAR_WATCHDOG_DAEMON_INTERFACE =
"android.automotive.watchdog.internal.ICarWatchdog/default";
private static final String SYSTEM_PACKAGE_NAME = "system_package";
private static final int MAX_WAIT_TIME_MS = 3000;
private static final long OVERUSE_HANDLING_DELAY_MILLS = 1000;
private static final int RECURRING_OVERUSE_TIMES = 2;
private static final int RECURRING_OVERUSE_PERIOD_IN_DAYS = 2;
private static final int UID_IO_USAGE_SUMMARY_TOP_COUNT = 3;
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 SYSTEM_DAILY_IO_USAGE_SUMMARY_MULTIPLIER = 10_000;
private static final int PACKAGE_KILLABLE_STATE_RESET_DAYS = 90;
private static final UserHandle TEST_USER_HANDLE = UserHandle.of(100);
@Mock private Context mMockContext;
@Mock private Context mMockBuiltinPackageContext;
@Mock private ClassLoader mMockClassLoader;
@Mock private PackageManager mMockPackageManager;
@Mock private StatsManager mMockStatsManager;
@Mock private UserManager mMockUserManager;
@Mock private SystemInterface mMockSystemInterface;
@Mock private CarPowerManagementService mMockCarPowerManagementService;
@Mock private CarUserService mMockCarUserService;
@Mock private CarUxRestrictionsManagerService mMockCarUxRestrictionsManagerService;
@Mock private Resources mMockResources;
@Mock private IBinder mMockBinder;
@Mock private ICarWatchdog mMockCarWatchdogDaemon;
@Mock private NotificationHelper mMockNotificationHelper;
@Mock private ICarServiceHelper.Stub mMockCarServiceHelper;
@Mock private WatchdogProcessHandler mMockWatchdogProcessHandler;
@Mock private WatchdogPerfHandler mMockWatchdogPerfHandler;
@Captor private ArgumentCaptor<ICarPowerStateListener> mICarPowerStateListenerCaptor;
@Captor private ArgumentCaptor<ICarPowerPolicyListener> mICarPowerPolicyListenerCaptor;
@Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
@Captor private ArgumentCaptor<IntentFilter> mIntentFilterCaptor;
@Captor private ArgumentCaptor<CarUserManager.UserLifecycleListener>
mUserLifecycleListenerCaptor;
@Captor private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor;
@Captor private ArgumentCaptor<ICarWatchdogServiceForSystem>
mICarWatchdogServiceForSystemCaptor;
private CarWatchdogService mCarWatchdogService;
private ICarWatchdogServiceForSystem mWatchdogServiceForSystemImpl;
private IBinder.DeathRecipient mCarWatchdogDaemonBinderDeathRecipient;
private WatchdogStorage mSpiedWatchdogStorage;
private BroadcastReceiver mBroadcastReceiver;
private boolean mIsDaemonCrashed;
private ICarPowerStateListener mCarPowerStateListener;
private ICarPowerPolicyListener mCarPowerPolicyListener;
private CarUserManager.UserLifecycleListener mUserLifecycleListener;
private File mTempSystemCarDir;
// Not used directly, but sets proper mockStatic() expectations on Settings
@SuppressWarnings("UnusedVariable")
private MockSettings mMockSettings;
private final TestTimeSource mTimeSource = new TestTimeSource();
private final SparseArray<String> mGenericPackageNameByUid = new SparseArray<>();
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 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<>();
private final IPackageManager mSpiedPackageManager = spy(ActivityThread.getPackageManager());
public CarWatchdogServiceUnitTest() {
super(CarWatchdogService.TAG);
}
@Override
protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
mMockSettings = new MockSettings(builder);
builder
.spyStatic(ServiceManager.class)
.spyStatic(Binder.class)
.spyStatic(ActivityManager.class)
.spyStatic(ActivityThread.class)
.spyStatic(CarLocalServices.class)
.spyStatic(CarStatsLog.class)
.spyStatic(CarServiceUtils.class)
.spyStatic(BuiltinPackageDependency.class)
.spyStatic(SystemProperties.class);
}
/**
* Initialize all of the objects with the @Mock annotation.
*/
@Before
public void setUp() throws Exception {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockContext.getSystemService(StatsManager.class)).thenReturn(mMockStatsManager);
when(mMockContext.getPackageName()).thenReturn(
CarWatchdogServiceUnitTest.class.getCanonicalName());
when(mMockContext.getResources()).thenReturn(mMockResources);
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(mMockSystemInterface)
.when(() -> CarLocalServices.getService(SystemInterface.class));
doReturn(mMockCarPowerManagementService)
.when(() -> CarLocalServices.getService(CarPowerManagementService.class));
doReturn(mMockCarUserService)
.when(() -> CarLocalServices.getService(CarUserService.class));
doReturn(mMockCarUxRestrictionsManagerService)
.when(() -> CarLocalServices.getService(CarUxRestrictionsManagerService.class));
doReturn(mSpiedPackageManager).when(() -> ActivityThread.getPackageManager());
when(mMockBuiltinPackageContext.getClassLoader()).thenReturn(mMockClassLoader);
doReturn(NotificationHelper.class).when(mMockClassLoader).loadClass(any());
doReturn(mMockNotificationHelper)
.when(() -> BuiltinPackageDependency.createNotificationHelper(
mMockBuiltinPackageContext));
doReturn(MISSING_INT_PROPERTY_VALUE).when(
() -> SystemProperties.getInt(PROPERTY_RO_CLIENT_HEALTHCHECK_INTERVAL,
MISSING_INT_PROPERTY_VALUE));
CarServiceHelperWrapper wrapper = CarServiceHelperWrapper.create();
wrapper.setCarServiceHelper(mMockCarServiceHelper);
when(mMockCarUxRestrictionsManagerService.getCurrentUxRestrictions())
.thenReturn(new CarUxRestrictions.Builder(/* reqOpt= */ false,
UX_RESTRICTIONS_BASELINE, /* time= */ 0).build());
mTempSystemCarDir = Files.createTempDirectory("watchdog_test").toFile();
when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempSystemCarDir);
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));
setupUsers();
mockWatchdogDaemon();
mockWatchdogStorage();
mockPackageManager();
mockBuildStatsEventCalls();
mockSettingsStringCalls();
mTimeSource.updateNow(/* numDaysAgo= */ 0);
mCarWatchdogService = new CarWatchdogService(mMockContext, mMockBuiltinPackageContext,
mSpiedWatchdogStorage, mTimeSource, mMockWatchdogProcessHandler,
mMockWatchdogPerfHandler);
initService(/* wantedInvocations= */ 1);
}
/**
* Releases resources.
*/
@After
public void tearDown() throws Exception {
if (mIsDaemonCrashed) {
/* Note: On daemon crash, CarWatchdogService retries daemon connection on the main
* thread. This retry outlives the test and impacts other test runs. Thus always call
* restartWatchdogDaemonAndAwait after crashing the daemon and before completing
* teardown.
*/
restartWatchdogDaemonAndAwait();
}
if (mTempSystemCarDir != null) {
FileUtils.deleteContentsAndDir(mTempSystemCarDir);
}
CarLocalServices.removeServiceForTest(CarServiceHelperWrapper.class);
}
@Test
public void testCarWatchdogServiceHealthCheck() throws Exception {
mWatchdogServiceForSystemImpl.checkIfAlive(123456, TIMEOUT_CRITICAL);
verify(mMockWatchdogProcessHandler).postHealthCheckMessage(eq(123456));
}
@Test
public void testTellClientAlive() throws Exception {
TestClient client = new TestClient();
mCarWatchdogService.tellClientAlive(client, TIMEOUT_CRITICAL);
verify(mMockWatchdogProcessHandler).tellClientAlive(eq(client), eq(TIMEOUT_CRITICAL));
}
@Test
public void testRegisterClient() throws Exception {
TestClient client = new TestClient();
mCarWatchdogService.registerClient(client, TIMEOUT_CRITICAL);
verify(mMockWatchdogProcessHandler).registerClient(eq(client), eq(TIMEOUT_CRITICAL));
}
@Test
public void testUnregisterClient() throws Exception {
TestClient client = new TestClient();
mCarWatchdogService.registerClient(client, TIMEOUT_CRITICAL);
mCarWatchdogService.unregisterClient(client);
verify(mMockWatchdogProcessHandler).unregisterClient(eq(client));
}
@Test
public void testRequestAidlVhalPid() throws Exception {
mWatchdogServiceForSystemImpl.requestAidlVhalPid();
verify(mMockWatchdogProcessHandler, timeout(MAX_WAIT_TIME_MS)).asyncFetchAidlVhalPid();
}
// TODO(b/262301082): Add a unit test to verify the race condition that caused watchdog to
// incorrectly terminate clients that were recently unregistered - b/261766872.
@Test
public void testGarageModeStateChangeToOn() throws Exception {
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(ACTION_GARAGE_MODE_ON));
verify(mMockWatchdogPerfHandler).onGarageModeChange(GarageMode.GARAGE_MODE_ON);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.GARAGE_MODE,
GarageMode.GARAGE_MODE_ON, MISSING_ARG_VALUE);
verify(mSpiedWatchdogStorage).shrinkDatabase();
}
@Test
public void testGarageModeStateChangeToOff() throws Exception {
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(ACTION_GARAGE_MODE_OFF));
// GARAGE_MODE_OFF is notified twice: Once during the initial daemon connect and once when
// the ACTION_GARAGE_MODE_OFF intent is received.
verify(mMockWatchdogPerfHandler).onGarageModeChange(GarageMode.GARAGE_MODE_OFF);
verify(mMockCarWatchdogDaemon, times(2)).notifySystemStateChange(StateType.GARAGE_MODE,
GarageMode.GARAGE_MODE_OFF, MISSING_ARG_VALUE);
verify(mSpiedWatchdogStorage, never()).shrinkDatabase();
}
@Test
public void testWatchdogDaemonRestart() throws Exception {
crashWatchdogDaemon();
mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ false, 101, 102);
mockUmIsUserRunning(mMockUserManager, /* userId= */ 101, /* isRunning= */ false);
mockUmIsUserRunning(mMockUserManager, /* userId= */ 102, /* isRunning= */ true);
setCarPowerState(CarPowerManager.STATE_SHUTDOWN_ENTER);
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(ACTION_GARAGE_MODE_ON));
restartWatchdogDaemonAndAwait();
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 101,
UserState.USER_STATE_STOPPED);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 102,
UserState.USER_STATE_STARTED);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER, MISSING_ARG_VALUE);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.GARAGE_MODE,
GarageMode.GARAGE_MODE_ON, MISSING_ARG_VALUE);
}
@Test
public void testUserSwitchingLifecycleEvents() throws Exception {
mUserLifecycleListener.onEvent(
new CarUserManager.UserLifecycleEvent(
USER_LIFECYCLE_EVENT_TYPE_SWITCHING, 100, 101));
mUserLifecycleListener.onEvent(
new CarUserManager.UserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKING, 101));
mUserLifecycleListener.onEvent(
new CarUserManager.UserLifecycleEvent(
USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED, 101));
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 101,
UserState.USER_STATE_SWITCHING);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 101,
UserState.USER_STATE_UNLOCKING);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 101,
UserState.USER_STATE_POST_UNLOCKED);
}
@Test
public void testUserRemovedBroadcast() throws Exception {
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(Intent.ACTION_USER_REMOVED)
.putExtra(Intent.EXTRA_USER, TEST_USER_HANDLE));
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 100,
UserState.USER_STATE_REMOVED);
verify(mMockWatchdogPerfHandler).deleteUser(eq(100));
}
@Test
public void testPowerCycleStateChangesDuringSuspend() throws Exception {
setCarPowerState(CarPowerManager.STATE_SUSPEND_ENTER);
setCarPowerState(CarPowerManager.STATE_SUSPEND_EXIT);
setCarPowerState(CarPowerManager.STATE_ON);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER, MISSING_ARG_VALUE);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_SUSPEND_EXIT, MISSING_ARG_VALUE);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_RESUME, MISSING_ARG_VALUE);
}
@Test
public void testPowerCycleStateChangesDuringHibernation() throws Exception {
setCarPowerState(CarPowerManager.STATE_HIBERNATION_ENTER);
setCarPowerState(CarPowerManager.STATE_HIBERNATION_EXIT);
setCarPowerState(CarPowerManager.STATE_ON);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER, MISSING_ARG_VALUE);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_SUSPEND_EXIT, MISSING_ARG_VALUE);
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_RESUME, MISSING_ARG_VALUE);
}
@Test
public void testDeviceRebootBroadcast() throws Exception {
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(Intent.ACTION_REBOOT)
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND));
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER, /* arg2= */ 0);
}
@Test
public void testDeviceShutdownBroadcast() throws Exception {
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(Intent.ACTION_SHUTDOWN)
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND));
verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER, /* arg2= */ 0);
}
@Test
public void testDeviceShutdownBroadcastWithoutFlagReceiverForeground() throws Exception {
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(Intent.ACTION_SHUTDOWN));
verify(mMockCarWatchdogDaemon, never()).notifySystemStateChange(StateType.POWER_CYCLE,
PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER, /* arg2= */ 0);
}
@Test
public void testDisableAppBroadcast() throws Exception {
Intent intent = new Intent(
CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP).putExtra(
Intent.EXTRA_PACKAGE_NAME, SYSTEM_PACKAGE_NAME).putExtra(Intent.EXTRA_USER,
TEST_USER_HANDLE).putExtra(INTENT_EXTRA_NOTIFICATION_ID,
RESOURCE_OVERUSE_NOTIFICATION_BASE_ID);
mBroadcastReceiver.onReceive(mMockContext, intent);
verify(mMockWatchdogPerfHandler).processUserNotificationIntent(eq(intent));
}
@Test
public void testDisableAppBroadcastWithDisabledPackage() throws Exception {
Intent intent = new Intent(
CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP)
.putExtra(Intent.EXTRA_PACKAGE_NAME, SYSTEM_PACKAGE_NAME)
.putExtra(Intent.EXTRA_USER, TEST_USER_HANDLE)
.putExtra(INTENT_EXTRA_NOTIFICATION_ID, RESOURCE_OVERUSE_NOTIFICATION_BASE_ID);
mBroadcastReceiver.onReceive(mMockContext, intent);
verify(mMockWatchdogPerfHandler).processUserNotificationIntent(eq(intent));
}
@Test
public void testLaunchAppSettingsBroadcast() throws Exception {
Intent intent = new Intent(
CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS)
.putExtra(Intent.EXTRA_PACKAGE_NAME, SYSTEM_PACKAGE_NAME)
.putExtra(Intent.EXTRA_USER, TEST_USER_HANDLE)
.putExtra(INTENT_EXTRA_NOTIFICATION_ID, RESOURCE_OVERUSE_NOTIFICATION_BASE_ID);
mBroadcastReceiver.onReceive(mMockContext, intent);
verify(mMockWatchdogPerfHandler).processUserNotificationIntent(eq(intent));
}
@Test
public void testDismissUserNotificationBroadcast() throws Exception {
Intent intent = new Intent(CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION)
.putExtra(Intent.EXTRA_PACKAGE_NAME, SYSTEM_PACKAGE_NAME)
.putExtra(Intent.EXTRA_USER, TEST_USER_HANDLE)
.putExtra(INTENT_EXTRA_NOTIFICATION_ID,
RESOURCE_OVERUSE_NOTIFICATION_BASE_ID);
mBroadcastReceiver.onReceive(mMockContext, intent);
verify(mMockWatchdogPerfHandler).processUserNotificationIntent(eq(intent));
}
@Test
public void testUserNotificationActionBroadcastsWithNullPackageName() throws Exception {
List<String> actions = Arrays.asList(CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP,
CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS,
CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION);
for (String action : actions) {
mBroadcastReceiver.onReceive(mMockContext, new Intent(action)
.putExtra(Intent.EXTRA_USER, TEST_USER_HANDLE)
.putExtra(INTENT_EXTRA_NOTIFICATION_ID, RESOURCE_OVERUSE_NOTIFICATION_BASE_ID));
}
verify(mMockBuiltinPackageContext, never()).startActivityAsUser(any(), any());
verifyNoMoreInteractions(mSpiedPackageManager);
verify(mMockNotificationHelper, never()).cancelNotificationAsUser(any(), anyInt());
}
@Test
public void testUserNotificationActionBroadcastsWithInvalidUserId() throws Exception {
List<String> actions = Arrays.asList(CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP,
CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS,
CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION);
for (String action : actions) {
mBroadcastReceiver.onReceive(mMockContext, new Intent(action)
.putExtra(Intent.EXTRA_PACKAGE_NAME, SYSTEM_PACKAGE_NAME)
.putExtra(Intent.EXTRA_USER, UserHandle.of(-1))
.putExtra(INTENT_EXTRA_NOTIFICATION_ID, RESOURCE_OVERUSE_NOTIFICATION_BASE_ID));
}
verify(mMockBuiltinPackageContext, never()).startActivityAsUser(any(), any());
verifyNoMoreInteractions(mSpiedPackageManager);
verify(mMockNotificationHelper, never()).cancelNotificationAsUser(any(), anyInt());
}
@Test
public void testUserNotificationActionBroadcastsWithMissingNotificationId() throws Exception {
List<String> actions = Arrays.asList(CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP,
CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS,
CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION);
for (String action : actions) {
Intent intent = new Intent(action)
.putExtra(Intent.EXTRA_PACKAGE_NAME, SYSTEM_PACKAGE_NAME)
.putExtra(Intent.EXTRA_USER, TEST_USER_HANDLE);
mBroadcastReceiver.onReceive(mMockContext, intent);
verify(mMockWatchdogPerfHandler).processUserNotificationIntent(intent);
}
}
@Test
public void testHandlePackageChangedBroadcast() throws Exception {
Intent intent = new Intent(ACTION_PACKAGE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, 100)
.setData(Uri.parse("package:" + SYSTEM_PACKAGE_NAME));
mBroadcastReceiver.onReceive(mMockContext, intent);
verify(mMockWatchdogPerfHandler).processPackageChangedIntent(eq(intent));
}
@Test
public void testSetOveruseHandlingDelay() {
mCarWatchdogService.setOveruseHandlingDelay(OVERUSE_HANDLING_DELAY_MILLS);
verify(mMockWatchdogPerfHandler).setOveruseHandlingDelay(eq(OVERUSE_HANDLING_DELAY_MILLS));
}
@Test
public void testGetResourceOveruseStats() {
mCarWatchdogService.getResourceOveruseStats(FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
verify(mMockWatchdogPerfHandler).getResourceOveruseStats(eq(FLAG_RESOURCE_OVERUSE_IO),
eq(CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
}
@Test
public void testGetAllResourceOveruseStats() {
mCarWatchdogService.getAllResourceOveruseStats(FLAG_RESOURCE_OVERUSE_IO,
FLAG_MINIMUM_STATS_IO_1_MB, STATS_PERIOD_CURRENT_DAY);
verify(mMockWatchdogPerfHandler).getAllResourceOveruseStats(eq(FLAG_RESOURCE_OVERUSE_IO),
eq(FLAG_MINIMUM_STATS_IO_1_MB), eq(STATS_PERIOD_CURRENT_DAY));
}
@Test
public void testGetResourceOveruseStatsForUserPackage() {
UserHandle userHandle = UserHandle.of(12);
mCarWatchdogService.getResourceOveruseStatsForUserPackage(
"vendor_package.critical", userHandle, FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
verify(mMockWatchdogPerfHandler).getResourceOveruseStatsForUserPackage(
eq("vendor_package.critical"), eq(userHandle), eq(FLAG_RESOURCE_OVERUSE_IO),
eq(CarWatchdogManager.STATS_PERIOD_CURRENT_DAY));
}
@Test
public void testOnLatestResourceOveruseStats() throws Exception {
// 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)));
ResourceStats resourceStats = new ResourceStats();
resourceStats.resourceOveruseStats =
new android.automotive.watchdog.internal.ResourceOveruseStats();
resourceStats.resourceOveruseStats.packageIoOveruseStats = packageIoOveruseStats;
mWatchdogServiceForSystemImpl.onLatestResourceStats(List.of(resourceStats));
verify(mMockWatchdogPerfHandler).latestIoOveruseStats(eq(packageIoOveruseStats));
}
@Test
public void testAddResourceOveruseListener() {
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
mCarWatchdogService.addResourceOveruseListener(FLAG_RESOURCE_OVERUSE_IO, mockListener);
verify(mMockWatchdogPerfHandler).addResourceOveruseListener(eq(FLAG_RESOURCE_OVERUSE_IO),
eq(mockListener));
}
@Test
public void testAddResourceOveruseListenerForSystem() {
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
mCarWatchdogService.addResourceOveruseListenerForSystem(FLAG_RESOURCE_OVERUSE_IO,
mockListener);
verify(mMockWatchdogPerfHandler).addResourceOveruseListenerForSystem(
eq(FLAG_RESOURCE_OVERUSE_IO), eq(mockListener));
}
@Test
public void testRemoveResourceOveruseListener() {
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
mCarWatchdogService.removeResourceOveruseListener(mockListener);
verify(mMockWatchdogPerfHandler).removeResourceOveruseListener(eq(mockListener));
}
@Test
public void testRemoveResourceOveruseListenerForSystem() {
IResourceOveruseListener mockListener = createMockResourceOveruseListener();
mCarWatchdogService.removeResourceOveruseListenerForSystem(mockListener);
verify(mMockWatchdogPerfHandler).removeResourceOveruseListenerForSystem(eq(mockListener));
}
@Test
public void testSetKillablePackageAsUser() throws Exception {
UserHandle userHandle = UserHandle.of(101);
mCarWatchdogService.setKillablePackageAsUser("third_party_package", userHandle,
/* isKillable= */ false);
verify(mMockWatchdogPerfHandler).setKillablePackageAsUser(
eq("third_party_package"), eq(userHandle), eq(false));
}
@Test
public void testGetPackageKillableStatesAsUser() throws Exception {
UserHandle userHandle = UserHandle.of(101);
mCarWatchdogService.getPackageKillableStatesAsUser(userHandle);
verify(mMockWatchdogPerfHandler).getPackageKillableStatesAsUser(eq(userHandle));
}
@Test
public void testSetResourceOveruseConfigurations() throws Exception {
List<ResourceOveruseConfiguration> configurations = sampleResourceOveruseConfigurations();
mCarWatchdogService.setResourceOveruseConfigurations(configurations,
FLAG_RESOURCE_OVERUSE_IO);
verify(mMockWatchdogPerfHandler).setResourceOveruseConfigurations(
eq(configurations), eq(FLAG_RESOURCE_OVERUSE_IO));
}
@Test
public void testGetResourceOveruseConfigurations() {
mCarWatchdogService.getResourceOveruseConfigurations(FLAG_RESOURCE_OVERUSE_IO);
verify(mMockWatchdogPerfHandler).getResourceOveruseConfigurations(
eq(FLAG_RESOURCE_OVERUSE_IO));
}
@Test
public void testRequestTodayIoUsageStats() throws Exception {
mWatchdogServiceForSystemImpl.requestTodayIoUsageStats();
verify(mMockWatchdogPerfHandler).asyncFetchTodayIoUsageStats();
}
@Test
public void testGetTodayIoUsageStats() throws Exception {
mWatchdogServiceForSystemImpl.getTodayIoUsageStats();
verify(mMockWatchdogPerfHandler).getTodayIoUsageStats();
}
@Test
public void testResetResourceOveruseStats() throws Exception {
String packageName = mMockContext.getPackageName();
mWatchdogServiceForSystemImpl.resetResourceOveruseStats(
Collections.singletonList(packageName));
verify(mMockWatchdogPerfHandler).resetResourceOveruseStats(any());
}
@Test
public void testGetPackageInfosForUids() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"system_package.A", 6001, null, ApplicationInfo.FLAG_SYSTEM, 0),
constructPackageManagerPackageInfo(
"vendor_package.B", 10005100, null, 0, ApplicationInfo.PRIVATE_FLAG_OEM),
constructPackageManagerPackageInfo(
"vendor_package.C", 10345678, null, 0, ApplicationInfo.PRIVATE_FLAG_ODM),
constructPackageManagerPackageInfo("third_party_package.D", 10200056, null)));
int[] uids = new int[]{6001, 10005100, 10200056, 10345678};
List<PackageInfo> actualPackageInfos = mWatchdogServiceForSystemImpl.getPackageInfosForUids(
uids, new ArrayList<>());
List<PackageInfo> expectedPackageInfos = Arrays.asList(
constructPackageInfo("system_package.A", 6001, new ArrayList<>(),
UidType.NATIVE, ComponentType.SYSTEM, ApplicationCategoryType.OTHERS),
constructPackageInfo("vendor_package.B", 10005100, new ArrayList<>(),
UidType.NATIVE, ComponentType.VENDOR, ApplicationCategoryType.OTHERS),
constructPackageInfo("third_party_package.D", 10200056, new ArrayList<>(),
UidType.APPLICATION, ComponentType.THIRD_PARTY,
ApplicationCategoryType.OTHERS),
constructPackageInfo("vendor_package.C", 10345678, new ArrayList<>(),
UidType.APPLICATION, ComponentType.VENDOR,
ApplicationCategoryType.OTHERS));
assertPackageInfoEquals(actualPackageInfos, expectedPackageInfos);
}
@Test
public void testGetPackageInfosWithSharedUids() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package.A", 6050,
"system_shared_package", ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0),
constructPackageManagerPackageInfo("system_package.B", 10100035,
"vendor_shared_package", 0, ApplicationInfo.PRIVATE_FLAG_PRODUCT),
constructPackageManagerPackageInfo("vendor_package.C", 10100035,
"vendor_shared_package", 0, ApplicationInfo.PRIVATE_FLAG_VENDOR),
constructPackageManagerPackageInfo(
"third_party_package.D", 6050, "system_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.E", 10100035, "vendor_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.F", 10200078, "third_party_shared_package")));
int[] uids = new int[]{6050, 10100035, 10200078};
List<PackageInfo> actualPackageInfos = mWatchdogServiceForSystemImpl.getPackageInfosForUids(
uids, new ArrayList<>());
List<PackageInfo> expectedPackageInfos = Arrays.asList(
constructPackageInfo("shared:system_shared_package", 6050,
Arrays.asList("system_package.A", "third_party_package.D"),
UidType.NATIVE, ComponentType.SYSTEM, ApplicationCategoryType.OTHERS),
constructPackageInfo("shared:vendor_shared_package", 10100035,
Arrays.asList("vendor_package.C", "system_package.B",
"third_party_package.E"), UidType.APPLICATION,
ComponentType.VENDOR, ApplicationCategoryType.OTHERS),
constructPackageInfo("shared:third_party_shared_package", 10200078,
Collections.singletonList("third_party_package.F"),
UidType.APPLICATION, ComponentType.THIRD_PARTY,
ApplicationCategoryType.OTHERS));
assertPackageInfoEquals(actualPackageInfos, expectedPackageInfos);
}
@Test
public void testGetPackageInfosForUidsWithVendorPackagePrefixes() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"vendor_package.A", 10010034, null, 0,
ApplicationInfo.PRIVATE_FLAG_PRODUCT),
constructPackageManagerPackageInfo("vendor_pkg.B", 10010035,
"vendor_shared_package", ApplicationInfo.FLAG_SYSTEM, 0),
constructPackageManagerPackageInfo(
"third_party_package.C", 10010035, "vendor_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.D", 10010035, "vendor_shared_package"),
constructPackageManagerPackageInfo(
"third_party_package.F", 10200078, "third_party_shared_package"),
constructPackageManagerPackageInfo("vndr_pkg.G", 10206345, "vendor_package.shared",
ApplicationInfo.FLAG_SYSTEM, 0),
/*
* A 3p package pretending to be a vendor package because 3p packages won't have the
* required flags.
*/
constructPackageManagerPackageInfo("vendor_package.imposter", 10203456, null, 0,
0)));
int[] uids = new int[]{10010034, 10010035, 10200078, 10206345, 10203456};
List<PackageInfo> actualPackageInfos = mWatchdogServiceForSystemImpl.getPackageInfosForUids(
uids, Arrays.asList("vendor_package.", "vendor_pkg.", "shared:vendor_package."));
List<PackageInfo> expectedPackageInfos = Arrays.asList(
constructPackageInfo("vendor_package.A", 10010034, new ArrayList<>(),
UidType.APPLICATION, ComponentType.VENDOR, ApplicationCategoryType.OTHERS),
constructPackageInfo("shared:vendor_shared_package", 10010035,
Arrays.asList("vendor_pkg.B", "third_party_package.C",
"third_party_package.D"), UidType.APPLICATION,
ComponentType.VENDOR, ApplicationCategoryType.OTHERS),
constructPackageInfo("shared:third_party_shared_package", 10200078,
Collections.singletonList("third_party_package.F"), UidType.APPLICATION,
ComponentType.THIRD_PARTY, ApplicationCategoryType.OTHERS),
constructPackageInfo("shared:vendor_package.shared", 10206345,
Collections.singletonList("vndr_pkg.G"), UidType.APPLICATION,
ComponentType.VENDOR, ApplicationCategoryType.OTHERS),
constructPackageInfo("vendor_package.imposter", 10203456,
new ArrayList<>(), UidType.APPLICATION, ComponentType.THIRD_PARTY,
ApplicationCategoryType.OTHERS));
assertPackageInfoEquals(actualPackageInfos, expectedPackageInfos);
}
@Test
public void testGetPackageInfosForUidsWithMissingApplicationInfos() throws Exception {
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"vendor_package.A", 10100034, null, 0, ApplicationInfo.PRIVATE_FLAG_OEM),
constructPackageManagerPackageInfo("vendor_package.B", 10100035,
"vendor_shared_package", 0, ApplicationInfo.PRIVATE_FLAG_VENDOR),
constructPackageManagerPackageInfo(
"third_party_package.C", 10100035, "vendor_shared_package")));
BiConsumer<Integer, String> addPackageToSharedUid = (uid, packageName) -> {
List<String> packages = mPackagesBySharedUid.get(uid);
if (packages == null) {
packages = new ArrayList<>();
}
packages.add(packageName);
mPackagesBySharedUid.put(uid, packages);
};
addPackageToSharedUid.accept(10100035, "third_party_package.G");
mGenericPackageNameByUid.put(10200056, "third_party_package.H");
mGenericPackageNameByUid.put(10200078, "shared:third_party_shared_package");
addPackageToSharedUid.accept(10200078, "third_party_package.I");
int[] uids = new int[]{10100034, 10100035, 10200056, 10200078};
List<PackageInfo> actualPackageInfos = mWatchdogServiceForSystemImpl.getPackageInfosForUids(
uids, new ArrayList<>());
List<PackageInfo> expectedPackageInfos = Arrays.asList(
constructPackageInfo("vendor_package.A", 10100034, new ArrayList<>(),
UidType.APPLICATION, ComponentType.VENDOR, ApplicationCategoryType.OTHERS),
constructPackageInfo("shared:vendor_shared_package", 10100035,
Arrays.asList("vendor_package.B", "third_party_package.C",
"third_party_package.G"),
UidType.APPLICATION, ComponentType.VENDOR, ApplicationCategoryType.OTHERS),
constructPackageInfo("third_party_package.H", 10200056, new ArrayList<>(),
UidType.APPLICATION, ComponentType.UNKNOWN,
ApplicationCategoryType.OTHERS),
constructPackageInfo("shared:third_party_shared_package", 10200078,
Collections.singletonList("third_party_package.I"),
UidType.APPLICATION, ComponentType.UNKNOWN,
ApplicationCategoryType.OTHERS));
assertPackageInfoEquals(actualPackageInfos, expectedPackageInfos);
}
@Test
public void testControlProcessHealthCheckEnabled() throws Exception {
mCarWatchdogService.controlProcessHealthCheck(true);
verify(mMockWatchdogProcessHandler).controlProcessHealthCheck(eq(true));
}
@Test
public void testControlProcessHealthCheckDisabled() throws Exception {
mCarWatchdogService.controlProcessHealthCheck(false);
verify(mMockWatchdogProcessHandler).controlProcessHealthCheck(eq(false));
}
@Test
public void testDisablePackageForUser() throws Exception {
mCarWatchdogService.performResourceOveruseKill("third_party_package", /* userId= */ 100);
verify(mMockWatchdogPerfHandler).disablePackageForUser(eq("third_party_package"),
eq(100));
}
@Test
public void testOveruseConfigurationCacheGetVendorPackagePrefixes() throws Exception {
OveruseConfigurationCache cache = new OveruseConfigurationCache();
cache.set(sampleInternalResourceOveruseConfigurations());
assertWithMessage("Vendor package prefixes").that(cache.getVendorPackagePrefixes())
.containsExactly("vendor_package", "some_pkg_as_vendor_pkg");
}
@Test
public void testOveruseConfigurationCacheFetchThreshold() throws Exception {
OveruseConfigurationCache cache = new OveruseConfigurationCache();
cache.set(sampleInternalResourceOveruseConfigurations());
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("system_package.non_critical.A", ComponentType.SYSTEM),
"System package with generic threshold")
.isEqualTo(constructPerStateBytes(10, 20, 30));
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("system_package.A", ComponentType.SYSTEM),
"System package with package specific threshold")
.isEqualTo(constructPerStateBytes(40, 50, 60));
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("system_package.MEDIA", ComponentType.SYSTEM),
"System package with media category threshold")
.isEqualTo(constructPerStateBytes(200, 400, 600));
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("vendor_package.non_critical.A", ComponentType.VENDOR),
"Vendor package with generic threshold")
.isEqualTo(constructPerStateBytes(20, 40, 60));
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("vendor_package.A", ComponentType.VENDOR),
"Vendor package with package specific threshold")
.isEqualTo(constructPerStateBytes(80, 100, 120));
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("vendor_package.MEDIA", ComponentType.VENDOR),
"Vendor package with media category threshold")
.isEqualTo(constructPerStateBytes(200, 400, 600));
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("third_party_package.A",
ComponentType.THIRD_PARTY),
"3p package with generic threshold").isEqualTo(constructPerStateBytes(30, 60, 90));
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("third_party_package.MAPS", ComponentType.VENDOR),
"3p package with maps category threshold")
.isEqualTo(constructPerStateBytes(2200, 4400, 6600));
}
@Test
public void testOveruseConfigurationCacheIsSafeToKill() throws Exception {
OveruseConfigurationCache cache = new OveruseConfigurationCache();
cache.set(sampleInternalResourceOveruseConfigurations());
assertWithMessage("isSafeToKill non-critical system package").that(cache.isSafeToKill(
"system_package.non_critical.A", ComponentType.SYSTEM, null)).isTrue();
assertWithMessage("isSafeToKill shared non-critical system package")
.that(cache.isSafeToKill("system_package.A", ComponentType.SYSTEM,
Collections.singletonList("system_package.non_critical.A"))).isTrue();
assertWithMessage("isSafeToKill non-critical vendor package").that(cache.isSafeToKill(
"vendor_package.non_critical.A", ComponentType.VENDOR, null)).isTrue();
assertWithMessage("isSafeToKill shared non-critical vendor package")
.that(cache.isSafeToKill("vendor_package.A", ComponentType.VENDOR,
Collections.singletonList("vendor_package.non_critical.A"))).isTrue();
assertWithMessage("isSafeToKill 3p package").that(cache.isSafeToKill(
"third_party_package.A", ComponentType.THIRD_PARTY, null)).isTrue();
assertWithMessage("isSafeToKill critical system package").that(cache.isSafeToKill(
"system_package.A", ComponentType.SYSTEM, null)).isFalse();
assertWithMessage("isSafeToKill critical vendor package").that(cache.isSafeToKill(
"vendor_package.A", ComponentType.VENDOR, null)).isFalse();
}
@Test
public void testOverwriteOveruseConfigurationCache() throws Exception {
OveruseConfigurationCache cache = new OveruseConfigurationCache();
cache.set(sampleInternalResourceOveruseConfigurations());
cache.set(new ArrayList<>());
assertWithMessage("Vendor package prefixes").that(cache.getVendorPackagePrefixes())
.isEmpty();
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("system_package.A", ComponentType.SYSTEM),
"System package with default threshold")
.isEqualTo(OveruseConfigurationCache.DEFAULT_THRESHOLD);
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("vendor_package.A", ComponentType.VENDOR),
"Vendor package with default threshold")
.isEqualTo(OveruseConfigurationCache.DEFAULT_THRESHOLD);
InternalPerStateBytesSubject.assertWithMessage(
cache.fetchThreshold("third_party_package.A", ComponentType.THIRD_PARTY),
"3p package with default threshold")
.isEqualTo(OveruseConfigurationCache.DEFAULT_THRESHOLD);
assertWithMessage("isSafeToKill any system package").that(cache.isSafeToKill(
"system_package.non_critical.A", ComponentType.SYSTEM, null)).isFalse();
assertWithMessage("isSafeToKill any vendor package").that(cache.isSafeToKill(
"vendor_package.non_critical.A", ComponentType.VENDOR, null)).isFalse();
}
@Test
public void testRelease() throws Exception {
mCarWatchdogService.release();
verify(mMockCarPowerManagementService).unregisterListener(
mICarPowerStateListenerCaptor.capture());
verify(mMockCarPowerManagementService).removePowerPolicyListener(
mICarPowerPolicyListenerCaptor.capture());
verify(mMockWatchdogPerfHandler).release();
verify(mSpiedWatchdogStorage).release();
verify(mMockCarWatchdogDaemon).unregisterCarWatchdogService(
mICarWatchdogServiceForSystemCaptor.capture());
}
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 void mockWatchdogDaemon() throws Exception {
when(mMockBinder.queryLocalInterface(anyString())).thenReturn(mMockCarWatchdogDaemon);
when(mMockCarWatchdogDaemon.asBinder()).thenReturn(mMockBinder);
doReturn(mMockBinder).when(
() -> ServiceManager.checkService(CAR_WATCHDOG_DAEMON_INTERFACE));
when(mMockCarWatchdogDaemon.getResourceOveruseConfigurations()).thenReturn(
sampleInternalResourceOveruseConfigurations());
mIsDaemonCrashed = false;
}
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 void setupUsers() {
when(mMockContext.getSystemService(UserManager.class)).thenReturn(mMockUserManager);
mockUmGetAllUsers(mMockUserManager, new UserHandle[0]);
}
private void initService(int wantedInvocations) throws Exception {
mCarWatchdogService.init();
verify(mMockWatchdogPerfHandler).init();
captureCarPowerListeners(wantedInvocations);
captureBroadcastReceiver(wantedInvocations);
captureUserLifecycleListener(wantedInvocations);
captureAndVerifyRegistrationWithDaemon(/* waitOnMain= */ true);
}
private void captureCarPowerListeners(int wantedInvocations) {
verify(mMockCarPowerManagementService, times(wantedInvocations)).registerListener(
mICarPowerStateListenerCaptor.capture());
mCarPowerStateListener = mICarPowerStateListenerCaptor.getValue();
assertWithMessage("Car power state listener").that(mCarPowerStateListener).isNotNull();
verify(mMockCarPowerManagementService, times(wantedInvocations)).addPowerPolicyListener(
any(), mICarPowerPolicyListenerCaptor.capture());
mCarPowerPolicyListener = mICarPowerPolicyListenerCaptor.getValue();
assertWithMessage("Car power policy listener").that(mCarPowerPolicyListener).isNotNull();
}
private void captureBroadcastReceiver(int wantedInvocations) {
verify(mMockContext, times(wantedInvocations * 2))
.registerReceiverForAllUsers(mBroadcastReceiverCaptor.capture(),
mIntentFilterCaptor.capture(), any(), any(), anyInt());
mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
assertWithMessage("Broadcast receiver").that(mBroadcastReceiver).isNotNull();
List<IntentFilter> filters = mIntentFilterCaptor.getAllValues();
int totalFilters = filters.size();
assertThat(totalFilters).isAtLeast(2);
// When CarWatchdogService is restarted, registerReceiverForAllUsers will be called more
// than 2 times. Thus, verify the filters only from the latest 2 calls.
IntentFilter filter = filters.get(totalFilters - 2);
assertFilterHasActions(filter, CAR_WATCHDOG_ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION,
ACTION_GARAGE_MODE_ON, ACTION_GARAGE_MODE_OFF,
CAR_WATCHDOG_ACTION_LAUNCH_APP_SETTINGS,
CAR_WATCHDOG_ACTION_RESOURCE_OVERUSE_DISABLE_APP, ACTION_USER_REMOVED);
filter = filters.get(totalFilters - 1);
assertFilterHasActions(filter, ACTION_PACKAGE_CHANGED);
assertFilterHasDataScheme(filter, /* dataScheme= */ "package");
}
private void captureUserLifecycleListener(int wantedInvocations) {
verify(mMockCarUserService, times(wantedInvocations)).addUserLifecycleListener(any(),
mUserLifecycleListenerCaptor.capture());
mUserLifecycleListener = mUserLifecycleListenerCaptor.getValue();
assertWithMessage("User lifecycle listener").that(mUserLifecycleListener).isNotNull();
}
private void captureAndVerifyRegistrationWithDaemon(boolean waitOnMain) throws Exception {
if (waitOnMain) {
// Registering to daemon is done on the main thread. To ensure the registration
// completes before verification, execute an empty block on the main thread.
CarServiceUtils.runOnMainSync(() -> {});
}
verify(mMockCarWatchdogDaemon, atLeastOnce()).asBinder();
verify(mMockBinder, atLeastOnce()).linkToDeath(mDeathRecipientCaptor.capture(), anyInt());
mCarWatchdogDaemonBinderDeathRecipient = mDeathRecipientCaptor.getValue();
assertWithMessage("Watchdog daemon binder death recipient")
.that(mCarWatchdogDaemonBinderDeathRecipient).isNotNull();
verify(mMockCarWatchdogDaemon, atLeastOnce()).registerCarWatchdogService(
mICarWatchdogServiceForSystemCaptor.capture());
mWatchdogServiceForSystemImpl = mICarWatchdogServiceForSystemCaptor.getValue();
assertWithMessage("Car watchdog service for system")
.that(mWatchdogServiceForSystemImpl).isNotNull();
verify(mMockCarWatchdogDaemon, atLeastOnce()).notifySystemStateChange(
StateType.GARAGE_MODE, GarageMode.GARAGE_MODE_OFF, MISSING_ARG_VALUE);
}
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 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;
});
}
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 setCarPowerState(int powerState) throws Exception {
when(mMockCarPowerManagementService.getPowerState()).thenReturn(powerState);
mCarPowerStateListener.onStateChanged(powerState, /* timeoutMs= */ -1);
}
private void crashWatchdogDaemon() {
doReturn(null).when(() -> ServiceManager.checkService(CAR_WATCHDOG_DAEMON_INTERFACE));
mCarWatchdogDaemonBinderDeathRecipient.binderDied();
mIsDaemonCrashed = true;
}
private void restartWatchdogDaemonAndAwait() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
doAnswer(args -> {
latch.countDown();
return null;
}).when(mMockBinder).linkToDeath(any(), anyInt());
mockWatchdogDaemon();
latch.await(MAX_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
captureAndVerifyRegistrationWithDaemon(/* waitOnMain= */ false);
}
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);
}
}
public static boolean isUserPackageIoUsageStatsEquals(UserPackageIoUsageStats actual,
UserPackageIoUsageStats expected) {
return actual.userId == expected.userId && actual.packageName.equals(expected.packageName)
&& isInternalPerStateBytesEquals(
actual.ioUsageStats.writtenBytes, expected.ioUsageStats.writtenBytes)
&& isInternalPerStateBytesEquals(actual.ioUsageStats.forgivenWriteBytes,
expected.ioUsageStats.forgivenWriteBytes)
&& actual.ioUsageStats.totalOveruses == expected.ioUsageStats.totalOveruses;
}
public static boolean isInternalPerStateBytesEquals(
android.automotive.watchdog.PerStateBytes actual,
android.automotive.watchdog.PerStateBytes expected) {
return actual.foregroundBytes == expected.foregroundBytes
&& actual.backgroundBytes == expected.backgroundBytes
&& actual.garageModeBytes == expected.garageModeBytes;
}
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);
}
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 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(constructCarWatchdogDailyIoUsageSummary(
/* fgWrBytes= */ 100 * i * weekMultiplier * sysOrUidMultiplier,
/* bgWrBytes= */ 200 * i * weekMultiplier * sysOrUidMultiplier,
/* gmWrBytes= */ 300 * i * weekMultiplier * sysOrUidMultiplier,
/* overuseCount= */ 2 * i));
}
return summaries;
}
static AtomsProto.CarWatchdogDailyIoUsageSummary constructCarWatchdogDailyIoUsageSummary(
long fgWrBytes, long bgWrBytes, long gmWrBytes, int overuseCount) {
return AtomsProto.CarWatchdogDailyIoUsageSummary.newBuilder()
.setWrittenBytes(WatchdogPerfHandler
.constructCarWatchdogPerStateBytes(fgWrBytes, bgWrBytes, gmWrBytes))
.setOveruseCount(overuseCount)
.build();
}
private static PackageInfo constructPackageInfo(String packageName, int uid,
List<String> sharedUidPackages, int uidType, int componentType, int appCategoryType) {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageIdentifier = new PackageIdentifier();
packageInfo.packageIdentifier.name = packageName;
packageInfo.packageIdentifier.uid = uid;
packageInfo.uidType = uidType;
packageInfo.sharedUidPackages = sharedUidPackages;
packageInfo.componentType = componentType;
packageInfo.appCategoryType = appCategoryType;
return packageInfo;
}
private static String toPackageInfosString(List<PackageInfo> packageInfos) {
StringBuilder builder = new StringBuilder();
for (PackageInfo packageInfo : packageInfos) {
builder = packageInfoStringBuilder(builder, packageInfo).append('\n');
}
return builder.toString();
}
private static StringBuilder packageInfoStringBuilder(
StringBuilder builder, PackageInfo packageInfo) {
if (packageInfo == null) {
return builder.append("Null package info\n");
}
builder.append("Package name: '").append(packageInfo.packageIdentifier.name)
.append("', UID: ").append(packageInfo.packageIdentifier.uid).append('\n')
.append("Owned packages: ");
if (packageInfo.sharedUidPackages != null) {
for (int i = 0; i < packageInfo.sharedUidPackages.size(); ++i) {
builder.append('\'').append(packageInfo.sharedUidPackages.get(i)).append('\'');
if (i < packageInfo.sharedUidPackages.size() - 1) {
builder.append(", ");
}
}
builder.append('\n');
} else {
builder.append("Null");
}
builder.append("Component type: ").append(packageInfo.componentType).append('\n')
.append("Application category type: ").append(packageInfo.appCategoryType).append(
'\n');
return builder;
}
private static void assertPackageInfoEquals(List<PackageInfo> actual,
List<PackageInfo> expected) throws Exception {
assertWithMessage("Package infos for UIDs:\nExpected: %s\nActual: %s",
CarWatchdogServiceUnitTest.toPackageInfosString(expected),
CarWatchdogServiceUnitTest.toPackageInfosString(actual))
.that(actual)
.comparingElementsUsing(
Correspondence.from(CarWatchdogServiceUnitTest::isPackageInfoEquals,
"is package info equal to")).containsExactlyElementsIn(expected);
}
private static boolean isPackageInfoEquals(PackageInfo lhs, PackageInfo rhs) {
return isEquals(lhs.packageIdentifier, rhs.packageIdentifier)
&& lhs.sharedUidPackages.containsAll(rhs.sharedUidPackages)
&& lhs.componentType == rhs.componentType
&& lhs.appCategoryType == rhs.appCategoryType;
}
private static boolean isEquals(PackageIdentifier lhs, PackageIdentifier rhs) {
return lhs.name.equals(rhs.name) && lhs.uid == rhs.uid;
}
private 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 static final class TestClient extends ICarWatchdogServiceCallback.Stub {
@Override
public void onCheckHealthStatus(int sessionId, int timeout) {}
@Override
public void onPrepareProcessTermination() {}
}
static final class TestTimeSource extends TimeSource {
private static final Instant TEST_DATE_TIME = Instant.parse("2021-11-12T13:14:15.16Z");
private Instant mNow;
TestTimeSource() {
mNow = TEST_DATE_TIME;
}
@Override
public Instant now() {
/* Return the same time, so the tests are deterministic. */
return mNow;
}
@Override
public String toString() {
return "Mocked date to " + now();
}
void updateNow(int numDaysAgo) {
mNow = TEST_DATE_TIME.minus(numDaysAgo, ChronoUnit.DAYS);
}
}
}