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