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 package com.android.internal.telephony; 17 18 import android.os.Handler; 19 import android.os.HandlerThread; 20 import android.os.Message; 21 import android.telephony.PhoneNumberUtils; 22 import android.telephony.ServiceState; 23 import android.test.suitebuilder.annotation.SmallTest; 24 25 import org.junit.After; 26 import org.junit.Before; 27 import org.junit.Test; 28 import org.mockito.ArgumentCaptor; 29 import org.mockito.Mock; 30 import org.mockito.Mockito; 31 32 import java.lang.reflect.Field; 33 34 import static org.junit.Assert.assertEquals; 35 import static org.junit.Assert.assertFalse; 36 import static org.junit.Assert.assertTrue; 37 import static org.mockito.Matchers.anyBoolean; 38 import static org.mockito.Matchers.anyInt; 39 import static org.mockito.Matchers.eq; 40 import static org.mockito.Matchers.isA; 41 import static org.mockito.Matchers.isNull; 42 import static org.mockito.Mockito.doReturn; 43 import static org.mockito.Mockito.times; 44 import static org.mockito.Mockito.verify; 45 import static org.mockito.Mockito.mock; 46 import static org.mockito.Mockito.any; 47 import static org.mockito.Mockito.anyChar; 48 import static org.mockito.Mockito.anyString; 49 50 public class CallManagerTest extends TelephonyTest { 51 52 @Mock 53 GsmCdmaCall mFgCall; 54 @Mock 55 GsmCdmaCall mBgCall; 56 @Mock 57 GsmCdmaCall mRingingCall; 58 @Mock 59 Phone mSecondPhone; 60 61 private CallManagerHandlerThread mCallManagerHandlerThread; 62 private Handler mHandler; 63 private static final int PHONE_REGISTER_EVENT = 0; 64 65 private class CallManagerHandlerThread extends HandlerThread { 66 private CallManagerHandlerThread(String name) { 67 super(name); 68 } 69 @Override 70 public void onLooperPrepared() { 71 /* CallManager is a static object with private constructor,no need call constructor */ 72 registerForPhone(mPhone); 73 74 // create a custom handler for the Handler Thread 75 mHandler = new Handler(mCallManagerHandlerThread.getLooper()) { 76 @Override 77 public void handleMessage(Message msg) { 78 switch (msg.what) { 79 case PHONE_REGISTER_EVENT: 80 logd("Phone registered with CallManager"); 81 registerForPhone((Phone) msg.obj); 82 setReady(true); 83 break; 84 default: 85 logd("Unknown Event " + msg.what); 86 } 87 } 88 }; 89 90 setReady(true); 91 } 92 93 private void registerForPhone(Phone mPhone) { 94 CallManager.getInstance().registerPhone(mPhone); 95 } 96 } 97 98 @Before 99 public void setUp() throws Exception { 100 super.setUp(this.getClass().getSimpleName()); 101 restoreInstance(CallManager.class, "INSTANCE", null); 102 /* Mock Phone and Call, initially all calls are idle */ 103 doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState(); 104 doReturn(mBgCall).when(mPhone).getBackgroundCall(); 105 doReturn(mFgCall).when(mPhone).getForegroundCall(); 106 doReturn(mRingingCall).when(mPhone).getRingingCall(); 107 doReturn(mPhone).when(mBgCall).getPhone(); 108 doReturn(mPhone).when(mFgCall).getPhone(); 109 doReturn(mPhone).when(mRingingCall).getPhone(); 110 doReturn(Call.State.IDLE).when(mBgCall).getState(); 111 doReturn(Call.State.IDLE).when(mFgCall).getState(); 112 doReturn(Call.State.IDLE).when(mRingingCall).getState(); 113 doReturn(true).when(mBgCall).isIdle(); 114 doReturn(true).when(mFgCall).isIdle(); 115 doReturn(true).when(mRingingCall).isIdle(); 116 117 mCallManagerHandlerThread = new CallManagerHandlerThread(TAG); 118 mCallManagerHandlerThread.start(); 119 waitUntilReady(); 120 } 121 122 @After 123 public void tearDown() throws Exception { 124 CallManager.getInstance().unregisterPhone(mPhone); 125 mCallManagerHandlerThread.quit(); 126 super.tearDown(); 127 } 128 129 @SmallTest @Test 130 public void testSanity() { 131 assertEquals(mPhone, CallManager.getInstance().getDefaultPhone()); 132 assertFalse(CallManager.getInstance().hasActiveBgCall()); 133 assertFalse(CallManager.getInstance().hasActiveRingingCall()); 134 assertFalse(CallManager.getInstance().hasActiveFgCall()); 135 /* return the default phone if there is no any active call */ 136 assertEquals(mPhone, CallManager.getInstance().getRingingPhone()); 137 assertEquals(mPhone, CallManager.getInstance().getBgPhone()); 138 assertEquals(mPhone, CallManager.getInstance().getFgPhone()); 139 } 140 141 @SmallTest @Test 142 public void testBasicDial() throws Exception { 143 //verify can dial and dial function of the phone is being triggered 144 CallManager.getInstance().dial(mPhone, 145 PhoneNumberUtils.stripSeparators("+17005554141"), 0); 146 ArgumentCaptor<String> mCaptorString = ArgumentCaptor.forClass(String.class); 147 ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsCaptor = 148 ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class); 149 verify(mPhone, times(1)).dial(mCaptorString.capture(), dialArgsCaptor.capture()); 150 assertEquals(PhoneNumberUtils.stripSeparators("+17005554141"), 151 mCaptorString.getValue()); 152 assertEquals(0, dialArgsCaptor.getValue().videoState); 153 } 154 155 @SmallTest @Test 156 public void testBasicAcceptCall() throws Exception { 157 CallManager.getInstance().acceptCall(mRingingCall); 158 verify(mPhone, times(1)).acceptCall(anyInt()); 159 } 160 161 @SmallTest @Test 162 public void testBasicRejectCall() throws Exception { 163 //verify can dial and dial function of the phone is being triggered 164 CallManager.getInstance().rejectCall(mRingingCall); 165 verify(mPhone, times(1)).rejectCall(); 166 } 167 168 @SmallTest @Test 169 public void testSendDtmf() throws Exception { 170 CallManager.getInstance().sendDtmf('a'); 171 verify(mPhone, times(0)).sendDtmf(eq('a')); 172 173 //has active fg Call 174 doReturn(false).when(mFgCall).isIdle(); 175 assertEquals(mFgCall, CallManager.getInstance().getActiveFgCall()); 176 CallManager.getInstance().sendDtmf('a'); 177 verify(mPhone, times(1)).sendDtmf(eq('a')); 178 } 179 180 @SmallTest @Test 181 public void testStartDtmf() throws Exception { 182 doReturn(true).when(mFgCall).isIdle(); 183 assertFalse(CallManager.getInstance().startDtmf('a')); 184 verify(mPhone, times(0)).startDtmf(anyChar()); 185 186 //has active fg Call 187 doReturn(false).when(mFgCall).isIdle(); 188 assertEquals(mFgCall, CallManager.getInstance().getActiveFgCall()); 189 assertTrue(CallManager.getInstance().startDtmf('a')); 190 verify(mPhone, times(1)).startDtmf('a'); 191 } 192 193 @SmallTest @Test 194 public void testStopDtmf() throws Exception { 195 doReturn(true).when(mFgCall).isIdle(); 196 CallManager.getInstance().stopDtmf(); 197 verify(mPhone, times(0)).stopDtmf(); 198 199 //has active fg Call 200 doReturn(false).when(mFgCall).isIdle(); 201 assertEquals(mPhone, CallManager.getInstance().getFgPhone()); 202 CallManager.getInstance().stopDtmf(); 203 verify(mPhone, times(1)).stopDtmf(); 204 } 205 206 @SmallTest @Test 207 public void testSendBurstDtmf() throws Exception { 208 doReturn(true).when(mFgCall).isIdle(); 209 assertFalse(CallManager.getInstance().sendBurstDtmf("12*#", 0, 0, null)); 210 verify(mPhone, times(0)).sendBurstDtmf(anyString(), anyInt(), anyInt(), (Message) any()); 211 212 //has active fg Call 213 doReturn(false).when(mFgCall).isIdle(); 214 assertTrue(CallManager.getInstance().sendBurstDtmf("12*#", 0, 0, null)); 215 verify(mPhone, times(1)).sendBurstDtmf("12*#", 0, 0, null); 216 } 217 218 @SmallTest @Test 219 public void testSetGetMute() throws Exception { 220 CallManager.getInstance().setMute(false); 221 verify(mPhone, times(0)).setMute(anyBoolean()); 222 223 //has active fg Call 224 doReturn(false).when(mFgCall).isIdle(); 225 CallManager.getInstance().setMute(false); 226 verify(mPhone, times(1)).setMute(false); 227 228 CallManager.getInstance().setMute(true); 229 verify(mPhone, times(1)).setMute(true); 230 } 231 232 @SmallTest @Test 233 public void testSwitchHoldingAndActive() throws Exception { 234 /* case 1: only active call */ 235 doReturn(false).when(mFgCall).isIdle(); 236 CallManager.getInstance().switchHoldingAndActive(null); 237 verify(mPhone, times(1)).switchHoldingAndActive(); 238 /* case 2: no active call but only held call, aka, unhold */ 239 doReturn(true).when(mFgCall).isIdle(); 240 CallManager.getInstance().switchHoldingAndActive(mBgCall); 241 verify(mPhone, times(2)).switchHoldingAndActive(); 242 /* case 3: both active and held calls from same phone, aka, swap */ 243 doReturn(false).when(mFgCall).isIdle(); 244 CallManager.getInstance().switchHoldingAndActive(mBgCall); 245 verify(mPhone, times(3)).switchHoldingAndActive(); 246 GsmCdmaPhone mPhoneHold = Mockito.mock(GsmCdmaPhone.class); 247 /* case 4: active and held calls from different phones, aka, phone swap */ 248 doReturn(mPhoneHold).when(mBgCall).getPhone(); 249 CallManager.getInstance().switchHoldingAndActive(mBgCall); 250 verify(mPhone, times(4)).switchHoldingAndActive(); 251 verify(mPhoneHold, times(1)).switchHoldingAndActive(); 252 } 253 254 @SmallTest @Test 255 public void testHangupForegroundResumeBackground() throws Exception { 256 CallManager.getInstance().hangupForegroundResumeBackground(mBgCall); 257 /* no active fgCall */ 258 verify(mPhone, times(0)).switchHoldingAndActive(); 259 verify(mFgCall, times(0)).hangup(); 260 261 /* have active foreground call, get hanged up */ 262 doReturn(false).when(mFgCall).isIdle(); 263 CallManager.getInstance().hangupForegroundResumeBackground(mBgCall); 264 verify(mFgCall, times(1)).hangup(); 265 verify(mPhone, times(0)).switchHoldingAndActive(); 266 267 /* mock bgcall and fgcall from different phone */ 268 GsmCdmaPhone mPhoneHold = Mockito.mock(GsmCdmaPhone.class); 269 doReturn(mPhoneHold).when(mBgCall).getPhone(); 270 CallManager.getInstance().hangupForegroundResumeBackground(mBgCall); 271 verify(mFgCall, times(2)).hangup(); 272 /* always hangup fgcall and both phone trigger swap */ 273 verify(mPhoneHold, times(1)).switchHoldingAndActive(); 274 verify(mPhone, times(1)).switchHoldingAndActive(); 275 } 276 277 @SmallTest @Test 278 public void testFgCallActiveDial() throws Exception { 279 /* set Fg/Bg Call state to active, verify CallManager Logical */ 280 doReturn(false).when(mFgCall).isIdle(); 281 doReturn(false).when(mBgCall).isIdle(); 282 assertTrue(CallManager.getInstance().hasActiveFgCall()); 283 assertTrue(CallManager.getInstance().hasActiveBgCall()); 284 assertTrue(CallManager.getInstance().hasActiveFgCall(mPhone.getSubId())); 285 assertFalse(CallManager.getInstance().hasDisconnectedFgCall()); 286 /* try dial with non-idle foreground call and background call */ 287 CallManager.getInstance().dial(mPhone, 288 PhoneNumberUtils.stripSeparators("+17005554141"), 0); 289 ArgumentCaptor<String> mCaptorString = ArgumentCaptor.forClass(String.class); 290 ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsCaptor = 291 ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class); 292 293 verify(mPhone, times(1)).dial(mCaptorString.capture(), dialArgsCaptor.capture()); 294 assertEquals(PhoneNumberUtils.stripSeparators("+17005554141"), 295 mCaptorString.getValue()); 296 assertEquals(0, dialArgsCaptor.getValue().videoState); 297 } 298 299 @Test @SmallTest 300 public void testRegisterEvent() throws Exception { 301 Field field = CallManager.class.getDeclaredField("EVENT_CALL_WAITING"); 302 field.setAccessible(true); 303 int mEvent = (Integer) field.get(CallManager.getInstance()); 304 verify(mPhone, times(1)).registerForCallWaiting(isA(Handler.class), 305 eq(mEvent), isNull()); 306 307 field = CallManager.class.getDeclaredField("EVENT_PRECISE_CALL_STATE_CHANGED"); 308 field.setAccessible(true); 309 mEvent = (Integer) field.get(CallManager.getInstance()); 310 verify(mPhone, times(1)).registerForPreciseCallStateChanged(isA(Handler.class), 311 eq(mEvent), isA(Object.class)); 312 313 field = CallManager.class.getDeclaredField("EVENT_RINGBACK_TONE"); 314 field.setAccessible(true); 315 mEvent = (Integer) field.get(CallManager.getInstance()); 316 verify(mPhone, times(1)).registerForRingbackTone(isA(Handler.class), 317 eq(mEvent), isA(Object.class)); 318 } 319 320 @Test @SmallTest 321 public void testGetServiceState() throws Exception { 322 // register for another phone 323 ServiceState mSecondServiceState = mock(ServiceState.class); 324 doReturn(mSecondServiceState).when(mSecondPhone).getServiceState(); 325 326 Message mRegisterPhone = mHandler.obtainMessage(PHONE_REGISTER_EVENT, 327 mSecondPhone); 328 setReady(false); 329 mRegisterPhone.sendToTarget(); 330 331 waitUntilReady(); 332 333 // mPhone: STATE_IN_SERVICE > mPhoneSecond: state STATE_OUT_OF_SERVICE 334 doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSecondServiceState).getState(); 335 assertEquals(ServiceState.STATE_IN_SERVICE, CallManager.getInstance().getServiceState()); 336 337 // mPhone: STATE_IN_SERVICE > mPhoneSecond: state STATE_EMERGENCY_ONLY 338 doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mSecondServiceState).getState(); 339 assertEquals(ServiceState.STATE_IN_SERVICE, CallManager.getInstance().getServiceState()); 340 341 // mPhone: STATE_IN_SERVICE > mPhoneSecond: state STATE_POWER_OFF 342 doReturn(ServiceState.STATE_POWER_OFF).when(mSecondServiceState).getState(); 343 assertEquals(ServiceState.STATE_IN_SERVICE, CallManager.getInstance().getServiceState()); 344 345 // mPhone: STATE_EMERGENCY_ONLY < mPhoneSecond: state STATE_OUT_OF_SERVICE 346 doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSecondServiceState).getState(); 347 doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getState(); 348 assertEquals(ServiceState.STATE_OUT_OF_SERVICE, 349 CallManager.getInstance().getServiceState()); 350 351 // mPhone: STATE_POWER_OFF < mPhoneSecond: state STATE_OUT_OF_SERVICE 352 doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getState(); 353 assertEquals(ServiceState.STATE_OUT_OF_SERVICE, 354 CallManager.getInstance().getServiceState()); 355 356 /* mPhone: STATE_POWER_OFF < mPhoneSecond: state STATE_EMERGENCY_ONLY 357 but OUT_OF_SERVICE will replaces EMERGENCY_ONLY and POWER_OFF */ 358 doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mSecondServiceState).getState(); 359 assertEquals(ServiceState.STATE_OUT_OF_SERVICE, 360 CallManager.getInstance().getServiceState()); 361 CallManager.getInstance().unregisterPhone(mSecondPhone); 362 } 363 } 364