1/*
2 * Copyright (C) 2016 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
17package com.android.services.telephony;
18
19import android.os.AsyncResult;
20import android.os.Handler;
21import android.telephony.ServiceState;
22import android.support.test.runner.AndroidJUnit4;
23import android.support.test.filters.FlakyTest;
24import android.test.suitebuilder.annotation.SmallTest;
25
26import com.android.TelephonyTestBase;
27import com.android.internal.telephony.Phone;
28import com.android.internal.telephony.PhoneConstants;
29import com.android.internal.telephony.ServiceStateTracker;
30
31import org.junit.After;
32import org.junit.Before;
33import org.junit.Test;
34import org.junit.runner.RunWith;
35import org.mockito.Mock;
36
37import static org.mockito.Matchers.anyBoolean;
38import static org.mockito.Matchers.isNull;
39import static org.mockito.Mockito.never;
40import static org.mockito.Mockito.times;
41import static org.mockito.Mockito.verify;
42import static org.mockito.Mockito.any;
43import static org.mockito.Mockito.eq;
44import static org.mockito.Mockito.when;
45
46/**
47 * Tests the EmergencyCallStateListener, which listens to one Phone and waits until its service
48 * state changes to accepting emergency calls or in service. If it can not find a tower to camp onto
49 * for emergency calls, then it will fail after a timeout period.
50 */
51@RunWith(AndroidJUnit4.class)
52public class EmergencyCallStateListenerTest extends TelephonyTestBase {
53
54    private static final long TIMEOUT_MS = 100;
55
56    @Mock Phone mMockPhone;
57    @Mock ServiceStateTracker mMockServiceStateTracker;
58    @Mock EmergencyCallStateListener.Callback mCallback;
59    EmergencyCallStateListener mListener;
60
61    @Before
62    public void setUp() throws Exception {
63        super.setUp();
64        mListener = new EmergencyCallStateListener();
65    }
66
67    @After
68    public void tearDown() throws Exception {
69        mListener.getHandler().removeCallbacksAndMessages(null);
70        super.tearDown();
71    }
72
73    /**
74     * Ensure that we successfully register for the ServiceState changed messages in Telephony.
75     */
76    @Test
77    @SmallTest
78    public void testRegisterForCallback() {
79        mListener.waitForRadioOn(mMockPhone, mCallback);
80
81        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
82
83        verify(mMockPhone).unregisterForServiceStateChanged(any(Handler.class));
84        verify(mMockPhone).registerForServiceStateChanged(any(Handler.class),
85                eq(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED), isNull());
86    }
87
88    /**
89     * Prerequisites:
90     *  - Phone is IN_SERVICE
91     *  - Radio is on
92     *
93     * Test: Send SERVICE_STATE_CHANGED message
94     *
95     * Result: callback's onComplete is called with the isRadioReady=true
96     */
97    @Test
98    @SmallTest
99    public void testPhoneChangeState_InService() {
100        ServiceState state = new ServiceState();
101        state.setState(ServiceState.STATE_IN_SERVICE);
102        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
103        when(mMockPhone.getServiceStateTracker()).thenReturn(mMockServiceStateTracker);
104        when(mMockServiceStateTracker.isRadioOn()).thenReturn(true);
105        mListener.waitForRadioOn(mMockPhone, mCallback);
106        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
107
108        mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED,
109                new AsyncResult(null, state, null)).sendToTarget();
110
111        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
112        verify(mCallback).onComplete(eq(mListener), eq(true));
113    }
114
115    /**
116     * Prerequisites:
117     *  - Phone is OUT_OF_SERVICE (emergency calls only)
118     *  - Radio is on
119     *
120     * Test: Send SERVICE_STATE_CHANGED message
121     *
122     * Result: callback's onComplete is called with the isRadioReady=true
123     */
124    @Test
125    @SmallTest
126    public void testPhoneChangeState_EmergencyCalls() {
127        ServiceState state = new ServiceState();
128        state.setState(ServiceState.STATE_OUT_OF_SERVICE);
129        state.setEmergencyOnly(true);
130        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
131        when(mMockPhone.getServiceState()).thenReturn(state);
132        when(mMockPhone.getServiceStateTracker()).thenReturn(mMockServiceStateTracker);
133        when(mMockServiceStateTracker.isRadioOn()).thenReturn(true);
134        mListener.waitForRadioOn(mMockPhone, mCallback);
135        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
136
137        mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED,
138                new AsyncResult(null, state, null)).sendToTarget();
139
140        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
141        verify(mCallback).onComplete(eq(mListener), eq(true));
142    }
143
144    /**
145     * Prerequisites:
146     *  - Phone is OUT_OF_SERVICE
147     *  - Radio is on
148     *
149     * Test: Send SERVICE_STATE_CHANGED message
150     *
151     * Result: callback's onComplete is called with the isRadioReady=true. Even though the radio is
152     * not reporting emergency calls only, we still send onComplete so that the radio can trigger
153     * the emergency call.
154     */
155    @Test
156    @SmallTest
157    public void testPhoneChangeState_OutOfService() {
158        ServiceState state = new ServiceState();
159        state.setState(ServiceState.STATE_OUT_OF_SERVICE);
160        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
161        when(mMockPhone.getServiceState()).thenReturn(state);
162        when(mMockPhone.getServiceStateTracker()).thenReturn(mMockServiceStateTracker);
163        when(mMockServiceStateTracker.isRadioOn()).thenReturn(true);
164        mListener.waitForRadioOn(mMockPhone, mCallback);
165        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
166
167        // Still expect an answer because we will be sending the onComplete message as soon as the
168        // radio is confirmed to be on, whether or not it is out of service or not.
169        mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED,
170                new AsyncResult(null, state, null)).sendToTarget();
171
172        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
173        verify(mCallback).onComplete(eq(mListener), eq(true));
174    }
175
176    /**
177     * Prerequisites:
178     *  - Phone is OUT_OF_SERVICE (emergency calls only)
179     *  - Radio is on
180     *
181     * Test: Wait for retry timer to complete (don't send ServiceState changed message)
182     *
183     * Result: callback's onComplete is called with the isRadioReady=true.
184     */
185    @Test
186    @FlakyTest
187    @SmallTest
188    public void testTimeout_EmergencyCalls() {
189        ServiceState state = new ServiceState();
190        state.setState(ServiceState.STATE_OUT_OF_SERVICE);
191        state.setEmergencyOnly(true);
192        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
193        when(mMockPhone.getServiceState()).thenReturn(state);
194        when(mMockPhone.getServiceStateTracker()).thenReturn(mMockServiceStateTracker);
195        when(mMockServiceStateTracker.isRadioOn()).thenReturn(true);
196        mListener.setTimeBetweenRetriesMillis(100);
197
198        // Wait for the timer to expire and check state manually in onRetryTimeout
199        mListener.waitForRadioOn(mMockPhone, mCallback);
200        waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, 500);
201
202        verify(mCallback).onComplete(eq(mListener), eq(true));
203    }
204
205    /**
206     * Prerequisites:
207     *  - Phone is OUT_OF_SERVICE
208     *  - Radio is off
209     *
210     * Test: Wait for retry timer to complete, no ServiceState changed messages received.
211     *
212     * Result:
213     * - callback's onComplete is called with the isRadioReady=false.
214     * - setRadioPower was send twice (tried to turn on the radio)
215     */
216    @Test
217    @FlakyTest
218    @SmallTest
219    public void testTimeout_RetryFailure() {
220        ServiceState state = new ServiceState();
221        state.setState(ServiceState.STATE_POWER_OFF);
222        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
223        when(mMockPhone.getServiceState()).thenReturn(state);
224        when(mMockPhone.getServiceStateTracker()).thenReturn(mMockServiceStateTracker);
225        when(mMockServiceStateTracker.isRadioOn()).thenReturn(false);
226        mListener.setTimeBetweenRetriesMillis(50);
227        mListener.setMaxNumRetries(2);
228
229        // Wait for the timer to expire and check state manually in onRetryTimeout
230        mListener.waitForRadioOn(mMockPhone, mCallback);
231        waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, 500);
232
233        verify(mCallback).onComplete(eq(mListener), eq(false));
234        verify(mMockPhone, times(2)).setRadioPower(eq(true));
235    }
236
237}
238