1 /* 2 * Copyright (C) 2017 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 package com.android.internal.telephony; 17 18 import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE; 19 import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED; 20 import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertNotEquals; 24 import static org.mockito.Matchers.anyBoolean; 25 import static org.mockito.Matchers.anyInt; 26 import static org.mockito.Matchers.eq; 27 import static org.mockito.Matchers.nullable; 28 import static org.mockito.Mockito.doReturn; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.reset; 31 import static org.mockito.Mockito.verify; 32 33 import static java.util.Arrays.asList; 34 35 import android.annotation.IntDef; 36 import android.app.UiModeManager; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.hardware.radio.V1_5.IndicationFilter; 40 import android.net.ConnectivityManager; 41 import android.net.TetheringManager; 42 import android.os.BatteryManager; 43 import android.os.Message; 44 import android.test.suitebuilder.annotation.MediumTest; 45 import android.testing.AndroidTestingRunner; 46 import android.testing.TestableLooper; 47 48 import org.junit.After; 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.ArgumentCaptor; 53 import org.mockito.Mock; 54 55 import java.lang.annotation.Retention; 56 import java.lang.annotation.RetentionPolicy; 57 import java.util.ArrayList; 58 import java.util.Map; 59 60 @RunWith(AndroidTestingRunner.class) 61 @TestableLooper.RunWithLooper 62 public class DeviceStateMonitorTest extends TelephonyTest { 63 private static final int INDICATION_FILTERS_MINIMUM = IndicationFilter.REGISTRATION_FAILURE; 64 65 // All implemented indiation filters set so far 66 // which is a subset of IndicationFilter.ALL 67 private static final int INDICATION_FILTERS_ALL = 68 IndicationFilter.SIGNAL_STRENGTH 69 | IndicationFilter.FULL_NETWORK_STATE 70 | IndicationFilter.DATA_CALL_DORMANCY_CHANGED 71 | IndicationFilter.LINK_CAPACITY_ESTIMATE 72 | IndicationFilter.PHYSICAL_CHANNEL_CONFIG 73 | IndicationFilter.REGISTRATION_FAILURE 74 | IndicationFilter.BARRING_INFO; 75 76 // INDICATION_FILTERS_ALL but excludes Indication.SIGNAL_STRENGTH 77 private static final int INDICATION_FILTERS_WHEN_TETHERING_ON = 78 INDICATION_FILTERS_ALL & ~IndicationFilter.SIGNAL_STRENGTH; 79 private static final int INDICATION_FILTERS_WHEN_CHARGING = INDICATION_FILTERS_ALL; 80 private static final int INDICATION_FILTERS_WHEN_SCREEN_ON = INDICATION_FILTERS_ALL; 81 82 /** @hide */ 83 @IntDef(prefix = {"STATE_TYPE_"}, value = { 84 STATE_TYPE_RIL_CONNECTED, 85 STATE_TYPE_SCREEN, 86 STATE_TYPE_POWER_SAVE_MODE, 87 STATE_TYPE_CHARGING, 88 STATE_TYPE_TETHERING, 89 STATE_TYPE_RADIO_AVAILABLE, 90 STATE_TYPE_WIFI_CONNECTED, 91 STATE_TYPE_ALWAYS_SIGNAL_STRENGTH_REPORTED, 92 }) 93 @Retention(RetentionPolicy.SOURCE) 94 private @interface StateType {} 95 96 // Keep the same value as correspoinding event 97 // See state2Event() for detail 98 private static final int STATE_TYPE_RIL_CONNECTED = 0; 99 // EVENT_UPDATE_NODE_CHANGED is not here, it will be removed in aosp soon 100 private static final int STATE_TYPE_SCREEN = 2; 101 private static final int STATE_TYPE_POWER_SAVE_MODE = 3; 102 private static final int STATE_TYPE_CHARGING = 4; 103 private static final int STATE_TYPE_TETHERING = 5; 104 private static final int STATE_TYPE_RADIO_AVAILABLE = 6; 105 private static final int STATE_TYPE_WIFI_CONNECTED = 7; 106 private static final int STATE_TYPE_ALWAYS_SIGNAL_STRENGTH_REPORTED = 8; 107 108 /** @hide */ 109 @IntDef(prefix = {"STATE_"}, value = { 110 STATE_OFF, 111 STATE_ON 112 }) 113 @Retention(RetentionPolicy.SOURCE) 114 private @interface StateStatus {} 115 116 private static final int STATE_OFF = 0; 117 private static final int STATE_ON = 1; 118 119 // The keys are the single IndicationFilter flags, 120 // The values are the array of states, when one state turn on, the corresponding 121 // IndicationFilter flag should NOT be turned off. 122 private static final Map<Integer, int[]> INDICATION_FILTER_2_TRIGGERS = Map.of( 123 IndicationFilter.SIGNAL_STRENGTH, new int[] { 124 STATE_TYPE_ALWAYS_SIGNAL_STRENGTH_REPORTED, STATE_TYPE_CHARGING, STATE_TYPE_SCREEN}, 125 IndicationFilter.FULL_NETWORK_STATE, new int[] { 126 STATE_TYPE_CHARGING, STATE_TYPE_SCREEN, STATE_TYPE_TETHERING}, 127 IndicationFilter.DATA_CALL_DORMANCY_CHANGED, new int[] { 128 STATE_TYPE_CHARGING, STATE_TYPE_SCREEN, STATE_TYPE_TETHERING}, 129 IndicationFilter.LINK_CAPACITY_ESTIMATE, new int[] { 130 STATE_TYPE_CHARGING, STATE_TYPE_SCREEN, STATE_TYPE_TETHERING}, 131 IndicationFilter.PHYSICAL_CHANNEL_CONFIG, new int[] { 132 STATE_TYPE_CHARGING, STATE_TYPE_SCREEN, STATE_TYPE_TETHERING} 133 ); 134 135 @Mock 136 UiModeManager mUiModeManager; 137 private DeviceStateMonitor mDSM; 138 // Given a stateType, return the event type that can change the state state2Event(@tateType int stateType)139 private int state2Event(@StateType int stateType) { 140 // As long as we keep the same value, we can directly return the stateType 141 return stateType; 142 } 143 updateState(@tateType int stateType, @StateStatus int stateValue)144 private void updateState(@StateType int stateType, @StateStatus int stateValue) { 145 final int event = state2Event(stateType); 146 mDSM.obtainMessage(event, stateValue, 0 /* arg2, not used*/).sendToTarget(); 147 processAllMessages(); 148 } 149 updateAllStatesToOff()150 private void updateAllStatesToOff() { 151 updateState(STATE_TYPE_RIL_CONNECTED, STATE_OFF); 152 updateState(STATE_TYPE_SCREEN, STATE_OFF); 153 updateState(STATE_TYPE_POWER_SAVE_MODE, STATE_OFF); 154 updateState(STATE_TYPE_CHARGING, STATE_OFF); 155 updateState(STATE_TYPE_TETHERING, STATE_OFF); 156 updateState(STATE_TYPE_RADIO_AVAILABLE, STATE_OFF); 157 updateState(STATE_TYPE_WIFI_CONNECTED, STATE_OFF); 158 updateState(STATE_TYPE_ALWAYS_SIGNAL_STRENGTH_REPORTED, STATE_OFF); 159 } 160 161 @Before setUp()162 public void setUp() throws Exception { 163 super.setUp(getClass().getSimpleName()); 164 mContextFixture.setSystemService(Context.UI_MODE_SERVICE, mUiModeManager); 165 // We don't even need a mock executor, we just need to not throw. 166 doReturn(null).when(mContextFixture.getTestDouble()).getMainExecutor(); 167 mDSM = new DeviceStateMonitor(mPhone); 168 169 // Initialize with ALL states off 170 updateAllStatesToOff(); 171 172 // eliminate the accumuted impact on Mockito.verify() 173 reset(mSimulatedCommandsVerifier); 174 } 175 176 @After tearDown()177 public void tearDown() throws Exception { 178 mDSM = null; 179 super.tearDown(); 180 } 181 182 /** 183 * Verify the behavior of CI.setUnsolResponseFilter(). 184 * Keeping other state unchanged, when one state change. setUnsolResponseFilter() 185 * should be called with right IndicationFilter flag set. 186 */ 187 @Test @MediumTest testSetUnsolResponseFilter_singleStateChange()188 public void testSetUnsolResponseFilter_singleStateChange() { 189 for (int indicationFilter : INDICATION_FILTER_2_TRIGGERS.keySet()) { 190 for (int state : INDICATION_FILTER_2_TRIGGERS.get(indicationFilter)) { 191 verifySetUnsolResponseFilter(state, indicationFilter); 192 } 193 } 194 } 195 verifySetUnsolResponseFilter(int state, int indicationFilter)196 private void verifySetUnsolResponseFilter(int state, int indicationFilter) { 197 reset(mSimulatedCommandsVerifier); 198 // In the beginning, all states are off 199 200 // Turn on the state 201 updateState(state, STATE_ON); 202 203 // Keep other states off, then specified indication filter should NOT be turn off 204 ArgumentCaptor<Integer> acIndicationFilter = ArgumentCaptor.forClass(Integer.class); 205 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter( 206 acIndicationFilter.capture(), nullable(Message.class)); 207 assertNotEquals((acIndicationFilter.getValue() & indicationFilter), 0); 208 209 // Turn off the state again 210 updateState(state, STATE_OFF); 211 212 // Keep other states off, then no filter flag is on 213 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter( 214 eq(INDICATION_FILTERS_MINIMUM), nullable(Message.class)); 215 } 216 217 @Test testSetUnsolResponseFilter_noReduandantCall()218 public void testSetUnsolResponseFilter_noReduandantCall() { 219 // initially all state off, turn screen on 220 updateState(STATE_TYPE_SCREEN, STATE_ON); 221 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter(anyInt(), 222 nullable(Message.class)); 223 reset(mSimulatedCommandsVerifier); 224 225 updateState(STATE_TYPE_CHARGING, STATE_ON); 226 verify(mSimulatedCommandsVerifier, never()).setUnsolResponseFilter(anyInt(), 227 nullable(Message.class)); 228 229 updateState(STATE_TYPE_POWER_SAVE_MODE, STATE_ON); 230 verify(mSimulatedCommandsVerifier, never()).setUnsolResponseFilter(anyInt(), 231 nullable(Message.class)); 232 } 233 234 @Test testScreenOnOff()235 public void testScreenOnOff() { 236 // screen was off by default, turn it on now 237 updateState(STATE_TYPE_SCREEN, STATE_ON); 238 processAllMessages(); 239 240 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter( 241 eq(INDICATION_FILTERS_WHEN_SCREEN_ON), nullable(Message.class)); 242 243 // turn screen off 244 updateState(STATE_TYPE_SCREEN, STATE_OFF); 245 processAllMessages(); 246 247 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter( 248 eq(INDICATION_FILTERS_MINIMUM), nullable(Message.class)); 249 } 250 251 @Test testTethering()252 public void testTethering() { 253 // Turn tethering on 254 Intent intent = new Intent(TetheringManager.ACTION_TETHER_STATE_CHANGED); 255 intent.putExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, new ArrayList<>(asList("abc"))); 256 mContext.sendBroadcast(intent); 257 processAllMessages(); 258 259 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter( 260 eq(INDICATION_FILTERS_WHEN_TETHERING_ON), nullable(Message.class)); 261 262 // Turn tethering off 263 intent = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); 264 intent.putExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, new ArrayList<>()); 265 mContext.sendBroadcast(intent); 266 processAllMessages(); 267 268 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter( 269 eq(INDICATION_FILTERS_MINIMUM), nullable(Message.class)); 270 271 verify(mSimulatedCommandsVerifier).sendDeviceState(eq(LOW_DATA_EXPECTED), 272 eq(true), nullable(Message.class)); 273 } 274 275 @Test testCharging()276 public void testCharging() { 277 // Charging 278 Intent intent = new Intent(BatteryManager.ACTION_CHARGING); 279 mContext.sendBroadcast(intent); 280 processAllMessages(); 281 282 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter( 283 eq(INDICATION_FILTERS_WHEN_CHARGING), nullable(Message.class)); 284 verify(mSimulatedCommandsVerifier).sendDeviceState(eq(CHARGING_STATE), 285 eq(true), nullable(Message.class)); 286 287 // Not charging 288 intent = new Intent(BatteryManager.ACTION_DISCHARGING); 289 mContext.sendBroadcast(intent); 290 processAllMessages(); 291 292 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter( 293 eq(INDICATION_FILTERS_MINIMUM), nullable(Message.class)); 294 verify(mSimulatedCommandsVerifier).sendDeviceState(eq(LOW_DATA_EXPECTED), 295 eq(true), nullable(Message.class)); 296 verify(mSimulatedCommandsVerifier).sendDeviceState(eq(CHARGING_STATE), 297 eq(false), nullable(Message.class)); 298 } 299 300 @Test testReset()301 public void testReset() { 302 testResetFromEvent(DeviceStateMonitor.EVENT_RIL_CONNECTED); 303 testResetFromEvent(DeviceStateMonitor.EVENT_RADIO_AVAILABLE); 304 } 305 testResetFromEvent(int event)306 private void testResetFromEvent(int event) { 307 reset(mSimulatedCommandsVerifier); 308 mDSM.obtainMessage(event).sendToTarget(); 309 processAllMessages(); 310 311 verify(mSimulatedCommandsVerifier).sendDeviceState(eq(CHARGING_STATE), 312 anyBoolean(), nullable(Message.class)); 313 verify(mSimulatedCommandsVerifier).sendDeviceState(eq(LOW_DATA_EXPECTED), 314 anyBoolean(), nullable(Message.class)); 315 verify(mSimulatedCommandsVerifier).sendDeviceState(eq(POWER_SAVE_MODE), 316 anyBoolean(), nullable(Message.class)); 317 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter(anyInt(), 318 nullable(Message.class)); 319 } 320 321 @Test 322 @MediumTest testComputeCellInfoMinInternal()323 public void testComputeCellInfoMinInternal() { 324 // by default, screen is off, charging is off and wifi is off 325 assertEquals( 326 DeviceStateMonitor.CELL_INFO_INTERVAL_LONG_MS, mDSM.computeCellInfoMinInterval()); 327 328 // keep screen off, but turn charging on 329 updateState(STATE_TYPE_CHARGING, STATE_ON); 330 assertEquals( 331 DeviceStateMonitor.CELL_INFO_INTERVAL_LONG_MS, mDSM.computeCellInfoMinInterval()); 332 333 // turn screen on, turn charging off and keep wifi off 334 updateState(STATE_TYPE_SCREEN, STATE_ON); 335 updateState(STATE_TYPE_CHARGING, STATE_OFF); 336 assertEquals( 337 DeviceStateMonitor.CELL_INFO_INTERVAL_SHORT_MS, mDSM.computeCellInfoMinInterval()); 338 339 // screen on, but on wifi 340 updateState(STATE_TYPE_WIFI_CONNECTED, STATE_ON); 341 assertEquals( 342 DeviceStateMonitor.CELL_INFO_INTERVAL_LONG_MS, mDSM.computeCellInfoMinInterval()); 343 344 // screen on, charging 345 updateState(STATE_TYPE_WIFI_CONNECTED, STATE_OFF); 346 updateState(STATE_TYPE_CHARGING, STATE_OFF); 347 assertEquals( 348 DeviceStateMonitor.CELL_INFO_INTERVAL_SHORT_MS, mDSM.computeCellInfoMinInterval()); 349 } 350 351 @Test testGetBarringInfo()352 public void testGetBarringInfo() { 353 // At beginning, all states off. Now turn screen on 354 updateState(STATE_TYPE_SCREEN, STATE_ON); 355 356 ArgumentCaptor<Integer> acBarringInfo = ArgumentCaptor.forClass(Integer.class); 357 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter(acBarringInfo.capture(), 358 nullable(Message.class)); 359 assertNotEquals((acBarringInfo.getValue() & IndicationFilter.BARRING_INFO), 0); 360 verify(mSimulatedCommandsVerifier).getBarringInfo(nullable(Message.class)); 361 362 reset(mSimulatedCommandsVerifier); 363 364 // Turn screen off 365 updateState(STATE_TYPE_SCREEN, STATE_OFF); 366 verify(mSimulatedCommandsVerifier, never()).getBarringInfo(nullable(Message.class)); 367 verify(mSimulatedCommandsVerifier).setUnsolResponseFilter(acBarringInfo.capture(), 368 nullable(Message.class)); 369 assertEquals((acBarringInfo.getValue() & IndicationFilter.BARRING_INFO), 0); 370 371 reset(mSimulatedCommandsVerifier); 372 373 // Turn tethering on, then screen on, getBarringInfo() should only be called once 374 updateState(STATE_TYPE_TETHERING, STATE_ON); 375 updateState(STATE_TYPE_SCREEN, STATE_ON); 376 verify(mSimulatedCommandsVerifier).getBarringInfo(nullable(Message.class)); 377 } 378 } 379