1 /* 2 * Copyright (C) 2022 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 android.telephony.mockmodem; 18 19 import android.hardware.radio.voice.CdmaSignalInfoRecord; 20 import android.hardware.radio.voice.LastCallFailCause; 21 import android.hardware.radio.voice.LastCallFailCauseInfo; 22 import android.hardware.radio.voice.UusInfo; 23 import android.os.Handler; 24 import android.os.HandlerThread; 25 import android.os.Looper; 26 import android.os.Message; 27 import android.support.annotation.GuardedBy; 28 import android.telephony.Annotation; 29 import android.telephony.DisconnectCause; 30 import android.util.Log; 31 32 import java.util.ArrayList; 33 import java.util.Timer; 34 import java.util.TimerTask; 35 36 public class MockVoiceService { 37 private static final int INVALID_CALL_ID = -1; 38 private static final int MIN_CALL_ID = 1; 39 private static final int MAX_CALL_ID = 9; 40 private static final int MSG_REQUEST_DIALING_CALL = 1; 41 private static final int MSG_REQUEST_RINGBACK_TONE = 2; 42 private static final int MSG_REQUEST_ALERTING_CALL = 3; 43 private static final int MSG_REQUEST_ACTIVATING_CALL = 4; 44 private static final int MSG_REQUEST_DISCONNECTING_CALL = 5; 45 private static final int MSG_REQUEST_INCOMING_CALL = 6; 46 private static final int MSG_REQUEST_CALL_END = 7; 47 48 private static final int EMERGENCY_TEMP_FAILURE = 325; 49 private static final int EMERGENCY_PERM_FAILURE = 326; 50 51 private String mTag = "MockVoiceService"; 52 private Handler mConfigHandler; 53 private HandlerThread mCallStateHandlerThread; 54 private MockCallStateHandler mCallStateHandler; 55 56 @GuardedBy("mCallList") 57 private final ArrayList<MockCallInfo> mCallList = new ArrayList<MockCallInfo>(); 58 59 private LastCallFailCauseInfo mLastCallEndInfo; 60 private boolean mMuteMode; 61 62 public class MockCallInfo { 63 // Call state definition 64 public static final int CALL_STATE_INIT = 0; 65 public static final int CALL_STATE_ACTIVE = 1; 66 public static final int CALL_STATE_HOLDING = 2; 67 public static final int CALL_STATE_DIALING = 3; 68 public static final int CALL_STATE_ALERTING = 4; 69 public static final int CALL_STATE_INCOMING = 5; 70 public static final int CALL_STATE_WAITING = 6; 71 public static final int CALL_STATE_DISCONNECTING = 7; 72 public static final int CALL_STATE_END = 8; 73 74 // Call presentation definition 75 public static final int CALL_PRESENTATION_ALLOWED = 0; 76 public static final int CALL_PRESENTATION_RESTRICTED = 1; 77 public static final int CALL_PRESENTATION_UNKNOWN = 2; 78 public static final int CALL_PRESENTATION_PAYPHONE = 3; 79 80 // Audio quality definition 81 public static final int AUDIO_QUALITY_UNSPECIFIED = 0; 82 public static final int AUDIO_QUALITY_AMR = 1; 83 public static final int AUDIO_QUALITY_AMR_WB = 2; 84 public static final int AUDIO_QUALITY_GSM_EFR = 3; 85 public static final int AUDIO_QUALITY_GSM_FR = 4; 86 public static final int AUDIO_QUALITY_GSM_HR = 5; 87 public static final int AUDIO_QUALITY_EVRC = 6; 88 public static final int AUDIO_QUALITY_EVRC_B = 7; 89 public static final int AUDIO_QUALITY_EVRC_WB = 8; 90 public static final int AUDIO_QUALITY_EVRC_NW = 9; 91 92 // Call type definition 93 public static final int CALL_TYPE_VOICE = 0; 94 public static final int CALL_TYPE_VIDEO = 1; 95 public static final int CALL_TYPE_EMERGENCY = 2; 96 public static final int CALL_TYPE_CDMA_VOICE = 3; 97 public static final int CALL_TYPE_CDMA_EMERGENCY = 4; 98 99 // CLIR type definition 100 public static final int CLIR_TYPE_DEFAULT = 0; 101 public static final int CLIR_TYPE_INVOCATION = 1; 102 public static final int CLIR_TYPE_SUPPRESSION = 2; 103 104 // Default type of address 105 private static final int DEFAULT_TOA = 145; 106 107 private int mState; 108 private int mIndex; 109 private int mToa; 110 private byte mAls; 111 private boolean mIsMpty; 112 private boolean mIsMT; 113 private boolean mIsVoice; 114 private boolean mIsVoicePrivacy; 115 private String mNumber; 116 private int mNumberPresentation; 117 private String mName; 118 private int mNamePresentation; 119 private UusInfo[] mUusInfo; 120 private int mAudioQuality; 121 private String mForwardedNumber; 122 private int mCallType; 123 private int mClir; 124 private CdmaSignalInfoRecord mCdmaSignalInfoRecord; 125 private MockCallControlInfo mCallControlInfo; 126 private int mCategories; 127 private String[] mUrns; 128 private int mRouting; 129 130 @GuardedBy("mTimerList") 131 private final ArrayList<Timer> mTimerList = new ArrayList<Timer>(); 132 133 private final class MockCallStateTimerTask extends TimerTask { 134 private Timer mTimer; 135 private int mCallId; 136 private int mEvent; 137 MockCallStateTimerTask(Timer timer, int callId, int event)138 MockCallStateTimerTask(Timer timer, int callId, int event) { 139 mTimer = timer; 140 mCallId = callId; 141 mEvent = event; 142 } 143 144 @Override run()145 public void run() { 146 Log.d( 147 mTag, 148 "Timer task - triggering call state event = " 149 + getCallStateRequestEventStr(mEvent) 150 + " for call id = " 151 + mCallId); 152 mCallStateHandler.obtainMessage(mEvent, mCallId).sendToTarget(); 153 synchronized (mTimerList) { 154 mTimerList.remove(mTimer); 155 } 156 } 157 } 158 MockCallInfo( boolean isMT, String address, int clir, UusInfo[] uusInfo, int callType, MockCallControlInfo callControlInfo)159 public MockCallInfo( 160 boolean isMT, 161 String address, 162 int clir, 163 UusInfo[] uusInfo, 164 int callType, 165 MockCallControlInfo callControlInfo) { 166 mState = CALL_STATE_INIT; 167 mIndex = generateCallId(); 168 mToa = DEFAULT_TOA; 169 mNumber = address; 170 mIsMT = isMT; 171 mClir = clir; 172 mUusInfo = uusInfo; 173 mCallType = callType; 174 mCdmaSignalInfoRecord = null; 175 if (callControlInfo == null) { 176 mCallControlInfo = new MockCallControlInfo(); 177 Log.w(mTag, "No call control info. Using default instead."); 178 } else { 179 mCallControlInfo = callControlInfo; 180 } 181 } 182 MockCallInfo( boolean isMT, String address, int categories, String[] urns, int routing, int callType, MockCallControlInfo callControlInfo)183 public MockCallInfo( 184 boolean isMT, 185 String address, 186 int categories, 187 String[] urns, 188 int routing, 189 int callType, 190 MockCallControlInfo callControlInfo) { 191 mState = CALL_STATE_INIT; 192 mIndex = generateCallId(); 193 mToa = DEFAULT_TOA; 194 mNumber = address; 195 mIsMT = isMT; 196 mUusInfo = new UusInfo[0]; 197 mCallType = callType; 198 mCategories = categories; 199 mUrns = urns; 200 mRouting = routing; 201 if (callControlInfo == null) { 202 mCallControlInfo = new MockCallControlInfo(); 203 Log.w(mTag, "No call control info. Using default instead."); 204 } else { 205 mCallControlInfo = callControlInfo; 206 } 207 } 208 getCallState()209 public int getCallState() { 210 return mState; 211 } 212 setCallState(int state)213 public void setCallState(int state) { 214 mState = state; 215 } 216 getCallId()217 public int getCallId() { 218 return mIndex; 219 } 220 setCallId(int callId)221 public void setCallId(int callId) { 222 mIndex = callId; 223 } 224 getCallToa()225 public int getCallToa() { 226 return mToa; 227 } 228 setCallToa(int toa)229 public void setCallToa(int toa) { 230 mToa = toa; 231 } 232 getCallAls()233 public byte getCallAls() { 234 return mAls; 235 } 236 setCallAls(byte als)237 public void setCallAls(byte als) { 238 mAls = als; 239 } 240 isMpty()241 public boolean isMpty() { 242 return mIsMpty; 243 } 244 setMpty(boolean isMpty)245 public void setMpty(boolean isMpty) { 246 mIsMpty = isMpty; 247 } 248 isMT()249 public boolean isMT() { 250 return mIsMT; 251 } 252 setMT(boolean isMT)253 public void setMT(boolean isMT) { 254 mIsMT = isMT; 255 } 256 isVoice()257 public boolean isVoice() { 258 return mIsVoice; 259 } 260 setVoice(boolean isVoice)261 public void setVoice(boolean isVoice) { 262 mIsVoice = isVoice; 263 } 264 isVoicePrivacy()265 public boolean isVoicePrivacy() { 266 return mIsVoicePrivacy; 267 } 268 setVoicePrivacy(boolean isVoicePrivacy)269 public void setVoicePrivacy(boolean isVoicePrivacy) { 270 mIsVoicePrivacy = isVoicePrivacy; 271 } 272 getNumber()273 public String getNumber() { 274 return mNumber; 275 } 276 setNumber(String number)277 public void setNumber(String number) { 278 mNumber = number; 279 } 280 getNumberPresentation()281 public int getNumberPresentation() { 282 return mNumberPresentation; 283 } 284 setNumberPresentation(int numberPresentation)285 public void setNumberPresentation(int numberPresentation) { 286 mNumberPresentation = numberPresentation; 287 } 288 getName()289 public String getName() { 290 return mName; 291 } 292 setName(String name)293 public void setName(String name) { 294 mName = name; 295 } 296 getNamePresentation()297 public int getNamePresentation() { 298 return mNamePresentation; 299 } 300 setNamePresentation(int namePresentation)301 public void setNamePresentation(int namePresentation) { 302 mNamePresentation = namePresentation; 303 } 304 getUusInfo()305 public UusInfo[] getUusInfo() { 306 return mUusInfo; 307 } 308 setUusInfo(UusInfo[] uusInfo)309 public void setUusInfo(UusInfo[] uusInfo) { 310 mUusInfo = uusInfo; 311 } 312 getAudioQuality()313 public int getAudioQuality() { 314 return mAudioQuality; 315 } 316 setAudioQuality(int audioQuality)317 public void setAudioQuality(int audioQuality) { 318 mAudioQuality = audioQuality; 319 } 320 getForwardedNumber()321 public String getForwardedNumber() { 322 return mForwardedNumber; 323 } 324 setForwardedNumber(String forwardedNumber)325 public void setForwardedNumber(String forwardedNumber) { 326 mForwardedNumber = forwardedNumber; 327 } 328 getClir()329 public int getClir() { 330 return mClir; 331 } 332 setClir(int clir)333 public void setClir(int clir) { 334 mClir = clir; 335 } 336 getCallType()337 public int getCallType() { 338 return mCallType; 339 } 340 setCallType(int callType)341 public void setCallType(int callType) { 342 mCallType = callType; 343 } 344 dump()345 public void dump() { 346 Log.d( 347 mTag, 348 "mState = " 349 + mState 350 + ", mIndex = " 351 + mIndex 352 + ", mToa = " 353 + mToa 354 + ", mAls = " 355 + mAls 356 + ", mIsMpty = " 357 + mIsMpty 358 + ", mIsVoice = " 359 + mIsVoice 360 + ", mIsvoicePrivacy = " 361 + mIsVoicePrivacy 362 + ", mNumber = " 363 + mNumber 364 + ", mNumberPresentation = " 365 + mNumberPresentation 366 + ", mName = " 367 + mName 368 + ", mNamePresentation = " 369 + mNamePresentation 370 + ", mAudioQuality = " 371 + mAudioQuality 372 + ", mForwardedNumber = " 373 + mForwardedNumber 374 + ", mCallType = " 375 + mCallType 376 + ", mClir = " 377 + mClir); 378 } 379 getCdmaSignalInfoRecord()380 public CdmaSignalInfoRecord getCdmaSignalInfoRecord() { 381 return mCdmaSignalInfoRecord; 382 } 383 setCdmaSignalInfoRecord(CdmaSignalInfoRecord cdmaSignalInfoRecord)384 public void setCdmaSignalInfoRecord(CdmaSignalInfoRecord cdmaSignalInfoRecord) { 385 mCdmaSignalInfoRecord = cdmaSignalInfoRecord; 386 } 387 getCallControlInfo()388 public MockCallControlInfo getCallControlInfo() { 389 return mCallControlInfo; 390 } 391 addCallStateTimerTask(int callId, int event, long duration)392 public void addCallStateTimerTask(int callId, int event, long duration) { 393 Timer timer = new Timer(false); 394 MockCallStateTimerTask timerTask = new MockCallStateTimerTask(timer, callId, event); 395 if (timer != null && timerTask != null) { 396 timer.schedule(timerTask, duration); 397 synchronized (mTimerList) { 398 if (mTimerList != null) { 399 mTimerList.add(timer); 400 } 401 } 402 } else { 403 Log.e( 404 mTag, 405 "Failed to start timer for event = " + getCallStateRequestEventStr(event)); 406 } 407 } 408 clearAllTimers()409 public void clearAllTimers() { 410 synchronized (mTimerList) { 411 if (mTimerList != null && mTimerList.size() > 0) { 412 for (int i = 0; i < mTimerList.size(); i++) { 413 mTimerList.get(i).cancel(); 414 } 415 mTimerList.clear(); 416 } 417 } 418 } 419 destroy()420 public void destroy() { 421 clearAllTimers(); 422 } 423 } 424 MockVoiceService(Handler handler)425 public MockVoiceService(Handler handler) { 426 mConfigHandler = handler; 427 mLastCallEndInfo = new LastCallFailCauseInfo(); 428 initMockVoiceService(); 429 430 // Start call state handler 431 mCallStateHandlerThread = new HandlerThread(mTag); 432 mCallStateHandlerThread.start(); 433 mCallStateHandler = new MockCallStateHandler(mCallStateHandlerThread.getLooper()); 434 } 435 destroy()436 public void destroy() { 437 Log.e(mTag, "destroy"); 438 clearAllCalls(); 439 if (mCallStateHandlerThread != null) { 440 mCallStateHandlerThread.quitSafely(); 441 mCallStateHandlerThread = null; 442 } 443 } 444 initMockVoiceService()445 private void initMockVoiceService() { 446 clearAllCalls(); 447 mMuteMode = false; 448 } 449 clearAllCalls()450 private void clearAllCalls() { 451 synchronized (mCallList) { 452 if (mCallList != null && mCallList.size() > 0) { 453 for (int i = 0; i < mCallList.size(); i++) { 454 mCallList.get(i).destroy(); 455 } 456 mCallList.clear(); 457 } 458 } 459 } 460 generateCallId()461 private int generateCallId() { 462 int callId = INVALID_CALL_ID; 463 int idx = 0; 464 465 synchronized (mCallList) { 466 for (callId = MIN_CALL_ID; callId <= MAX_CALL_ID; callId++) { 467 for (idx = 0; idx < mCallList.size(); idx++) { 468 if (mCallList.get(idx).getCallId() == callId) { 469 break; 470 } 471 } 472 if (idx == mCallList.size()) { 473 break; 474 } 475 } 476 } 477 478 if (callId > MAX_CALL_ID) { 479 callId = INVALID_CALL_ID; 480 Log.e(mTag, "Exceed maximum number of call (" + MAX_CALL_ID + ")."); 481 } 482 483 return callId; 484 } 485 getCallInfo(int callId)486 private MockCallInfo getCallInfo(int callId) { 487 MockCallInfo callInfo = null; 488 if (callId >= MIN_CALL_ID && callId <= MAX_CALL_ID) { 489 if (hasVoiceCalls()) { 490 synchronized (mCallList) { 491 for (int idx = 0; idx < mCallList.size(); idx++) { 492 callInfo = mCallList.get(idx); 493 if (callInfo.getCallId() == callId) { 494 break; 495 } else { 496 callInfo = null; 497 } 498 } 499 } 500 } else { 501 Log.w(mTag, "No any call in list."); 502 } 503 } else { 504 Log.e(mTag, "Invalid call id."); 505 } 506 507 if (callInfo == null) { 508 Log.e(mTag, "Not found any call info with call id " + callId + "."); 509 } 510 511 return callInfo; 512 } 513 removeCallInfo(int callId)514 private void removeCallInfo(int callId) { 515 MockCallInfo callInfo = null; 516 517 if (callId >= MIN_CALL_ID && callId <= MAX_CALL_ID) { 518 if (hasVoiceCalls()) { 519 synchronized (mCallList) { 520 for (int idx = 0; idx < mCallList.size(); idx++) { 521 callInfo = mCallList.get(idx); 522 if (callInfo.getCallId() == callId) { 523 mCallList.remove(idx); 524 break; 525 } else { 526 callInfo = null; 527 } 528 } 529 } 530 } else { 531 Log.w(mTag, "No any call in list."); 532 } 533 } else { 534 Log.e(mTag, "Invalid call id."); 535 } 536 537 if (callInfo == null) { 538 Log.e(mTag, "Not found any call info with call id " + callId + "."); 539 } 540 541 return; 542 } 543 getIncomingCallInfo()544 private MockCallInfo getIncomingCallInfo() { 545 MockCallInfo callInfo = null; 546 547 if (hasVoiceCalls()) { 548 synchronized (mCallList) { 549 for (int idx = 0; idx < mCallList.size(); idx++) { 550 callInfo = mCallList.get(idx); 551 if (callInfo.isMT() 552 && callInfo.getCallState() == MockCallInfo.CALL_STATE_INCOMING) { 553 break; 554 } else { 555 callInfo = null; 556 } 557 } 558 } 559 } else { 560 Log.w(mTag, "No any call in list."); 561 } 562 563 if (callInfo == null) { 564 Log.e(mTag, "Not found any incoming call info."); 565 } 566 567 return callInfo; 568 } 569 getCallStateRequestEventStr(int event)570 private String getCallStateRequestEventStr(int event) { 571 switch (event) { 572 case MSG_REQUEST_DIALING_CALL: 573 return "MSG_REQUEST_DIALING_CALL"; 574 case MSG_REQUEST_RINGBACK_TONE: 575 return "MSG_REQUEST_RINGBACK_TONE"; 576 case MSG_REQUEST_ALERTING_CALL: 577 return "MSG_REQUEST_ALERTING_CALL"; 578 case MSG_REQUEST_ACTIVATING_CALL: 579 return "MSG_REQUEST_ACTIVATING_CALL"; 580 case MSG_REQUEST_DISCONNECTING_CALL: 581 return "MSG_REQUEST_DISCONNECTING_CALL"; 582 case MSG_REQUEST_INCOMING_CALL: 583 return "MSG_REQUEST_INCOMING_CALL"; 584 case MSG_REQUEST_CALL_END: 585 return "MSG_REQUEST_CALL_END"; 586 } 587 return "Unknown"; 588 } 589 scheduleNextEventTimer(MockCallInfo callInfo, int nextEvent, long duration)590 private void scheduleNextEventTimer(MockCallInfo callInfo, int nextEvent, long duration) { 591 Log.d( 592 mTag, 593 "Schedule " 594 + getCallStateRequestEventStr(nextEvent) 595 + " for call id " 596 + callInfo.getCallId() 597 + " in " 598 + duration 599 + " ms."); 600 if (nextEvent >= 0) { 601 callInfo.addCallStateTimerTask(callInfo.getCallId(), nextEvent, duration); 602 } 603 } 604 handleDialingCall(int callId)605 private boolean handleDialingCall(int callId) { 606 Log.d(mTag, "handleDialingCall for call id: " + callId); 607 boolean isCallStateChanged = false; 608 609 synchronized (mCallList) { 610 MockCallInfo callInfo = getCallInfo(callId); 611 612 if (callInfo != null) { 613 long dialing_duration_in_ms = 614 callInfo.getCallControlInfo().getDialingDurationInMs(); 615 long alerting_duration_in_ms = 616 callInfo.getCallControlInfo().getAlertingDurationInMs(); 617 long ringback_tone_in_ms = callInfo.getCallControlInfo().getRingbackToneTimeInMs(); 618 int call_state_fail_bitmask = 619 callInfo.getCallControlInfo().getCallStateFailBitMask(); 620 int next_event = -1; 621 622 if (callInfo.getCallState() != MockCallInfo.CALL_STATE_DIALING) { 623 callInfo.setCallState(MockCallInfo.CALL_STATE_DIALING); 624 isCallStateChanged = true; 625 Log.d(mTag, "call id = " + callId + " call state = CALL_STATE_DIALING"); 626 } 627 628 if (isCallStateChanged) { 629 if ((call_state_fail_bitmask & MockCallControlInfo.CALL_DIALING_FAIL_BITMASK) 630 != 0) { 631 if (dialing_duration_in_ms < 0) { 632 Log.d(mTag, "Dialing duration < 0, using default duration!"); 633 dialing_duration_in_ms = 634 MockCallControlInfo.DEFAULT_DIALING_FAIL_DURATION_IN_MS; 635 } 636 next_event = MSG_REQUEST_DISCONNECTING_CALL; 637 Log.d( 638 mTag, 639 "Start call disconnecting task after " 640 + dialing_duration_in_ms 641 + " ms."); 642 } else { 643 // Ringback tone start event 644 callInfo.getCallControlInfo().setRingbackToneState(true); 645 next_event = MSG_REQUEST_RINGBACK_TONE; 646 if (ringback_tone_in_ms < 0 647 || ringback_tone_in_ms 648 > (dialing_duration_in_ms + alerting_duration_in_ms)) { 649 ringback_tone_in_ms = dialing_duration_in_ms; 650 Log.e( 651 mTag, 652 "ringback_tone_in_ms < 0 or > (dialing + alerting) duration (" 653 + (dialing_duration_in_ms + alerting_duration_in_ms) 654 + ") ms. Reset to dialing duration (" 655 + dialing_duration_in_ms 656 + ") ms"); 657 } 658 659 Log.d( 660 mTag, 661 "Start ringback tone task after " + ringback_tone_in_ms + " ms."); 662 663 scheduleNextEventTimer(callInfo, next_event, ringback_tone_in_ms); 664 665 // Next call state change event 666 if (dialing_duration_in_ms >= 0) { 667 next_event = MSG_REQUEST_ALERTING_CALL; 668 Log.d( 669 mTag, 670 "Start alerting task after " + dialing_duration_in_ms + " ms."); 671 } else { 672 next_event = -1; 673 Log.d(mTag, "Call dialing forever...."); 674 } 675 } 676 677 scheduleNextEventTimer(callInfo, next_event, dialing_duration_in_ms); 678 } 679 } else { 680 Log.e(mTag, "No found call id = " + callId); 681 } 682 } 683 684 return isCallStateChanged; 685 } 686 handleRingbackTone(int callId)687 private boolean handleRingbackTone(int callId) { 688 Log.d(mTag, "handleRingbackTone for call id: " + callId); 689 690 synchronized (mCallList) { 691 MockCallInfo callInfo = getCallInfo(callId); 692 693 if (callInfo != null) { 694 Message ringback_tone_msg = 695 mConfigHandler.obtainMessage( 696 MockModemConfigBase.EVENT_RINGBACK_TONE, 697 callInfo.getCallControlInfo().getRingbackToneState()); 698 mConfigHandler.sendMessage(ringback_tone_msg); 699 } else { 700 Log.e(mTag, "No found call id = " + callId); 701 } 702 } 703 704 return false; 705 } 706 handleAlertingCall(int callId)707 private boolean handleAlertingCall(int callId) { 708 Log.d(mTag, "handleAlertingCall for call id: " + callId); 709 710 boolean isCallStateChanged = false; 711 712 synchronized (mCallList) { 713 MockCallInfo callInfo = getCallInfo(callId); 714 715 if (callInfo != null) { 716 long alerting_duration_in_ms = 717 callInfo.getCallControlInfo().getAlertingDurationInMs(); 718 int call_state_fail_bitmask = 719 callInfo.getCallControlInfo().getCallStateFailBitMask(); 720 int next_event = -1; 721 722 if (callInfo.getCallState() != MockCallInfo.CALL_STATE_ALERTING) { 723 callInfo.setCallState(MockCallInfo.CALL_STATE_ALERTING); 724 isCallStateChanged = true; 725 Log.d(mTag, "call id = " + callId + " call state = CALL_STATE_ALERTING"); 726 } 727 728 if (isCallStateChanged) { 729 if ((call_state_fail_bitmask & MockCallControlInfo.CALL_ALERTING_FAIL_BITMASK) 730 != 0) { 731 if (alerting_duration_in_ms < 0) { 732 Log.d(mTag, "Alerting duration < 0, using default duration!"); 733 alerting_duration_in_ms = 734 MockCallControlInfo.DEFAULT_ALERTING_FAIL_DURATION_IN_MS; 735 } 736 next_event = MSG_REQUEST_DISCONNECTING_CALL; 737 Log.d( 738 mTag, 739 "Start call disconnecting task after " 740 + alerting_duration_in_ms 741 + " ms."); 742 } else { 743 if (alerting_duration_in_ms >= 0) { 744 next_event = MSG_REQUEST_ACTIVATING_CALL; 745 Log.d( 746 mTag, 747 "Start activating task after " 748 + alerting_duration_in_ms 749 + " ms."); 750 } else { 751 next_event = -1; 752 Log.d(mTag, "Call alerting forever...."); 753 } 754 } 755 756 scheduleNextEventTimer(callInfo, next_event, alerting_duration_in_ms); 757 } 758 } else { 759 Log.e(mTag, "No found call id = " + callId); 760 } 761 } 762 763 return isCallStateChanged; 764 } 765 handleActivatingCall(int callId)766 private boolean handleActivatingCall(int callId) { 767 Log.d(mTag, "handleActivatingCall for call id: " + callId); 768 769 boolean isCallStateChanged = false; 770 771 synchronized (mCallList) { 772 MockCallInfo callInfo = getCallInfo(callId); 773 774 if (callInfo != null) { 775 long active_duration_in_ms = callInfo.getCallControlInfo().getActiveDurationInMs(); 776 int next_event = -1; 777 778 if (callInfo.getCallState() != MockCallInfo.CALL_STATE_ACTIVE) { 779 // Ringback tone stop event 780 callInfo.getCallControlInfo().setRingbackToneState(false); 781 next_event = MSG_REQUEST_RINGBACK_TONE; 782 Log.d(mTag, "Start ringback tone task immediately."); 783 scheduleNextEventTimer(callInfo, next_event, 0); 784 callInfo.setCallState(MockCallInfo.CALL_STATE_ACTIVE); 785 isCallStateChanged = true; 786 Log.d(mTag, "call id = " + callId + " call state = CALL_STATE_ACTIVE"); 787 } 788 789 if (isCallStateChanged) { 790 // Next call state change event 791 if (active_duration_in_ms >= 0) { 792 next_event = MSG_REQUEST_DISCONNECTING_CALL; 793 Log.d( 794 mTag, 795 "Start call disconnecting task after " 796 + active_duration_in_ms 797 + " ms."); 798 scheduleNextEventTimer(callInfo, next_event, active_duration_in_ms); 799 } else { 800 Log.d(mTag, "Call active forever...."); 801 } 802 } 803 } else { 804 Log.e(mTag, "No found call id = " + callId); 805 } 806 } 807 808 return isCallStateChanged; 809 } 810 handleDisconnectingCall(int callId)811 private boolean handleDisconnectingCall(int callId) { 812 Log.d(mTag, "handleDisconnectingCall for call id: " + callId); 813 814 boolean isCallStateChanged = false; 815 816 synchronized (mCallList) { 817 MockCallInfo callInfo = getCallInfo(callId); 818 819 if (callInfo != null) { 820 long disconnecting_duration_in_ms = 821 callInfo.getCallControlInfo().getDisconnectingDurationInMs(); 822 int next_event = -1; 823 824 if (callInfo.getCallState() != MockCallInfo.CALL_STATE_DISCONNECTING) { 825 callInfo.setCallState(MockCallInfo.CALL_STATE_DISCONNECTING); 826 callInfo.clearAllTimers(); 827 isCallStateChanged = true; 828 Log.d(mTag, "call id = " + callId + " call state = CALL_STATE_DISCONNECTING"); 829 } 830 831 if (isCallStateChanged) { 832 if (disconnecting_duration_in_ms >= 0) { 833 next_event = MSG_REQUEST_CALL_END; 834 Log.d( 835 mTag, 836 "Start call end task after " 837 + disconnecting_duration_in_ms 838 + " ms."); 839 scheduleNextEventTimer(callInfo, next_event, disconnecting_duration_in_ms); 840 } else { 841 Log.d(mTag, "Call disconnecting forever...."); 842 } 843 // No need updating call disconnecting to upper layer 844 isCallStateChanged = false; 845 } 846 } else { 847 Log.e(mTag, "No found call id = " + callId); 848 } 849 } 850 851 return isCallStateChanged; 852 } 853 handleIncomingCall(int callId)854 private boolean handleIncomingCall(int callId) { 855 Log.d(mTag, "handleIncomingCall for call id: " + callId); 856 857 boolean isCallStateChanged = false; 858 859 synchronized (mCallList) { 860 MockCallInfo callInfo = getCallInfo(callId); 861 862 if (callInfo != null) { 863 long incoming_duration_in_ms = 864 callInfo.getCallControlInfo().getIncomingDurationInMs(); 865 int call_state_fail_bitmask = 866 callInfo.getCallControlInfo().getCallStateFailBitMask(); 867 int next_event = -1; 868 869 if (callInfo.getCallState() != MockCallInfo.CALL_STATE_INCOMING) { 870 callInfo.setCallState(MockCallInfo.CALL_STATE_INCOMING); 871 isCallStateChanged = true; 872 Log.d(mTag, "call id = " + callId + " call state = CALL_STATE_INCOMING"); 873 } 874 875 if (isCallStateChanged) { 876 if ((call_state_fail_bitmask & MockCallControlInfo.CALL_INCOMING_FAIL_BITMASK) 877 != 0) { 878 if (incoming_duration_in_ms < 0) { 879 Log.d(mTag, "Incoming duration < 0, using default duration!"); 880 incoming_duration_in_ms = 881 MockCallControlInfo.DEFAULT_INCOMING_FAIL_DURATION_IN_MS; 882 } 883 next_event = MSG_REQUEST_DISCONNECTING_CALL; 884 Log.d( 885 mTag, 886 "Start call disconnecting task after " 887 + incoming_duration_in_ms 888 + " ms."); 889 } else { 890 if (incoming_duration_in_ms >= 0) { 891 next_event = MSG_REQUEST_ACTIVATING_CALL; 892 Log.d( 893 mTag, 894 "Start activating task after " 895 + incoming_duration_in_ms 896 + " ms."); 897 } else { 898 next_event = -1; 899 Log.d(mTag, "Call incoming forever...."); 900 } 901 } 902 903 scheduleNextEventTimer(callInfo, next_event, incoming_duration_in_ms); 904 } 905 } else { 906 Log.e(mTag, "No found call id = " + callId); 907 } 908 909 if (isCallStateChanged) { 910 Message call_incoming_msg = 911 mConfigHandler.obtainMessage( 912 MockModemConfigBase.EVENT_CALL_INCOMING, callInfo); 913 mConfigHandler.sendMessage(call_incoming_msg); 914 } 915 } 916 917 return isCallStateChanged; 918 } 919 handleCallEnd(int callId)920 private boolean handleCallEnd(int callId) { 921 Log.d(mTag, "handleCallEnd for call id: " + callId); 922 923 boolean isCallStateChanged = false; 924 925 synchronized (mCallList) { 926 MockCallInfo callInfo = getCallInfo(callId); 927 928 if (callInfo != null) { 929 if (callInfo.getCallState() != MockCallInfo.CALL_STATE_END) { 930 callInfo.setCallState(MockCallInfo.CALL_STATE_END); 931 mLastCallEndInfo.causeCode = 932 callInfo.getCallControlInfo().getCallEndInfo().causeCode; 933 mLastCallEndInfo.vendorCause = 934 callInfo.getCallControlInfo().getCallEndInfo().vendorCause; 935 isCallStateChanged = true; 936 removeCallInfo(callId); 937 Log.d( 938 mTag, 939 "call id = " 940 + callId 941 + " call state = CALL_STATE_END with causeCode = " 942 + mLastCallEndInfo.causeCode 943 + ", vendorCause = " 944 + mLastCallEndInfo.vendorCause); 945 } 946 } else { 947 Log.e(mTag, "No found call id = " + callId); 948 } 949 } 950 951 return isCallStateChanged; 952 } 953 954 private final class MockCallStateHandler extends Handler { MockCallStateHandler(Looper looper)955 MockCallStateHandler(Looper looper) { 956 super(looper); 957 } 958 959 @Override handleMessage(Message msg)960 public void handleMessage(Message msg) { 961 Log.d( 962 mTag, 963 "Call state change handling begin for " 964 + getCallStateRequestEventStr(msg.what)); 965 boolean isCallStateChanged = false; 966 967 try { 968 switch (msg.what) { 969 case MSG_REQUEST_DIALING_CALL: 970 isCallStateChanged = handleDialingCall((int) msg.obj); 971 break; 972 case MSG_REQUEST_RINGBACK_TONE: 973 isCallStateChanged = handleRingbackTone((int) msg.obj); 974 break; 975 case MSG_REQUEST_ALERTING_CALL: 976 isCallStateChanged = handleAlertingCall((int) msg.obj); 977 break; 978 case MSG_REQUEST_ACTIVATING_CALL: 979 isCallStateChanged = handleActivatingCall((int) msg.obj); 980 break; 981 case MSG_REQUEST_DISCONNECTING_CALL: 982 isCallStateChanged = handleDisconnectingCall((int) msg.obj); 983 break; 984 case MSG_REQUEST_INCOMING_CALL: 985 isCallStateChanged = handleIncomingCall((int) msg.obj); 986 break; 987 case MSG_REQUEST_CALL_END: 988 isCallStateChanged = handleCallEnd((int) msg.obj); 989 break; 990 default: 991 Log.e(mTag, "Unknown message id."); 992 break; 993 } 994 } finally { 995 Log.d(mTag, "Call state change handling complete"); 996 if (isCallStateChanged) { 997 synchronized (mCallList) { 998 Message call_state_changed_msg = 999 mConfigHandler.obtainMessage( 1000 MockModemConfigBase.EVENT_CALL_STATE_CHANGE, mCallList); 1001 mConfigHandler.sendMessage(call_state_changed_msg); 1002 } 1003 } 1004 } 1005 } 1006 } 1007 getNumberOfCalls()1008 public int getNumberOfCalls() { 1009 int numOfCalls = 0; 1010 synchronized (mCallList) { 1011 numOfCalls = mCallList.size(); 1012 } 1013 return numOfCalls; 1014 } 1015 hasVoiceCalls()1016 public boolean hasVoiceCalls() { 1017 return (getNumberOfCalls() > 0 ? true : false); 1018 } 1019 hasDisconnectingCall()1020 public boolean hasDisconnectingCall() { 1021 boolean result = false; 1022 synchronized (mCallList) { 1023 for (int i = 0; i < mCallList.size(); i++) { 1024 if (mCallList.get(i).getCallState() == MockCallInfo.CALL_STATE_DISCONNECTING) { 1025 result = true; 1026 break; 1027 } 1028 } 1029 } 1030 return result; 1031 } 1032 getCurrentCalls()1033 public boolean getCurrentCalls() { 1034 if (!hasDisconnectingCall()) { 1035 synchronized (mCallList) { 1036 Message current_calls_response_msg = 1037 mConfigHandler.obtainMessage( 1038 MockModemConfigBase.EVENT_CURRENT_CALLS_RESPONSE, mCallList); 1039 mConfigHandler.sendMessage(current_calls_response_msg); 1040 } 1041 } else { 1042 Log.d(mTag, "Has disconnecting calls, skip to trigger EVENT_CURRENT_CALLS_RESPONSE."); 1043 } 1044 return true; 1045 } 1046 dialVoiceCall( String address, int clir, UusInfo[] uusInfo, int callType, MockCallControlInfo callControlInfo)1047 public boolean dialVoiceCall( 1048 String address, 1049 int clir, 1050 UusInfo[] uusInfo, 1051 int callType, 1052 MockCallControlInfo callControlInfo) { 1053 boolean result = false; 1054 MockCallInfo newCall = 1055 new MockCallInfo(false, address, clir, uusInfo, callType, callControlInfo); 1056 1057 if (newCall != null) { 1058 synchronized (mCallList) { 1059 mCallList.add(newCall); 1060 newCall.dump(); 1061 newCall.getCallControlInfo().dump(); 1062 } 1063 mCallStateHandler 1064 .obtainMessage(MSG_REQUEST_DIALING_CALL, newCall.getCallId()) 1065 .sendToTarget(); 1066 result = true; 1067 } else { 1068 Log.e(mTag, "Call info creation failed!"); 1069 } 1070 return result; 1071 } 1072 dialEccVoiceCall( String address, int categories, String[] urns, int routing, int callType, MockCallControlInfo callControlInfo)1073 public boolean dialEccVoiceCall( 1074 String address, 1075 int categories, 1076 String[] urns, 1077 int routing, 1078 int callType, 1079 MockCallControlInfo callControlInfo) { 1080 boolean result = false; 1081 MockCallInfo newCall = 1082 new MockCallInfo(false, address, 1083 categories, urns, routing, callType, callControlInfo); 1084 1085 if (newCall != null) { 1086 synchronized (mCallList) { 1087 mCallList.add(newCall); 1088 newCall.dump(); 1089 newCall.getCallControlInfo().dump(); 1090 } 1091 mCallStateHandler 1092 .obtainMessage(MSG_REQUEST_DIALING_CALL, newCall.getCallId()) 1093 .sendToTarget(); 1094 result = true; 1095 } else { 1096 Log.e(mTag, "Call info creation failed!"); 1097 } 1098 return result; 1099 } 1100 hangupVoiceCall(int index)1101 public boolean hangupVoiceCall(int index) { 1102 boolean result = false; 1103 MockCallInfo callInfo = null; 1104 1105 synchronized (mCallList) { 1106 callInfo = getCallInfo(index); 1107 1108 if (callInfo != null) { 1109 mCallStateHandler 1110 .obtainMessage(MSG_REQUEST_DISCONNECTING_CALL, index) 1111 .sendToTarget(); 1112 result = true; 1113 } else { 1114 Log.e(mTag, "Cannot find any call with id = " + index); 1115 } 1116 } 1117 1118 return result; 1119 } 1120 rejectVoiceCall()1121 public boolean rejectVoiceCall() { 1122 boolean result = false; 1123 MockCallInfo callInfo = null; 1124 1125 synchronized (mCallList) { 1126 callInfo = getIncomingCallInfo(); 1127 1128 if (callInfo != null) { 1129 mCallStateHandler 1130 .obtainMessage(MSG_REQUEST_DISCONNECTING_CALL, callInfo.getCallId()) 1131 .sendToTarget(); 1132 result = true; 1133 } else { 1134 Log.e(mTag, "Cannot find any incoming call."); 1135 } 1136 } 1137 1138 return result; 1139 } 1140 acceptVoiceCall()1141 public boolean acceptVoiceCall() { 1142 boolean result = false; 1143 MockCallInfo callInfo = null; 1144 1145 synchronized (mCallList) { 1146 callInfo = getIncomingCallInfo(); 1147 1148 if (callInfo != null) { 1149 mCallStateHandler 1150 .obtainMessage(MSG_REQUEST_ACTIVATING_CALL, callInfo.getCallId()) 1151 .sendToTarget(); 1152 result = true; 1153 } else { 1154 Log.e(mTag, "Cannot find any incoming call."); 1155 } 1156 } 1157 1158 return result; 1159 } 1160 triggerIncomingVoiceCall( String address, UusInfo[] uusInfo, int callType, CdmaSignalInfoRecord cdmaSignalInfoRecord, MockCallControlInfo callControlInfo)1161 public boolean triggerIncomingVoiceCall( 1162 String address, 1163 UusInfo[] uusInfo, 1164 int callType, 1165 CdmaSignalInfoRecord cdmaSignalInfoRecord, 1166 MockCallControlInfo callControlInfo) { 1167 boolean result = false; 1168 MockCallInfo newCall = 1169 new MockCallInfo( 1170 true, 1171 address, 1172 MockCallInfo.CLIR_TYPE_DEFAULT, 1173 uusInfo, 1174 callType, 1175 callControlInfo); 1176 1177 if (newCall != null) { 1178 if (cdmaSignalInfoRecord != null) { 1179 newCall.setCdmaSignalInfoRecord(cdmaSignalInfoRecord); 1180 } 1181 1182 synchronized (mCallList) { 1183 mCallList.add(newCall); 1184 newCall.dump(); 1185 } 1186 mCallStateHandler 1187 .obtainMessage(MSG_REQUEST_INCOMING_CALL, newCall.getCallId()) 1188 .sendToTarget(); 1189 result = true; 1190 } else { 1191 Log.e(mTag, "Call info creation failed!"); 1192 } 1193 1194 return result; 1195 } 1196 getMuteMode()1197 public boolean getMuteMode() { 1198 return mMuteMode; 1199 } 1200 setMuteMode(boolean isMute)1201 public void setMuteMode(boolean isMute) { 1202 mMuteMode = isMute; 1203 } 1204 getLastCallEndInfo()1205 public LastCallFailCauseInfo getLastCallEndInfo() { 1206 return mLastCallEndInfo; 1207 } 1208 setLastCallFailCause(@nnotation.DisconnectCauses int cause)1209 public void setLastCallFailCause(@Annotation.DisconnectCauses int cause) { 1210 mLastCallEndInfo.causeCode = convertToLastCallFailCause(cause); 1211 } 1212 clearAllCalls(@nnotation.DisconnectCauses int cause)1213 public void clearAllCalls(@Annotation.DisconnectCauses int cause) { 1214 setLastCallFailCause(cause); 1215 synchronized (mCallList) { 1216 if (mCallList != null && mCallList.size() > 0) { 1217 clearAllCalls(); 1218 Message call_state_changed_msg = 1219 mConfigHandler.obtainMessage( 1220 MockModemConfigBase.EVENT_CALL_STATE_CHANGE, mCallList); 1221 mConfigHandler.sendMessage(call_state_changed_msg); 1222 } 1223 } 1224 } 1225 1226 /** 1227 * Converts {@link DisconnectCause} to {@link LastCallFailCause}. 1228 * 1229 * @param cause The disconnect cause code. 1230 * @return The converted call fail cause. 1231 */ convertToLastCallFailCause( @nnotation.DisconnectCauses int cause)1232 private @LastCallFailCause int convertToLastCallFailCause( 1233 @Annotation.DisconnectCauses int cause) { 1234 switch (cause) { 1235 case DisconnectCause.BUSY: 1236 return LastCallFailCause.BUSY; 1237 case DisconnectCause.CONGESTION: 1238 return LastCallFailCause.TEMPORARY_FAILURE; 1239 case DisconnectCause.NORMAL: 1240 return LastCallFailCause.NORMAL; 1241 case DisconnectCause.POWER_OFF: 1242 return LastCallFailCause.RADIO_OFF; 1243 case DisconnectCause.EMERGENCY_TEMP_FAILURE: 1244 return EMERGENCY_TEMP_FAILURE; 1245 case DisconnectCause.EMERGENCY_PERM_FAILURE: 1246 return EMERGENCY_PERM_FAILURE; 1247 default: 1248 return LastCallFailCause.ERROR_UNSPECIFIED; 1249 } 1250 } 1251 } 1252