1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.timedetector; 18 19 import static org.junit.Assert.assertTrue; 20 import static org.mockito.ArgumentMatchers.eq; 21 import static org.mockito.Mockito.any; 22 import static org.mockito.Mockito.anyLong; 23 import static org.mockito.Mockito.doReturn; 24 import static org.mockito.Mockito.reset; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.verifyZeroInteractions; 27 import static org.mockito.Mockito.when; 28 29 import android.app.AlarmManager; 30 import android.app.AlarmManager.OnAlarmListener; 31 import android.app.time.UnixEpochTime; 32 import android.content.Context; 33 import android.location.Location; 34 import android.location.LocationListener; 35 import android.location.LocationManager; 36 import android.location.LocationManagerInternal; 37 import android.location.LocationRequest; 38 import android.location.LocationTime; 39 40 import androidx.test.runner.AndroidJUnit4; 41 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 import org.mockito.ArgumentCaptor; 46 import org.mockito.Mock; 47 import org.mockito.MockitoAnnotations; 48 49 @RunWith(AndroidJUnit4.class) 50 public final class GnssTimeUpdateServiceTest { 51 private static final long GNSS_TIME = 999_999_999L; 52 private static final long ELAPSED_REALTIME_NS = 123_000_000L; 53 private static final long ELAPSED_REALTIME_MS = ELAPSED_REALTIME_NS / 1_000_000L; 54 55 @Mock private Context mMockContext; 56 @Mock private AlarmManager mMockAlarmManager; 57 @Mock private LocationManager mMockLocationManager; 58 @Mock private LocationManagerInternal mMockLocationManagerInternal; 59 @Mock private TimeDetectorInternal mMockTimeDetectorInternal; 60 61 private GnssTimeUpdateService mGnssTimeUpdateService; 62 63 @Before setUp()64 public void setUp() { 65 MockitoAnnotations.initMocks(this); 66 67 installGpsProviderInMockLocationManager(); 68 69 mGnssTimeUpdateService = new GnssTimeUpdateService( 70 mMockContext, mMockAlarmManager, mMockLocationManager, mMockLocationManagerInternal, 71 mMockTimeDetectorInternal); 72 } 73 74 @Test testLocationListenerOnLocationChanged_validLocationTime_suggestsGnssTime()75 public void testLocationListenerOnLocationChanged_validLocationTime_suggestsGnssTime() { 76 UnixEpochTime timeSignal = new UnixEpochTime( 77 ELAPSED_REALTIME_MS, GNSS_TIME); 78 GnssTimeSuggestion timeSuggestion = new GnssTimeSuggestion(timeSignal); 79 LocationTime locationTime = new LocationTime(GNSS_TIME, ELAPSED_REALTIME_NS); 80 doReturn(locationTime).when(mMockLocationManagerInternal).getGnssTimeMillis(); 81 82 assertTrue(mGnssTimeUpdateService.startGnssListeningInternal()); 83 84 ArgumentCaptor<LocationListener> locationListenerCaptor = 85 ArgumentCaptor.forClass(LocationListener.class); 86 verify(mMockLocationManager).requestLocationUpdates( 87 eq(LocationManager.GPS_PROVIDER), 88 eq(new LocationRequest.Builder(LocationRequest.PASSIVE_INTERVAL) 89 .setMinUpdateIntervalMillis(0) 90 .build()), 91 any(), 92 locationListenerCaptor.capture()); 93 LocationListener locationListener = locationListenerCaptor.getValue(); 94 Location location = new Location(LocationManager.GPS_PROVIDER); 95 96 locationListener.onLocationChanged(location); 97 98 verify(mMockLocationManager).removeUpdates(locationListener); 99 verify(mMockTimeDetectorInternal).suggestGnssTime(timeSuggestion); 100 verify(mMockAlarmManager).set( 101 eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), 102 anyLong(), 103 any(), 104 any(), 105 any()); 106 } 107 108 @Test testLocationListenerOnLocationChanged_nullLocationTime_doesNotSuggestGnssTime()109 public void testLocationListenerOnLocationChanged_nullLocationTime_doesNotSuggestGnssTime() { 110 doReturn(null).when(mMockLocationManagerInternal).getGnssTimeMillis(); 111 112 assertTrue(mGnssTimeUpdateService.startGnssListeningInternal()); 113 114 ArgumentCaptor<LocationListener> locationListenerCaptor = 115 ArgumentCaptor.forClass(LocationListener.class); 116 verify(mMockLocationManager).requestLocationUpdates( 117 eq(LocationManager.GPS_PROVIDER), 118 eq(new LocationRequest.Builder(LocationRequest.PASSIVE_INTERVAL) 119 .setMinUpdateIntervalMillis(0) 120 .build()), 121 any(), 122 locationListenerCaptor.capture()); 123 LocationListener locationListener = locationListenerCaptor.getValue(); 124 Location location = new Location(LocationManager.GPS_PROVIDER); 125 126 locationListener.onLocationChanged(location); 127 128 verify(mMockLocationManager).removeUpdates(locationListener); 129 verifyZeroInteractions(mMockTimeDetectorInternal); 130 verify(mMockAlarmManager).set( 131 eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), 132 anyLong(), 133 any(), 134 any(), 135 any()); 136 } 137 138 @Test testLocationListeningRestartsAfterSleep()139 public void testLocationListeningRestartsAfterSleep() { 140 ArgumentCaptor<LocationListener> locationListenerCaptor = 141 ArgumentCaptor.forClass(LocationListener.class); 142 ArgumentCaptor<OnAlarmListener> alarmListenerCaptor = 143 ArgumentCaptor.forClass(OnAlarmListener.class); 144 145 advanceServiceToSleepingState(locationListenerCaptor, alarmListenerCaptor); 146 147 // Simulate the alarm manager's wake-up call. 148 OnAlarmListener wakeUpListener = alarmListenerCaptor.getValue(); 149 wakeUpListener.onAlarm(); 150 151 // Verify the service returned to location listening. 152 verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any()); 153 verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal); 154 } 155 156 // Tests what happens when a call is made to startGnssListeningInternal() when service is 157 // sleeping. This can happen when the start_gnss_listening shell command is used. 158 @Test testStartGnssListeningInternalCalledWhenSleeping()159 public void testStartGnssListeningInternalCalledWhenSleeping() { 160 ArgumentCaptor<LocationListener> locationListenerCaptor = 161 ArgumentCaptor.forClass(LocationListener.class); 162 ArgumentCaptor<OnAlarmListener> alarmListenerCaptor = 163 ArgumentCaptor.forClass(OnAlarmListener.class); 164 165 advanceServiceToSleepingState(locationListenerCaptor, alarmListenerCaptor); 166 167 // Call startGnssListeningInternal(), as can happen if the start_gnss_listening shell 168 // command is used. 169 assertTrue(mGnssTimeUpdateService.startGnssListeningInternal()); 170 171 // Verify the alarm manager is told to stopped sleeping and the location manager is 172 // listening again. 173 verify(mMockAlarmManager).cancel(alarmListenerCaptor.getValue()); 174 verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any()); 175 verifyZeroInteractions(mMockTimeDetectorInternal); 176 } 177 advanceServiceToSleepingState( ArgumentCaptor<LocationListener> locationListenerCaptor, ArgumentCaptor<OnAlarmListener> alarmListenerCaptor)178 private void advanceServiceToSleepingState( 179 ArgumentCaptor<LocationListener> locationListenerCaptor, 180 ArgumentCaptor<OnAlarmListener> alarmListenerCaptor) { 181 UnixEpochTime timeSignal = new UnixEpochTime( 182 ELAPSED_REALTIME_MS, GNSS_TIME); 183 GnssTimeSuggestion timeSuggestion = new GnssTimeSuggestion(timeSignal); 184 LocationTime locationTime = new LocationTime(GNSS_TIME, ELAPSED_REALTIME_NS); 185 doReturn(locationTime).when(mMockLocationManagerInternal).getGnssTimeMillis(); 186 187 assertTrue(mGnssTimeUpdateService.startGnssListeningInternal()); 188 189 verify(mMockLocationManager).requestLocationUpdates( 190 any(), any(), any(), locationListenerCaptor.capture()); 191 LocationListener locationListener = locationListenerCaptor.getValue(); 192 Location location = new Location(LocationManager.GPS_PROVIDER); 193 verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal); 194 195 locationListener.onLocationChanged(location); 196 197 verify(mMockLocationManager).removeUpdates(locationListener); 198 verify(mMockTimeDetectorInternal).suggestGnssTime(timeSuggestion); 199 200 // Verify the service is now "sleeping", i.e. waiting for a period before listening for 201 // GNSS locations again. 202 verify(mMockAlarmManager).set( 203 eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), 204 anyLong(), 205 any(), 206 alarmListenerCaptor.capture(), 207 any()); 208 209 // Reset mocks making it easier to verify the calls that follow. 210 reset(mMockAlarmManager, mMockTimeDetectorInternal, mMockLocationManager, 211 mMockLocationManagerInternal); 212 installGpsProviderInMockLocationManager(); 213 } 214 215 /** 216 * Configures the mock response to ensure {@code 217 * locationManager.hasProvider(LocationManager.GPS_PROVIDER) == true } 218 */ installGpsProviderInMockLocationManager()219 private void installGpsProviderInMockLocationManager() { 220 when(mMockLocationManager.hasProvider(LocationManager.GPS_PROVIDER)) 221 .thenReturn(true); 222 } 223 } 224