blob: e956d8dfcdd0665c584f6e6c848e77ac1a7833a8 [file] [log] [blame]
package com.android.clockwork.healthservices;
import static com.android.clockwork.healthservices.HealthService.BOOT_BIND_DELAY_MILLIS;
import static com.android.clockwork.healthservices.HealthService.PACKAGE_UPDATE_BIND_DELAY_MILLIS;
import static com.android.clockwork.healthservices.HealthService.USER_UNLOCK_BIND_DELAY_MILLIS;
import static com.android.clockwork.healthservices.HealthService.WHS_CRASH_BIND_DELAY_MILLIS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import com.android.clockwork.healthservices.HealthService.BinderService;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import com.google.android.clockwork.healthservices.types.AutoExerciseEvent;
import com.google.android.clockwork.healthservices.types.AutoPauseEvent;
import com.google.android.clockwork.healthservices.types.AutoPauseResumeEventType;
import com.google.android.clockwork.healthservices.types.AutoStartConfig;
import com.google.android.clockwork.healthservices.types.AutoStartEvent;
import com.google.android.clockwork.healthservices.types.DataType;
import com.google.android.clockwork.healthservices.types.DataTypeGoal;
import com.google.android.clockwork.healthservices.types.DataTypeOffset;
import com.google.android.clockwork.healthservices.types.ExerciseType;
import com.google.android.clockwork.healthservices.types.GoalThreshold;
import com.google.android.clockwork.healthservices.types.GoalThresholdType;
import com.google.android.clockwork.healthservices.types.OffsetValue;
import com.google.android.clockwork.healthservices.types.OffsetValueType;
import com.google.android.clockwork.healthservices.types.TrackingConfig;
import com.google.android.clockwork.healthservices.IHealthServiceCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.Arrays;
import vendor.google_clockwork.healthservices.V1_0.IHealthServices;
@RunWith(RobolectricTestRunner.class)
public final class HealthServiceTest {
private static final int TEST_WHS_PACKAGE_UID = 1234;
private static final int TEST_NON_WHS_PACKAGE_UID = 5;
private final ArgumentCaptor<IntentFilter> mIntentFilterCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
private final ArgumentCaptor<Intent> mIntentCaptor = ArgumentCaptor.forClass(Intent.class);
@Mock Context mockContext;
@Mock PackageManager mockPackageManager;
@Mock HandlerBindingAgent mockBindingHandler;
@Mock ConnectionTracker mockConnectionTracker;
@Mock IBinder mockBinder;
@Mock HalAdapter mockHalAdapter;
@Mock IHealthServiceCallback mockIHealthServiceCallback;
private BinderService mBinderService;
private TargetUser mTargetUser;
private HealthService mService;
@Before
public void setUp() {
initMocks(this);
when(mockContext.getPackageManager()).thenReturn(mockPackageManager);
when(mockPackageManager.getNameForUid(TEST_WHS_PACKAGE_UID))
.thenReturn(HealthService.WHS_PACKAGE);
when(mockPackageManager.getNameForUid(TEST_NON_WHS_PACKAGE_UID)).thenReturn("random_package");
mService =
spy(
new HealthService(
mockContext, mockBindingHandler, mockConnectionTracker, mockHalAdapter));
doNothing().when(mService).publishBinderService();
mBinderService = mService.new BinderService();
UserInfo userInfo = new UserInfo(1, "rand", null, 0, UserManager.USER_TYPE_PROFILE_MANAGED);
mTargetUser = new TargetUser(userInfo);
}
@Test
public void onStart_registersBroadcastReceiver() {
mService.onStart();
verify(mockContext)
.registerReceiver(
any(HealthService.PackageEventsReceiver.class), mIntentFilterCaptor.capture());
IntentFilter capturedFilter = mIntentFilterCaptor.getValue();
assertThat(capturedFilter.hasAction(Intent.ACTION_PACKAGE_REPLACED)).isTrue();
assertThat(capturedFilter.hasAction(Intent.ACTION_PACKAGE_RESTARTED)).isTrue();
verify(mockHalAdapter).registerHalV1_0RegistrationNotification();
verify(mockHalAdapter).maybeGetAidlHalService();
}
@Test
public void onBootComplete_schedulesBind() {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
verify(mockBindingHandler).bind(BOOT_BIND_DELAY_MILLIS);
}
@Test
public void onUserUnlock_notConnected_schedulesBind() {
when(mockConnectionTracker.isConnected()).thenReturn(false);
mService.onUserUnlocked(mTargetUser);
verify(mockBindingHandler).bind(USER_UNLOCK_BIND_DELAY_MILLIS);
}
@Test
public void onUserUnlock_connected_noBind() {
when(mockConnectionTracker.isConnected()).thenReturn(false);
mService.onUserUnlocked(mTargetUser);
verify(mockBindingHandler, never()).bind(anyInt());
}
@Test
public void onBinderDied_schedulesBind() {
mService.binderDied();
verify(mockBindingHandler).bind(WHS_CRASH_BIND_DELAY_MILLIS);
}
@Test
public void onReceive_unwantedAction_noBind() {
when(mockConnectionTracker.isConnected()).thenReturn(true);
mService.mWhsPackageEventsReceiver.onReceive(mockContext, new Intent("random_action"));
verify(mockBindingHandler, never()).bind(anyInt());
}
@Test
public void onReceive_packageReplaced_nonWhsPackage_noBind() {
when(mockConnectionTracker.isConnected()).thenReturn(true);
mService.mWhsPackageEventsReceiver.onReceive(
mockContext,
new Intent(Intent.ACTION_PACKAGE_REPLACED)
.putExtra(Intent.EXTRA_UID, TEST_NON_WHS_PACKAGE_UID));
verify(mockBindingHandler, never()).bind(anyInt());
}
@Test
public void onReceive_packageRestarted_nonWhsPackage_noBind() {
when(mockConnectionTracker.isConnected()).thenReturn(true);
mService.mWhsPackageEventsReceiver.onReceive(
mockContext,
new Intent(Intent.ACTION_PACKAGE_RESTARTED)
.putExtra(Intent.EXTRA_UID, TEST_NON_WHS_PACKAGE_UID));
verify(mockBindingHandler, never()).bind(anyInt());
}
@Test
public void onReceive_packageReplaced_whsPackage_connected_noBind() {
when(mockConnectionTracker.isConnected()).thenReturn(true);
mService.mWhsPackageEventsReceiver.onReceive(
mockContext,
new Intent(Intent.ACTION_PACKAGE_REPLACED)
.putExtra(Intent.EXTRA_UID, TEST_WHS_PACKAGE_UID));
verify(mockBindingHandler, never()).bind(anyInt());
}
@Test
public void onReceive_packageRestarted_whsPackage_connected_noBind() {
when(mockConnectionTracker.isConnected()).thenReturn(true);
mService.mWhsPackageEventsReceiver.onReceive(
mockContext,
new Intent(Intent.ACTION_PACKAGE_RESTARTED)
.putExtra(Intent.EXTRA_UID, TEST_WHS_PACKAGE_UID));
verify(mockBindingHandler, never()).bind(anyInt());
}
@Test
public void onReceive_packageReplaced_whsPackage_notConnected_binds() {
when(mockConnectionTracker.isConnected()).thenReturn(false);
mService.mWhsPackageEventsReceiver.onReceive(
mockContext,
new Intent(Intent.ACTION_PACKAGE_REPLACED)
.putExtra(Intent.EXTRA_UID, TEST_WHS_PACKAGE_UID));
verify(mockBindingHandler).bind(PACKAGE_UPDATE_BIND_DELAY_MILLIS);
}
@Test
public void onReceive_packageRestarted_whsPackage_notConnected_binds() {
when(mockConnectionTracker.isConnected()).thenReturn(false);
mService.mWhsPackageEventsReceiver.onReceive(
mockContext,
new Intent(Intent.ACTION_PACKAGE_RESTARTED)
.putExtra(Intent.EXTRA_UID, TEST_WHS_PACKAGE_UID));
verify(mockBindingHandler).bind(PACKAGE_UPDATE_BIND_DELAY_MILLIS);
}
@Test
public void addGoal_forwardsToHalAdapter() throws RemoteException {
DataTypeGoal dataTypeGoal = getDataTypeGoal(DataType.EXERCISE_STEP_COUNT, 20);
mBinderService.addGoal(dataTypeGoal);
verify(mockHalAdapter).addGoal(dataTypeGoal);
}
@Test
public void removeGoal_forwardsToHalAdapter() throws RemoteException {
DataTypeGoal dataTypeGoal = getDataTypeGoal(DataType.EXERCISE_STEP_COUNT, 20);
mBinderService.removeGoal(dataTypeGoal);
verify(mockHalAdapter).removeGoal(dataTypeGoal);
}
@Test
public void startTracking_noOffsets_forwardsToHalAdapter() throws RemoteException {
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
TrackingConfig config = getTrackingConfig(ExerciseType.WALKING, false, 0, 0);
DataTypeOffset[] offsets = {getDataTypeOffset(DataType.EXERCISE_STEP_COUNT, 0)};
boolean isPaused = false;
mBinderService.startTracking(config, dataTypes, offsets, isPaused);
verify(mockHalAdapter).startTracking(config, dataTypes, offsets, isPaused);
}
@Test
public void startTracking_nonZeroOffset_forwardsToHalAdapter() throws RemoteException {
int offset = 100;
int maxReportLatencyMs = 120000;
int samplingPeriodMs = 60000;
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
TrackingConfig config =
getTrackingConfig(ExerciseType.WALKING, false, maxReportLatencyMs, samplingPeriodMs);
DataTypeOffset[] offsets = {getDataTypeOffset(DataType.EXERCISE_STEP_COUNT, offset)};
boolean isPaused = true;
mBinderService.startTracking(config, dataTypes, offsets, isPaused);
verify(mockHalAdapter).startTracking(config, dataTypes, offsets, isPaused);
}
@Test
public void startAutomaticExerciseDetection_noOffset_forwardsToHalAdapter()
throws RemoteException {
AutoStartConfig autoStartConfig = new AutoStartConfig();
autoStartConfig.exerciseType = ExerciseType.WALKING;
autoStartConfig.minimumNotificationLatencyMs = 123;
AutoStartConfig[] autoStartConfigs = {autoStartConfig};
mBinderService.startAutomaticExerciseDetection(autoStartConfigs, null);
verify(mockHalAdapter).startAutomaticExerciseDetection(autoStartConfigs, null);
}
@Test
public void startAutomaticExerciseDetection_withOffset_forwardsToHalAdapter()
throws RemoteException {
AutoStartConfig autoStartConfig = new AutoStartConfig();
autoStartConfig.exerciseType = ExerciseType.WALKING;
autoStartConfig.minimumNotificationLatencyMs = 123;
AutoStartConfig[] autoStartConfigs = {autoStartConfig};
AutoStartEvent autoStartEvent = new AutoStartEvent();
autoStartEvent.elapsedTimeSinceBootNanos = 123;
autoStartEvent.exerciseType = ExerciseType.WALKING;
autoStartEvent.type = AutoStartEvent.AutoStartEventType.AUTO_EXERCISE_START_DETECTED;
mBinderService.startAutomaticExerciseDetection(autoStartConfigs, autoStartEvent);
verify(mockHalAdapter).startAutomaticExerciseDetection(autoStartConfigs, autoStartEvent);
}
@Test
public void stopAutomaticExerciseDetection_forwardsToHalAdapter() throws RemoteException {
int[] exerciseTypes = {ExerciseType.WALKING, ExerciseType.RUNNING};
mBinderService.stopAutomaticExerciseDetection(exerciseTypes);
verify(mockHalAdapter).stopAutomaticExerciseDetection(exerciseTypes);
}
@Test
public void updateTrackingConfig_forwardsToHalAdapter() throws RemoteException {
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
TrackingConfig config = getTrackingConfig(ExerciseType.WALKING, false, 0, 0);
mBinderService.updateTrackingConfig(config, dataTypes);
verify(mockHalAdapter).updateTrackingConfig(config, dataTypes);
}
@Test
public void pauseTracking_forwardsToHalAdapter() throws RemoteException {
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
mBinderService.pauseTracking(dataTypes);
verify(mockHalAdapter).pauseTracking(dataTypes);
}
@Test
public void resumeTracking_forwardsToHalAdapter() throws RemoteException {
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
mBinderService.resumeTracking(dataTypes);
verify(mockHalAdapter).resumeTracking(dataTypes);
}
@Test
public void stopTracking_forwardsToHalAdapter() throws RemoteException {
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
mBinderService.stopTracking(dataTypes);
verify(mockHalAdapter).stopTracking(dataTypes);
}
@Test
public void flush_generatesNewId_forwardsToHalAdapter_singleRequest() throws RemoteException {
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
mBinderService.flush(dataTypes);
verify(mockHalAdapter).flush(0, dataTypes);
}
@Test
public void flush_generatesNewId_forwardsToHalAdapter_multiRequest() throws RemoteException {
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
mBinderService.flush(dataTypes);
mBinderService.flush(dataTypes);
mBinderService.flush(dataTypes);
verify(mockHalAdapter).flush(0, dataTypes);
verify(mockHalAdapter).flush(1, dataTypes);
verify(mockHalAdapter).flush(2, dataTypes);
}
@Test
public void flush_generatesNewId_forwardsToHalAdapter_flushIdWrapsAtMaxInt()
throws RemoteException {
int[] dataTypes = {DataType.EXERCISE_STEP_COUNT};
mService.nextFlushId = Integer.MAX_VALUE;
mBinderService.flush(dataTypes);
mBinderService.flush(dataTypes);
verify(mockHalAdapter).flush(Integer.MAX_VALUE, dataTypes);
verify(mockHalAdapter).flush(0, dataTypes);
}
@Test
public void resetDataTypeOffsets_forwardsToHalAdapter() throws RemoteException {
int offset = 10;
DataTypeOffset[] offsets = {getDataTypeOffset(DataType.EXERCISE_STEP_COUNT, offset)};
mBinderService.resetDataTypeOffsets(offsets);
verify(mockHalAdapter).resetDataTypeOffsets(offsets);
}
@Test
public void setOemCustomConfiguration_forwardsToHalAdapter() throws RemoteException {
int configId = 1;
byte[] configValue = {1};
mBinderService.setOemCustomConfiguration(configId, configValue);
verify(mockHalAdapter).setOemCustomConfiguration(configId, configValue);
}
@Test
public void onHalConnected_callbackSet_forwardsToCallback() throws RemoteException {
mBinderService.setHealthServiceCallback(mockIHealthServiceCallback);
mService.onHalConnected();
verify(mockIHealthServiceCallback).onHalServiceConnected();
}
@Test
public void onHalDied_callbackSet_forwardsToCallback() throws RemoteException {
mBinderService.setHealthServiceCallback(mockIHealthServiceCallback);
mService.onHalDied();
verify(mockIHealthServiceCallback).onHalServiceDied();
}
@Test
public void onAutoExerciseEvent_callbackSet_forwardsToCallback() throws RemoteException {
mBinderService.setHealthServiceCallback(mockIHealthServiceCallback);
AutoPauseEvent autoPauseEvent = new AutoPauseEvent();
autoPauseEvent.type = AutoPauseResumeEventType.AUTO_PAUSE_DETECTED;
autoPauseEvent.elapsedTimeSinceBootNanos = 123;
AutoExerciseEvent autoExerciseEvent = new AutoExerciseEvent();
autoExerciseEvent.setAutoPauseEvent(autoPauseEvent);
mService.onAutoExerciseEvent(autoExerciseEvent);
verify(mockIHealthServiceCallback).onAutoExerciseEvent(autoExerciseEvent);
}
private DataTypeGoal getDataTypeGoal(int dataType, int value) {
DataTypeGoal dataTypeGoal = new DataTypeGoal();
dataTypeGoal.dataType = dataType;
dataTypeGoal.threshold = new GoalThreshold();
dataTypeGoal.threshold.type = GoalThresholdType.INT;
dataTypeGoal.threshold.intValue = value;
return dataTypeGoal;
}
private TrackingConfig getTrackingConfig(
int exerciseType, boolean enableGpsControl, int maxReportLatencyMs, int samplingPeriodMs) {
TrackingConfig config = new TrackingConfig();
config.exerciseType = exerciseType;
config.enableGpsControl = enableGpsControl;
config.maxReportLatencyMs = maxReportLatencyMs;
config.samplingPeriodMs = samplingPeriodMs;
return config;
}
private DataTypeOffset getDataTypeOffset(int dataType, int offset) {
DataTypeOffset dataTypeOffset = new DataTypeOffset();
dataTypeOffset.dataType = dataType;
OffsetValue offsetValue = new OffsetValue();
offsetValue.type = OffsetValueType.INT;
offsetValue.intValue = offset;
dataTypeOffset.offsetValue = offsetValue;
return dataTypeOffset;
}
}