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