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