• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 com.googlecode.android_scripting.facade.telephony;
18 
19 import static com.googlecode.android_scripting.facade.telephony.InCallServiceImpl.HandleVoiceThreadState.TERMINATE;
20 import static com.googlecode.android_scripting.facade.telephony.InCallServiceImpl.HandleVoiceThreadState.RUN;
21 import static com.googlecode.android_scripting.facade.telephony.RecordVoiceInCall.MONO_CHANNEL;
22 import static com.googlecode.android_scripting.facade.telephony.RecordVoiceInCall.SAMPLE_RATE_16K;
23 import static com.googlecode.android_scripting.facade.telephony.RecordVoiceInCall.SAMPLE_RATE_48K;
24 import static com.googlecode.android_scripting.facade.telephony.RecordVoiceInCall.STEREO_CHANNEL;
25 
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Set;
31 import java.io.File;
32 
33 import android.content.Context;
34 import android.telecom.Call;
35 import android.telecom.Call.Details;
36 import android.telecom.CallAudioState;
37 import android.telecom.Connection;
38 import android.telecom.InCallService;
39 import android.telecom.Phone;
40 import android.telecom.TelecomManager;
41 import android.telecom.VideoProfile;
42 import android.telecom.VideoProfile.CameraCapabilities;
43 import android.media.AudioDeviceInfo;
44 import android.media.AudioManager;
45 
46 import com.googlecode.android_scripting.Log;
47 
48 import com.googlecode.android_scripting.facade.EventFacade;
49 
50 public class InCallServiceImpl extends InCallService {
51 
52     private static InCallServiceImpl sService = null;
53 
54     private static PlayAudioInCall playAudioInCall;
55     private static RecordVoiceInCall recordVoiceInCall;
56 
57     // The call is to play an audio file or record voice.
58     private static Call playRecordCall = null;
59 
60     // A telephony device to route voice through mobile telphony network
61     private static AudioDeviceInfo audioTelephonyInfo = null;
62 
63     // Indicates if the call is playing audio or not
64     private static HandleVoiceThreadState playAudioInCallState = TERMINATE;
65     // Indicates if the call is recording voice or not
66     private static HandleVoiceThreadState recordVoiceInCallState = TERMINATE;
67 
68     private static AudioManager mAudioManager = null;
69 
70     // The audio file is to play audio on the route of telephony network
71     private static File playAudioFile;
72     // The audio file is to store voice wav data on the route of telephony network
73     private static File recordVoiceFile;
74 
getService()75     public static InCallServiceImpl getService() {
76         return sService;
77     }
78 
79     public static class CallListener {
80 
81         public static final int LISTEN_CALL_ADDED   = 1 << 0;
82         public static final int LISTEN_CALL_REMOVED = 1 << 1;
83         public static final int LISTEN_ALL = LISTEN_CALL_ADDED | LISTEN_CALL_REMOVED;
84 
85         private static int sListenedEvents = 0;
86 
startListeningForEvent( int event )87         public static void startListeningForEvent( int event ) {
88             sListenedEvents |= event & LISTEN_ALL;
89         }
90 
stopListeningForEvent( int event )91         public static void stopListeningForEvent( int event ) {
92             sListenedEvents &= ~(event & LISTEN_ALL);
93         }
94 
onCallAdded(String callId, Call call)95         public static void onCallAdded(String callId, Call call) {
96             Log.d("CallListener:onCallAdded()");
97             if ((sListenedEvents & LISTEN_CALL_ADDED)
98                     == LISTEN_CALL_ADDED) {
99                 servicePostEvent(TelephonyConstants.EventTelecomCallAdded,
100                         new CallEvent<Call>(callId, call));
101             }
102         }
103 
onCallRemoved(String callId, Call call)104         public static void onCallRemoved(String callId, Call call) {
105             Log.d("CallListener:onCallRemoved()");
106             if ((sListenedEvents & LISTEN_CALL_REMOVED)
107                     == LISTEN_CALL_REMOVED) {
108                 servicePostEvent(TelephonyConstants.EventTelecomCallRemoved,
109                         new CallEvent<Call>(callId, call));
110             }
111         }
112     };
113 
114 
115     private static Object mLock = new Object();
116 
117     // Provides a return value for getCallState when no call is active
118     public static final int STATE_INVALID = -1;
119 
120     // Provides a return value for getCallQuality when input is invalid
121     public static final int QUALITY_INVALID = -1;
122 
123     // Provides a return value for getAudioRoute when input is invalid
124     public static final int INVALID_AUDIO_ROUTE = -1;
125 
126     public static final int VIDEO_STATE_AUDIO_ONLY = VideoProfile.STATE_AUDIO_ONLY;
127 
128     public static final int VIDEO_STATE_TX_ENABLED = VideoProfile.STATE_TX_ENABLED;
129 
130     public static final int VIDEO_STATE_RX_ENABLED = VideoProfile.STATE_RX_ENABLED;
131 
132     public static final int VIDEO_STATE_BIDIRECTIONAL = VideoProfile.STATE_BIDIRECTIONAL;
133 
134     public static final int VIDEO_STATE_TX_PAUSED =
135             VideoProfile.STATE_TX_ENABLED | VideoProfile.STATE_PAUSED;
136 
137     public static final int VIDEO_STATE_RX_PAUSED =
138             VideoProfile.STATE_RX_ENABLED | VideoProfile.STATE_PAUSED;
139 
140     public static final int VIDEO_STATE_BIDIRECTIONAL_PAUSED =
141             VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED;
142 
143     // Container class to return the call ID along with the event
144     public static class CallEvent<EventType> {
145 
146         private final String mCallId;
147         private final EventType mEvent;
148 
CallEvent(String callId, EventType event)149         CallEvent(String callId, EventType event) {
150             mCallId = callId;
151             mEvent = event;
152         }
153 
getCallId()154         public String getCallId() {
155             return mCallId;
156         }
157 
getEvent()158         public EventType getEvent() {
159             return mEvent;
160         }
161     }
162 
163     // Currently the same as a call event... here for future use
164     public static class VideoCallEvent<EventType> extends CallEvent<EventType> {
VideoCallEvent(String callId, EventType event)165         VideoCallEvent(String callId, EventType event) {
166             super(callId, event);
167         }
168     }
169 
170     private class CallCallback extends Call.Callback {
171 
172         // Invalid video state (valid >= 0)
173         public static final int STATE_INVALID = InCallServiceImpl.STATE_INVALID;
174 
175         public static final int EVENT_INVALID = -1;
176         public static final int EVENT_NONE = 0;
177         public static final int EVENT_STATE_CHANGED = 1 << 0;
178         public static final int EVENT_PARENT_CHANGED = 1 << 1;
179         public static final int EVENT_CHILDREN_CHANGED = 1 << 2;
180         public static final int EVENT_DETAILS_CHANGED = 1 << 3;
181         public static final int EVENT_CANNED_TEXT_RESPONSES_LOADED = 1 << 4;
182         public static final int EVENT_POST_DIAL_WAIT = 1 << 5;
183         public static final int EVENT_VIDEO_CALL_CHANGED = 1 << 6;
184         public static final int EVENT_CALL_DESTROYED = 1 << 7;
185         public static final int EVENT_CONFERENCABLE_CALLS_CHANGED = 1 << 8;
186 
187         public static final int EVENT_ALL = EVENT_STATE_CHANGED |
188                 EVENT_PARENT_CHANGED |
189                 EVENT_CHILDREN_CHANGED |
190                 EVENT_DETAILS_CHANGED |
191                 EVENT_CANNED_TEXT_RESPONSES_LOADED |
192                 EVENT_POST_DIAL_WAIT |
193                 EVENT_VIDEO_CALL_CHANGED |
194                 EVENT_DETAILS_CHANGED |
195                 EVENT_CALL_DESTROYED |
196                 EVENT_CONFERENCABLE_CALLS_CHANGED;
197 
198         private int mEvents;
199         private String mCallId;
200 
CallCallback(String callId, int events)201         public CallCallback(String callId, int events) {
202             super();
203             mEvents = events & EVENT_ALL;
204             mCallId = callId;
205         }
206 
startListeningForEvents(int events)207         public void startListeningForEvents(int events) {
208             mEvents |= events & EVENT_ALL;
209         }
210 
stopListeningForEvents(int events)211         public void stopListeningForEvents(int events) {
212             mEvents &= ~(events & EVENT_ALL);
213         }
214 
215         @Override
onStateChanged( Call call, int state)216         public void onStateChanged(
217                 Call call, int state) {
218             Log.d("CallCallback:onStateChanged()");
219             if ((mEvents & EVENT_STATE_CHANGED)
220                     == EVENT_STATE_CHANGED) {
221                 servicePostEvent(TelephonyConstants.EventTelecomCallStateChanged,
222                         new CallEvent<String>(mCallId, getCallStateString(state)));
223             }
224         }
225 
226         @Override
onParentChanged( Call call, Call parent)227         public void onParentChanged(
228                 Call call, Call parent) {
229             Log.d("CallCallback:onParentChanged()");
230             if ((mEvents & EVENT_PARENT_CHANGED)
231                     == EVENT_PARENT_CHANGED) {
232                 servicePostEvent(TelephonyConstants.EventTelecomCallParentChanged,
233                         new CallEvent<String>(mCallId, getCallId(parent)));
234             }
235         }
236 
237         @Override
onChildrenChanged( Call call, List<Call> children)238         public void onChildrenChanged(
239                 Call call, List<Call> children) {
240             Log.d("CallCallback:onChildrenChanged()");
241 
242             if ((mEvents & EVENT_CHILDREN_CHANGED)
243                     == EVENT_CHILDREN_CHANGED) {
244                 List<String> childList = new ArrayList<String>();
245 
246                 for (Call child : children) {
247                     childList.add(getCallId(child));
248                 }
249                 servicePostEvent(TelephonyConstants.EventTelecomCallChildrenChanged,
250                         new CallEvent<List<String>>(mCallId, childList));
251             }
252         }
253 
254         @Override
onDetailsChanged( Call call, Details details)255         public void onDetailsChanged(
256                 Call call, Details details) {
257             Log.d("CallCallback:onDetailsChanged()");
258 
259             if ((mEvents & EVENT_DETAILS_CHANGED)
260                     == EVENT_DETAILS_CHANGED) {
261                 servicePostEvent(TelephonyConstants.EventTelecomCallDetailsChanged,
262                         new CallEvent<Details>(mCallId, details));
263             }
264         }
265 
266         @Override
onCannedTextResponsesLoaded( Call call, List<String> cannedTextResponses)267         public void onCannedTextResponsesLoaded(
268                 Call call, List<String> cannedTextResponses) {
269             Log.d("CallCallback:onCannedTextResponsesLoaded()");
270             if ((mEvents & EVENT_CANNED_TEXT_RESPONSES_LOADED)
271                     == EVENT_CANNED_TEXT_RESPONSES_LOADED) {
272                 servicePostEvent(TelephonyConstants.EventTelecomCallCannedTextResponsesLoaded,
273                         new CallEvent<List<String>>(mCallId, cannedTextResponses));
274             }
275         }
276 
277         @Override
onPostDialWait( Call call, String remainingPostDialSequence)278         public void onPostDialWait(
279                 Call call, String remainingPostDialSequence) {
280             Log.d("CallCallback:onPostDialWait()");
281             if ((mEvents & EVENT_POST_DIAL_WAIT)
282                     == EVENT_POST_DIAL_WAIT) {
283                 servicePostEvent(TelephonyConstants.EventTelecomCallPostDialWait,
284                         new CallEvent<String>(mCallId, remainingPostDialSequence));
285             }
286         }
287 
288         @Override
onVideoCallChanged( Call call, InCallService.VideoCall videoCall)289         public void onVideoCallChanged(
290                 Call call, InCallService.VideoCall videoCall) {
291 
292             /*
293              * There is a race condition such that the lifetime of the VideoCall is not aligned with
294              * the lifetime of the underlying call object. We are using the onVideoCallChanged
295              * method as a way of determining the lifetime of the VideoCall object rather than
296              * onCallAdded/onCallRemoved.
297              */
298             Log.d("CallCallback:onVideoCallChanged()");
299 
300             if (call != null) {
301                 String callId = getCallId(call);
302                 CallContainer cc = mCallContainerMap.get(callId);
303                 if (cc == null) {
304                     Log.d(String.format("Call container returned null for callId %s", callId));
305                 }
306                 else {
307                     synchronized (mLock) {
308                         if (videoCall == null) {
309                             Log.d("Yo dawg, I heard you like null video calls.");
310                             // Try and see if the videoCall has been added/changed after firing the
311                             // callback
312                             // This probably won't work.
313                             videoCall = call.getVideoCall();
314                         }
315                         if (cc.getVideoCall() != videoCall) {
316                             if (videoCall == null) {
317                                 // VideoCall object deleted
318                                 cc.updateVideoCall(null, null);
319                                 Log.d("Removing video call from call.");
320                             }
321                             else if (cc.getVideoCall() != null) {
322                                 // Somehow we have a mismatched VideoCall ID!
323                                 Log.d("Mismatched video calls for same call ID.");
324                             }
325                             else {
326                                 Log.d("Huzzah, we have a video call!");
327 
328                                 VideoCallCallback videoCallCallback =
329                                         new VideoCallCallback(callId, VideoCallCallback.EVENT_NONE);
330 
331                                 videoCall.registerCallback(videoCallCallback);
332 
333                                 cc.updateVideoCall(
334                                         videoCall,
335                                         videoCallCallback);
336                             }
337                         }
338                         else {
339                             Log.d("Change to existing video call.");
340                         }
341 
342                     }
343                 }
344             }
345             else {
346                 Log.d("passed null call pointer to call callback");
347             }
348 
349             if ((mEvents & EVENT_VIDEO_CALL_CHANGED)
350                     == EVENT_VIDEO_CALL_CHANGED) {
351                 // TODO: b/26273778 Need to determine what to return;
352                 // probably not the whole video call
353                 servicePostEvent(TelephonyConstants.EventTelecomCallVideoCallChanged,
354                         new CallEvent<String>(mCallId, videoCall.toString()));
355             }
356         }
357 
358         @Override
onCallDestroyed(Call call)359         public void onCallDestroyed(Call call) {
360             Log.d("CallCallback:onCallDestroyed()");
361 
362             if ((mEvents & EVENT_CALL_DESTROYED)
363                     == EVENT_CALL_DESTROYED) {
364                 servicePostEvent(TelephonyConstants.EventTelecomCallDestroyed,
365                         new CallEvent<Call>(mCallId, call));
366             }
367         }
368 
369         @Override
onConferenceableCallsChanged( Call call, List<Call> conferenceableCalls)370         public void onConferenceableCallsChanged(
371                 Call call, List<Call> conferenceableCalls) {
372             Log.d("CallCallback:onConferenceableCallsChanged()");
373 
374             if ((mEvents & EVENT_CONFERENCABLE_CALLS_CHANGED)
375                     == EVENT_CONFERENCABLE_CALLS_CHANGED) {
376                 List<String> confCallList = new ArrayList<String>();
377                 for (Call cc : conferenceableCalls) {
378                     confCallList.add(getCallId(cc));
379                 }
380                 servicePostEvent(TelephonyConstants.EventTelecomCallConferenceableCallsChanged,
381                         new CallEvent<List<String>>(mCallId, confCallList));
382             }
383         }
384     }
385 
386     private class VideoCallCallback extends InCallService.VideoCall.Callback {
387 
388         public static final int EVENT_INVALID = -1;
389         public static final int EVENT_NONE = 0;
390         public static final int EVENT_SESSION_MODIFY_REQUEST_RECEIVED = 1 << 0;
391         public static final int EVENT_SESSION_MODIFY_RESPONSE_RECEIVED = 1 << 1;
392         public static final int EVENT_SESSION_EVENT = 1 << 2;
393         public static final int EVENT_PEER_DIMENSIONS_CHANGED = 1 << 3;
394         public static final int EVENT_VIDEO_QUALITY_CHANGED = 1 << 4;
395         public static final int EVENT_DATA_USAGE_CHANGED = 1 << 5;
396         public static final int EVENT_CAMERA_CAPABILITIES_CHANGED = 1 << 6;
397         public static final int EVENT_ALL =
398                 EVENT_SESSION_MODIFY_REQUEST_RECEIVED |
399                 EVENT_SESSION_MODIFY_RESPONSE_RECEIVED |
400                 EVENT_SESSION_EVENT |
401                 EVENT_PEER_DIMENSIONS_CHANGED |
402                 EVENT_VIDEO_QUALITY_CHANGED |
403                 EVENT_DATA_USAGE_CHANGED |
404                 EVENT_CAMERA_CAPABILITIES_CHANGED;
405 
406         private String mCallId;
407         private int mEvents;
408 
VideoCallCallback(String callId, int listeners)409         public VideoCallCallback(String callId, int listeners) {
410 
411             mCallId = callId;
412             mEvents = listeners & EVENT_ALL;
413         }
414 
startListeningForEvents(int events)415         public void startListeningForEvents(int events) {
416             Log.d(String.format(
417                     "VideoCallCallback(%s):startListeningForEvents(%x): events:%x",
418                     mCallId, events, mEvents));
419 
420             mEvents |= events & EVENT_ALL;
421 
422         }
423 
stopListeningForEvents(int events)424         public void stopListeningForEvents(int events) {
425             mEvents &= ~(events & EVENT_ALL);
426         }
427 
428         @Override
onSessionModifyRequestReceived(VideoProfile videoProfile)429         public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
430             Log.d(String.format("VideoCallCallback(%s):onSessionModifyRequestReceived()", mCallId));
431 
432             if ((mEvents & EVENT_SESSION_MODIFY_REQUEST_RECEIVED)
433                     == EVENT_SESSION_MODIFY_REQUEST_RECEIVED) {
434                 servicePostEvent(TelephonyConstants.EventTelecomVideoCallSessionModifyRequestReceived,
435                         new VideoCallEvent<VideoProfile>(mCallId, videoProfile));
436             }
437 
438         }
439 
440         @Override
onSessionModifyResponseReceived(int status, VideoProfile requestedProfile, VideoProfile responseProfile)441         public void onSessionModifyResponseReceived(int status,
442                 VideoProfile requestedProfile, VideoProfile responseProfile) {
443             Log.d("VideoCallCallback:onSessionModifyResponseReceived()");
444 
445             if ((mEvents & EVENT_SESSION_MODIFY_RESPONSE_RECEIVED)
446                     == EVENT_SESSION_MODIFY_RESPONSE_RECEIVED) {
447 
448                 HashMap<String, VideoProfile> smrrInfo = new HashMap<String, VideoProfile>();
449 
450                 smrrInfo.put("RequestedProfile", requestedProfile);
451                 smrrInfo.put("ResponseProfile", responseProfile);
452 
453                 servicePostEvent(TelephonyConstants.EventTelecomVideoCallSessionModifyResponseReceived,
454                         new VideoCallEvent<HashMap<String, VideoProfile>>(mCallId, smrrInfo));
455             }
456         }
457 
458         @Override
onCallSessionEvent(int event)459         public void onCallSessionEvent(int event) {
460             Log.d("VideoCallCallback:onCallSessionEvent()");
461 
462             String eventString = getVideoCallSessionEventString(event);
463 
464             if ((mEvents & EVENT_SESSION_EVENT)
465                     == EVENT_SESSION_EVENT) {
466                 servicePostEvent(TelephonyConstants.EventTelecomVideoCallSessionEvent,
467                         new VideoCallEvent<String>(mCallId, eventString));
468             }
469         }
470 
471         @Override
onPeerDimensionsChanged(int width, int height)472         public void onPeerDimensionsChanged(int width, int height) {
473             Log.d("VideoCallCallback:onPeerDimensionsChanged()");
474 
475             if ((mEvents & EVENT_PEER_DIMENSIONS_CHANGED)
476                     == EVENT_PEER_DIMENSIONS_CHANGED) {
477 
478                 HashMap<String, Integer> temp = new HashMap<String, Integer>();
479                 temp.put("Width", width);
480                 temp.put("Height", height);
481 
482                 servicePostEvent(TelephonyConstants.EventTelecomVideoCallPeerDimensionsChanged,
483                         new VideoCallEvent<HashMap<String, Integer>>(mCallId, temp));
484             }
485         }
486 
487         @Override
onVideoQualityChanged(int videoQuality)488         public void onVideoQualityChanged(int videoQuality) {
489             Log.d("VideoCallCallback:onVideoQualityChanged()");
490 
491             if ((mEvents & EVENT_VIDEO_QUALITY_CHANGED)
492                     == EVENT_VIDEO_QUALITY_CHANGED) {
493                 servicePostEvent(TelephonyConstants.EventTelecomVideoCallVideoQualityChanged,
494                         new VideoCallEvent<String>(mCallId,
495                                 getVideoCallQualityString(videoQuality)));
496             }
497         }
498 
499         @Override
onCallDataUsageChanged(long dataUsage)500         public void onCallDataUsageChanged(long dataUsage) {
501             Log.d("VideoCallCallback:onCallDataUsageChanged()");
502 
503             if ((mEvents & EVENT_DATA_USAGE_CHANGED)
504                     == EVENT_DATA_USAGE_CHANGED) {
505                 servicePostEvent(TelephonyConstants.EventTelecomVideoCallDataUsageChanged,
506                         new VideoCallEvent<Long>(mCallId, dataUsage));
507             }
508         }
509 
510         @Override
onCameraCapabilitiesChanged( CameraCapabilities cameraCapabilities)511         public void onCameraCapabilitiesChanged(
512                 CameraCapabilities cameraCapabilities) {
513             Log.d("VideoCallCallback:onCallDataUsageChanged()");
514 
515             if ((mEvents & EVENT_DATA_USAGE_CHANGED)
516                     == EVENT_DATA_USAGE_CHANGED) {
517                 servicePostEvent(TelephonyConstants.EventTelecomVideoCallCameraCapabilities,
518                         new VideoCallEvent<CameraCapabilities>(mCallId, cameraCapabilities));
519             }
520 
521         }
522     }
523 
524     /*
525      * Container Class for Call and CallCallback Objects
526      */
527     private class CallContainer {
528 
529         /*
530          * Call Container Members
531          */
532 
533         private Call mCall;
534         private CallCallback mCallCallback;
535         private VideoCall mVideoCall;
536         private VideoCallCallback mVideoCallCallback;
537 
538         /*
539          * Call Container Functions
540          */
541 
CallContainer(Call call, CallCallback callback, VideoCall videoCall, VideoCallCallback videoCallCallback)542         public CallContainer(Call call,
543                 CallCallback callback,
544                 VideoCall videoCall,
545                 VideoCallCallback videoCallCallback) {
546             mCall = call;
547             mCallCallback = callback;
548             mVideoCall = videoCall;
549             mVideoCallCallback = videoCallCallback;
550         }
551 
getCall()552         public Call getCall() {
553             return mCall;
554         }
555 
getCallback()556         public CallCallback getCallback() {
557             return mCallCallback;
558         }
559 
getVideoCall()560         public InCallService.VideoCall getVideoCall() {
561             return mVideoCall;
562         }
563 
getVideoCallCallback()564         public VideoCallCallback getVideoCallCallback() {
565             return mVideoCallCallback;
566         }
567 
updateVideoCall(VideoCall videoCall, VideoCallCallback videoCallCallback)568         public void updateVideoCall(VideoCall videoCall, VideoCallCallback videoCallCallback) {
569             if (videoCall == null && videoCallCallback != null) {
570                 Log.d("UpdateVideoCall: videoCall and videoCallCallback are null.");
571                 return;
572             }
573             mVideoCall = videoCall;
574             mVideoCallCallback = videoCallCallback;
575         }
576     }
577 
578     /** Indicates and controls the state of the audio playing or voice recording thread. */
579     enum HandleVoiceThreadState {
580         /** The audio playing/voice recording thread is terminated. */
581         TERMINATE,
582         /** The audio playing/voice recording thread is running. */
583         RUN
584     }
585 
586     /*
587      * TODO: b/26272583 Refactor so that these are instance members of the
588      * incallservice. Then we can perform null checks using the design pattern
589      * of the "manager" classes.
590      */
591 
592     private static EventFacade mEventFacade = null;
593     private static HashMap<String, CallContainer> mCallContainerMap =
594             new HashMap<String, CallContainer>();
595 
596     @Override
onCallAdded(Call call)597     public void onCallAdded(Call call) {
598         Log.d("onCallAdded: " + call.toString());
599         String id = getCallId(call);
600         Log.d("Adding " + id);
601         CallCallback callCallback = new CallCallback(id, CallCallback.EVENT_NONE);
602 
603         call.registerCallback(callCallback);
604         // Make sure the first call is used to play or record voice
605         if (playRecordCall == null) {
606             playRecordCall = call;
607         }
608 
609         VideoCall videoCall = call.getVideoCall();
610         VideoCallCallback videoCallCallback = null;
611 
612         if (videoCall != null) {
613             synchronized (mLock) {
614                 if (getVideoCallById(id) == null) {
615                     videoCallCallback = new VideoCallCallback(id, VideoCallCallback.EVENT_NONE);
616                     videoCall.registerCallback(videoCallCallback);
617                 }
618             }
619         }
620         else {
621             // No valid video object
622             Log.d("No Video Call provided to InCallService.");
623         }
624 
625         mCallContainerMap.put(id,
626                 new CallContainer(call,
627                         callCallback,
628                         videoCall,
629                         videoCallCallback));
630 
631         /*
632          * Once we have a call active, anchor the inCallService instance as a psuedo-singleton.
633          * Because object lifetime is not guaranteed we shouldn't do this in the
634          * constructor/destructor.
635          */
636         if (sService == null) {
637             sService = this;
638             mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
639         }
640         else if (sService != this) {
641             Log.e("Multiple InCall Services Active in SL4A!");
642         }
643 
644         CallListener.onCallAdded(id, call);
645     }
646 
647     @Override
onCallRemoved(Call call)648     public void onCallRemoved(Call call) {
649         Log.d("onCallRemoved: " + call.toString());
650         String id = getCallId(call);
651         Log.d("Removing " + id);
652 
653         mCallContainerMap.remove(id);
654 
655         if (getCallId(call).equals(getCallId(playRecordCall))) {
656             Log.d("Terminate the call for playing/recording.");
657             playAudioInCallState = TERMINATE;
658             playRecordCall = null;
659             recordVoiceInCallState = TERMINATE;
660             recordVoiceInCall = null;
661         }
662 
663         CallListener.onCallRemoved(id, call);
664 
665         if (mCallContainerMap.size() == 0) {
666             sService = null;
667         }
668     }
669 
setEventFacade(EventFacade facade)670     public static void setEventFacade(EventFacade facade) {
671         Log.d(String.format("setEventFacade(): Settings SL4A event facade to %s",
672                 (facade != null) ? facade.toString() : "null"));
673         mEventFacade = facade;
674     }
675 
servicePostEvent(String eventName, Object event)676     private static boolean servicePostEvent(String eventName, Object event) {
677 
678         if (mEventFacade == null) {
679             Log.d("servicePostEvent():SL4A eventFacade Is Null!!");
680             return false;
681         }
682 
683         mEventFacade.postEvent(eventName, event);
684 
685         return true;
686     }
687 
getCallId(Call call)688     public static String getCallId(Call call) {
689         if (call != null) {
690             return "Call:" + call.hashCode();
691         }
692         else
693             return "";
694     }
695 
getVideoCallId(InCallServiceImpl.VideoCall videoCall)696     public static String getVideoCallId(InCallServiceImpl.VideoCall videoCall) {
697         if (videoCall != null)
698             return "VideoCall:" + videoCall.hashCode();
699         else
700             return "";
701     }
702 
getCallById(String callId)703     public static Call getCallById(String callId) {
704 
705         CallContainer cc = mCallContainerMap.get(callId);
706 
707         if (cc != null) {
708             return cc.getCall();
709         }
710 
711         return null;
712     }
713 
getCallCallbackById(String callId)714     private static CallCallback getCallCallbackById(String callId) {
715 
716         CallContainer cc = mCallContainerMap.get(callId);
717 
718         if (cc != null) {
719             return cc.getCallback();
720         }
721 
722         return null;
723     }
724 
getVideoCallById(String callId)725     private static InCallService.VideoCall getVideoCallById(String callId) {
726 
727         CallContainer cc = mCallContainerMap.get(callId);
728 
729         if (cc != null) {
730             return cc.getVideoCall();
731 
732         }
733 
734         return null;
735     }
736 
737     private static VideoCallCallback
getVideoCallListenerById(String callId)738             getVideoCallListenerById(String callId) {
739 
740         CallContainer cc = mCallContainerMap.get(callId);
741 
742         if (cc != null) {
743             return cc.getVideoCallCallback();
744         }
745 
746         return null;
747     }
748 
749     /*
750      * Public Call/Phone Functions
751      */
752 
callDisconnect(String callId)753     public static void callDisconnect(String callId) {
754         Call c = getCallById(callId);
755         if (c == null) {
756             Log.d("callDisconnect: callId is null");
757             return;
758         }
759 
760         c.disconnect();
761     }
762 
holdCall(String callId)763     public static void holdCall(String callId) {
764         Call c = getCallById(callId);
765         if (c == null) {
766             Log.d("holdCall: callId is null");
767             return;
768         }
769         c.hold();
770     }
771 
muteCall(Boolean shouldMute)772     public static void muteCall(Boolean shouldMute) {
773         InCallServiceImpl svc = getService();
774 
775         if (svc == null) {
776             Log.d("muteCall: InCallServiceImpl is null.");
777             return;
778         }
779 
780         svc.setMuted(shouldMute);
781     }
782 
mergeCallsInConference(String callId)783     public static void mergeCallsInConference(String callId) {
784         Call c = getCallById(callId);
785         if (c == null) {
786             Log.d("mergeCallsInConference: callId is null");
787             return;
788         }
789         c.mergeConference();
790     }
791 
splitCallFromConf(String callId)792     public static void splitCallFromConf(String callId) {
793         Call c = getCallById(callId);
794         if (c == null) {
795             Log.d("splitCallFromConf: callId is null");
796             return;
797         }
798         c.splitFromConference();
799     }
800 
unholdCall(String callId)801     public static void unholdCall(String callId) {
802         Call c = getCallById(callId);
803         if (c == null) {
804             Log.d("unholdCall: callId is null");
805             return;
806         }
807         c.unhold();
808     }
809 
joinCallsInConf(String callIdOne, String callIdTwo)810     public static void joinCallsInConf(String callIdOne, String callIdTwo) {
811         Call callOne = getCallById(callIdOne);
812         Call callTwo = getCallById(callIdTwo);
813 
814         if (callOne == null || callTwo == null) {
815             Log.d("joinCallsInConf: callOne or CallTwo is null");
816             return;
817         }
818 
819         callOne.conference(callTwo);
820     }
821 
getCallIdList()822     public static Set<String> getCallIdList() {
823         return mCallContainerMap.keySet();
824     }
825 
clearCallList()826     public static void clearCallList() {
827         mCallContainerMap.clear();
828     }
829 
callGetState(String callId)830     public static String callGetState(String callId) {
831         Call c = getCallById(callId);
832         if (c == null) {
833             return getCallStateString(STATE_INVALID);
834         }
835 
836         return getCallStateString(c.getState());
837     }
838 
callGetDetails(String callId)839     public static Call.Details callGetDetails(String callId) {
840         Call c = getCallById(callId);
841         if (c == null) {
842             Log.d(String.format("Couldn't find an active call with ID:%s", callId));
843             return null;
844         }
845 
846         return c.getDetails();
847     }
848 
callGetCallProperties(String callId)849     public static List<String> callGetCallProperties(String callId) {
850         Call.Details details = callGetDetails(callId);
851 
852         if (details == null) {
853             return null;
854         }
855 
856         return getCallPropertiesString(details.getCallProperties());
857     }
858 
callGetCallCapabilities(String callId)859     public static List<String> callGetCallCapabilities(String callId) {
860         Call.Details details = callGetDetails(callId);
861 
862         if (details == null) {
863             return null;
864         }
865 
866         return getCallCapabilitiesString(details.getCallCapabilities());
867     }
868 
869     @SuppressWarnings("deprecation")
overrideProximitySensor(Boolean screenOn)870     public static void overrideProximitySensor(Boolean screenOn) {
871         InCallServiceImpl svc = getService();
872         if (svc == null) {
873             Log.d("overrideProximitySensor: InCallServiceImpl is null.");
874             return;
875         }
876 
877         Phone phone = svc.getPhone();
878         if (phone == null) {
879             Log.d("overrideProximitySensor: phone is null.");
880             return;
881         }
882 
883         phone.setProximitySensorOff(screenOn);
884     }
885 
serviceGetCallAudioState()886     public static CallAudioState serviceGetCallAudioState() {
887         InCallServiceImpl svc = getService();
888 
889         if (svc != null) {
890             return svc.getCallAudioState();
891         }
892         else {
893             return null;
894         }
895     }
896 
897     // Wonky name due to conflict with internal function
serviceSetAudioRoute(String route)898     public static void serviceSetAudioRoute(String route) {
899         InCallServiceImpl svc = getService();
900 
901         if (svc == null) {
902             Log.d("serviceSetAudioRoute: InCallServiceImpl is null.");
903             return;
904         }
905 
906         int r = getAudioRoute(route);
907 
908         Log.d(String.format("Setting Audio Route to %s:%d", route, r));
909 
910         if (r == INVALID_AUDIO_ROUTE) {
911             Log.d(String.format("Invalid Audio route %s:%d", route, r));
912             return;
913         }
914         svc.setAudioRoute(r);
915     }
916 
callStartListeningForEvent(String callId, String strEvent)917     public static void callStartListeningForEvent(String callId, String strEvent) {
918 
919         CallCallback cl = getCallCallbackById(callId);
920 
921         if (cl == null) {
922             Log.d("callStartListeningForEvent: CallCallback is null.");
923             return;
924         }
925 
926         int event = getCallCallbackEvent(strEvent);
927 
928         if (event == CallCallback.EVENT_INVALID) {
929             Log.d("callStartListeningForEvent: event is invalid.");
930             return;
931         }
932 
933         cl.startListeningForEvents(event);
934     }
935 
callStopListeningForEvent(String callId, String strEvent)936     public static void callStopListeningForEvent(String callId, String strEvent) {
937         CallCallback cl = getCallCallbackById(callId);
938 
939         if (cl == null) {
940             Log.d("callStopListeningForEvent: CallCallback is null.");
941             return;
942         }
943 
944         int event = getCallCallbackEvent(strEvent);
945 
946         if (event == CallCallback.EVENT_INVALID) {
947             Log.d("callStopListeningForEvent: event is invalid.");
948             return;
949         }
950 
951         cl.stopListeningForEvents(event);
952     }
953 
videoCallStartListeningForEvent(String callId, String strEvent)954     public static void videoCallStartListeningForEvent(String callId, String strEvent) {
955         VideoCallCallback cl = getVideoCallListenerById(callId);
956 
957         if (cl == null) {
958             Log.d(String.format("Couldn't find a call with call id:%s", callId));
959             return;
960         }
961 
962         int event = getVideoCallCallbackEvent(strEvent);
963 
964         if (event == VideoCallCallback.EVENT_INVALID) {
965             Log.d(String.format("Failed to find a valid event:[%s]", strEvent));
966             return;
967         }
968 
969         cl.startListeningForEvents(event);
970     }
971 
videoCallStopListeningForEvent(String callId, String strEvent)972     public static void videoCallStopListeningForEvent(String callId, String strEvent) {
973         VideoCallCallback cl = getVideoCallListenerById(callId);
974 
975         if (cl == null) {
976             Log.d("videoCallStopListeningForEvent: CallCallback is null.");
977             return;
978         }
979 
980         int event = getVideoCallCallbackEvent(strEvent);
981 
982         if (event == VideoCallCallback.EVENT_INVALID) {
983             Log.d("getVideoCallCallbackEvent: event is invalid.");
984             return;
985         }
986 
987         cl.stopListeningForEvents(event);
988     }
989 
videoCallGetState(String callId)990     public static String videoCallGetState(String callId) {
991         Call c = getCallById(callId);
992 
993         int state = CallCallback.STATE_INVALID;
994 
995         if (c == null) {
996             Log.d("videoCallGetState: call is null.");
997         }
998         else {
999             state = c.getDetails().getVideoState();
1000         }
1001 
1002         return getVideoCallStateString(state);
1003     }
1004 
videoCallSendSessionModifyRequest( String callId, String videoStateString, String videoQualityString)1005     public static void videoCallSendSessionModifyRequest(
1006             String callId, String videoStateString, String videoQualityString) {
1007         VideoCall vc = getVideoCallById(callId);
1008 
1009         if (vc == null) {
1010             Log.d("Invalid video call for call ID");
1011             return;
1012         }
1013 
1014         int videoState = getVideoCallState(videoStateString);
1015         int videoQuality = getVideoCallQuality(videoQualityString);
1016 
1017         Log.d(String.format("Sending Modify request for %s:%d, %s:%d",
1018                 videoStateString, videoState, videoQualityString, videoQuality));
1019 
1020         if (videoState == CallCallback.STATE_INVALID ||
1021                 videoQuality == QUALITY_INVALID || videoQuality == VideoProfile.QUALITY_UNKNOWN) {
1022             Log.d("Invalid session modify request!");
1023             return;
1024         }
1025 
1026         vc.sendSessionModifyRequest(new VideoProfile(videoState, videoQuality));
1027     }
1028 
videoCallSendSessionModifyResponse( String callId, String videoStateString, String videoQualityString)1029     public static void videoCallSendSessionModifyResponse(
1030             String callId, String videoStateString, String videoQualityString) {
1031         VideoCall vc = getVideoCallById(callId);
1032 
1033         if (vc == null) {
1034             Log.d("Invalid video call for call ID");
1035             return;
1036         }
1037 
1038         int videoState = getVideoCallState(videoStateString);
1039         int videoQuality = getVideoCallQuality(videoQualityString);
1040 
1041         Log.d(String.format("Sending Modify request for %s:%d, %s:%d",
1042                 videoStateString, videoState, videoQualityString, videoQuality));
1043 
1044         if (videoState == CallCallback.STATE_INVALID ||
1045                 videoQuality == QUALITY_INVALID || videoQuality == VideoProfile.QUALITY_UNKNOWN) {
1046             Log.d("Invalid session modify request!");
1047             return;
1048         }
1049 
1050         vc.sendSessionModifyResponse(new VideoProfile(videoState, videoQuality));
1051     }
1052 
callAnswer(String callId, String videoState)1053     public static void callAnswer(String callId, String videoState) {
1054         Call c = getCallById(callId);
1055 
1056         if (c == null) {
1057             Log.d("callAnswer: call is null.");
1058         }
1059 
1060         int state = getVideoCallState(videoState);
1061 
1062         if (state == CallCallback.STATE_INVALID) {
1063             Log.d("callAnswer: video state is invalid.");
1064             state = VideoProfile.STATE_AUDIO_ONLY;
1065         }
1066 
1067         c.answer(state);
1068     }
1069 
callReject(String callId, String message)1070     public static void callReject(String callId, String message) {
1071         Call c = getCallById(callId);
1072 
1073         if (c == null) {
1074             Log.d("callReject: call is null.");
1075         }
1076 
1077         c.reject((message != null) ? true : false, message);
1078     }
1079 
getCallParent(String callId)1080     public static String getCallParent(String callId) {
1081         Call c = getCallById(callId);
1082 
1083         if (c == null) {
1084             Log.d("getCallParent: call is null.");
1085             return null;
1086         }
1087         Call callParent = c.getParent();
1088         return getCallId(callParent);
1089     }
1090 
getCallChildren(String callId)1091     public static List<String> getCallChildren(String callId) {
1092         Call c = getCallById(callId);
1093 
1094         if (c == null) {
1095             Log.d("getCallChildren: call is null.");
1096             return null;
1097         }
1098         List<String> childrenList = new ArrayList<String>();
1099         List<Call> callChildren = c.getChildren();
1100         for (Call call : callChildren) {
1101             childrenList.add(getCallId(call));
1102         }
1103         return childrenList;
1104     }
1105 
swapCallsInConference(String callId)1106     public static void swapCallsInConference(String callId) {
1107         Call c = getCallById(callId);
1108         if (c == null) {
1109             Log.d("swapCallsInConference: call is null.");
1110             return;
1111         }
1112         c.swapConference();
1113     }
1114 
callPlayDtmfTone(String callId, char digit)1115     public static void callPlayDtmfTone(String callId, char digit) {
1116         Call c = getCallById(callId);
1117         if (c == null) {
1118             Log.d("callPlayDtmfTone: call is null.");
1119             return;
1120         }
1121         c.playDtmfTone(digit);
1122     }
1123 
callStopDtmfTone(String callId)1124     public static void callStopDtmfTone(String callId) {
1125         Call c = getCallById(callId);
1126         if (c == null) {
1127             Log.d("callStopDtmfTone: call is null.");
1128             return;
1129         }
1130         c.stopDtmfTone();
1131     }
1132 
callGetCannedTextResponses(String callId)1133     public static List<String> callGetCannedTextResponses(String callId) {
1134         Call c = getCallById(callId);
1135         if (c == null) {
1136             return null;
1137         }
1138 
1139         return c.getCannedTextResponses();
1140     }
1141 
1142     /*
1143      * String Mapping Functions for Facade Parameter Translation
1144      */
1145 
getVideoCallStateString(int state)1146     public static String getVideoCallStateString(int state) {
1147         switch (state) {
1148             case VIDEO_STATE_AUDIO_ONLY:
1149                 return TelephonyConstants.VT_STATE_AUDIO_ONLY;
1150             case VIDEO_STATE_TX_ENABLED:
1151                 return TelephonyConstants.VT_STATE_TX_ENABLED;
1152             case VIDEO_STATE_RX_ENABLED:
1153                 return TelephonyConstants.VT_STATE_RX_ENABLED;
1154             case VIDEO_STATE_BIDIRECTIONAL:
1155                 return TelephonyConstants.VT_STATE_BIDIRECTIONAL;
1156             case VIDEO_STATE_TX_PAUSED:
1157                 return TelephonyConstants.VT_STATE_TX_PAUSED;
1158             case VIDEO_STATE_RX_PAUSED:
1159                 return TelephonyConstants.VT_STATE_RX_PAUSED;
1160             case VIDEO_STATE_BIDIRECTIONAL_PAUSED:
1161                 return TelephonyConstants.VT_STATE_BIDIRECTIONAL_PAUSED;
1162             default:
1163         }
1164         Log.d("getVideoCallStateString: state is invalid.");
1165         return TelephonyConstants.VT_STATE_STATE_INVALID;
1166     }
1167 
getVideoCallState(String state)1168     public static int getVideoCallState(String state) {
1169         switch (state.toUpperCase()) {
1170             case TelephonyConstants.VT_STATE_AUDIO_ONLY:
1171                 return VIDEO_STATE_AUDIO_ONLY;
1172             case TelephonyConstants.VT_STATE_TX_ENABLED:
1173                 return VIDEO_STATE_TX_ENABLED;
1174             case TelephonyConstants.VT_STATE_RX_ENABLED:
1175                 return VIDEO_STATE_RX_ENABLED;
1176             case TelephonyConstants.VT_STATE_BIDIRECTIONAL:
1177                 return VIDEO_STATE_BIDIRECTIONAL;
1178             case TelephonyConstants.VT_STATE_TX_PAUSED:
1179                 return VIDEO_STATE_TX_PAUSED;
1180             case TelephonyConstants.VT_STATE_RX_PAUSED:
1181                 return VIDEO_STATE_RX_PAUSED;
1182             case TelephonyConstants.VT_STATE_BIDIRECTIONAL_PAUSED:
1183                 return VIDEO_STATE_BIDIRECTIONAL_PAUSED;
1184 
1185             default:
1186         }
1187         Log.d("getVideoCallState: state is invalid.");
1188         return CallCallback.STATE_INVALID;
1189     }
1190 
getVideoCallQuality(String quality)1191     private static int getVideoCallQuality(String quality) {
1192 
1193         switch (quality.toUpperCase()) {
1194             case TelephonyConstants.VT_VIDEO_QUALITY_UNKNOWN:
1195                 return VideoProfile.QUALITY_UNKNOWN;
1196             case TelephonyConstants.VT_VIDEO_QUALITY_HIGH:
1197                 return VideoProfile.QUALITY_HIGH;
1198             case TelephonyConstants.VT_VIDEO_QUALITY_MEDIUM:
1199                 return VideoProfile.QUALITY_MEDIUM;
1200             case TelephonyConstants.VT_VIDEO_QUALITY_LOW:
1201                 return VideoProfile.QUALITY_LOW;
1202             case TelephonyConstants.VT_VIDEO_QUALITY_DEFAULT:
1203                 return VideoProfile.QUALITY_DEFAULT;
1204             default:
1205         }
1206         Log.d("getVideoCallQuality: quality is invalid.");
1207         return QUALITY_INVALID;
1208     }
1209 
getVideoCallQualityString(int quality)1210     public static String getVideoCallQualityString(int quality) {
1211         switch (quality) {
1212             case VideoProfile.QUALITY_UNKNOWN:
1213                 return TelephonyConstants.VT_VIDEO_QUALITY_UNKNOWN;
1214             case VideoProfile.QUALITY_HIGH:
1215                 return TelephonyConstants.VT_VIDEO_QUALITY_HIGH;
1216             case VideoProfile.QUALITY_MEDIUM:
1217                 return TelephonyConstants.VT_VIDEO_QUALITY_MEDIUM;
1218             case VideoProfile.QUALITY_LOW:
1219                 return TelephonyConstants.VT_VIDEO_QUALITY_LOW;
1220             case VideoProfile.QUALITY_DEFAULT:
1221                 return TelephonyConstants.VT_VIDEO_QUALITY_DEFAULT;
1222             default:
1223         }
1224         Log.d("getVideoCallQualityString: quality is invalid.");
1225         return TelephonyConstants.VT_VIDEO_QUALITY_INVALID;
1226     }
1227 
getCallCallbackEvent(String event)1228     private static int getCallCallbackEvent(String event) {
1229 
1230         switch (event.toUpperCase()) {
1231             case "EVENT_STATE_CHANGED":
1232                 return CallCallback.EVENT_STATE_CHANGED;
1233             case "EVENT_PARENT_CHANGED":
1234                 return CallCallback.EVENT_PARENT_CHANGED;
1235             case "EVENT_CHILDREN_CHANGED":
1236                 return CallCallback.EVENT_CHILDREN_CHANGED;
1237             case "EVENT_DETAILS_CHANGED":
1238                 return CallCallback.EVENT_DETAILS_CHANGED;
1239             case "EVENT_CANNED_TEXT_RESPONSES_LOADED":
1240                 return CallCallback.EVENT_CANNED_TEXT_RESPONSES_LOADED;
1241             case "EVENT_POST_DIAL_WAIT":
1242                 return CallCallback.EVENT_POST_DIAL_WAIT;
1243             case "EVENT_VIDEO_CALL_CHANGED":
1244                 return CallCallback.EVENT_VIDEO_CALL_CHANGED;
1245             case "EVENT_CALL_DESTROYED":
1246                 return CallCallback.EVENT_CALL_DESTROYED;
1247             case "EVENT_CONFERENCABLE_CALLS_CHANGED":
1248                 return CallCallback.EVENT_CONFERENCABLE_CALLS_CHANGED;
1249         }
1250         Log.d("getCallCallbackEvent: event is invalid.");
1251         return CallCallback.EVENT_INVALID;
1252     }
1253 
getCallCallbackEventString(int event)1254     public static String getCallCallbackEventString(int event) {
1255 
1256         switch (event) {
1257             case CallCallback.EVENT_STATE_CHANGED:
1258                 return "EVENT_STATE_CHANGED";
1259             case CallCallback.EVENT_PARENT_CHANGED:
1260                 return "EVENT_PARENT_CHANGED";
1261             case CallCallback.EVENT_CHILDREN_CHANGED:
1262                 return "EVENT_CHILDREN_CHANGED";
1263             case CallCallback.EVENT_DETAILS_CHANGED:
1264                 return "EVENT_DETAILS_CHANGED";
1265             case CallCallback.EVENT_CANNED_TEXT_RESPONSES_LOADED:
1266                 return "EVENT_CANNED_TEXT_RESPONSES_LOADED";
1267             case CallCallback.EVENT_POST_DIAL_WAIT:
1268                 return "EVENT_POST_DIAL_WAIT";
1269             case CallCallback.EVENT_VIDEO_CALL_CHANGED:
1270                 return "EVENT_VIDEO_CALL_CHANGED";
1271             case CallCallback.EVENT_CALL_DESTROYED:
1272                 return "EVENT_CALL_DESTROYED";
1273             case CallCallback.EVENT_CONFERENCABLE_CALLS_CHANGED:
1274                 return "EVENT_CONFERENCABLE_CALLS_CHANGED";
1275         }
1276         Log.d("getCallCallbackEventString: event is invalid.");
1277         return "EVENT_INVALID";
1278     }
1279 
getVideoCallCallbackEvent(String event)1280     private static int getVideoCallCallbackEvent(String event) {
1281 
1282         switch (event) {
1283             case TelephonyConstants.EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED:
1284                 return VideoCallCallback.EVENT_SESSION_MODIFY_REQUEST_RECEIVED;
1285             case TelephonyConstants.EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED:
1286                 return VideoCallCallback.EVENT_SESSION_MODIFY_RESPONSE_RECEIVED;
1287             case TelephonyConstants.EVENT_VIDEO_SESSION_EVENT:
1288                 return VideoCallCallback.EVENT_SESSION_EVENT;
1289             case TelephonyConstants.EVENT_VIDEO_PEER_DIMENSIONS_CHANGED:
1290                 return VideoCallCallback.EVENT_PEER_DIMENSIONS_CHANGED;
1291             case TelephonyConstants.EVENT_VIDEO_QUALITY_CHANGED:
1292                 return VideoCallCallback.EVENT_VIDEO_QUALITY_CHANGED;
1293             case TelephonyConstants.EVENT_VIDEO_DATA_USAGE_CHANGED:
1294                 return VideoCallCallback.EVENT_DATA_USAGE_CHANGED;
1295             case TelephonyConstants.EVENT_VIDEO_CAMERA_CAPABILITIES_CHANGED:
1296                 return VideoCallCallback.EVENT_CAMERA_CAPABILITIES_CHANGED;
1297         }
1298         Log.d("getVideoCallCallbackEvent: event is invalid.");
1299         return CallCallback.EVENT_INVALID;
1300     }
1301 
getVideoCallCallbackEventString(int event)1302     public static String getVideoCallCallbackEventString(int event) {
1303 
1304         switch (event) {
1305             case VideoCallCallback.EVENT_SESSION_MODIFY_REQUEST_RECEIVED:
1306                 return TelephonyConstants.EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED;
1307             case VideoCallCallback.EVENT_SESSION_MODIFY_RESPONSE_RECEIVED:
1308                 return TelephonyConstants.EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED;
1309             case VideoCallCallback.EVENT_SESSION_EVENT:
1310                 return TelephonyConstants.EVENT_VIDEO_SESSION_EVENT;
1311             case VideoCallCallback.EVENT_PEER_DIMENSIONS_CHANGED:
1312                 return TelephonyConstants.EVENT_VIDEO_PEER_DIMENSIONS_CHANGED;
1313             case VideoCallCallback.EVENT_VIDEO_QUALITY_CHANGED:
1314                 return TelephonyConstants.EVENT_VIDEO_QUALITY_CHANGED;
1315             case VideoCallCallback.EVENT_DATA_USAGE_CHANGED:
1316                 return TelephonyConstants.EVENT_VIDEO_DATA_USAGE_CHANGED;
1317             case VideoCallCallback.EVENT_CAMERA_CAPABILITIES_CHANGED:
1318                 return TelephonyConstants.EVENT_VIDEO_CAMERA_CAPABILITIES_CHANGED;
1319         }
1320         Log.d("getVideoCallCallbackEventString: event is invalid.");
1321         return TelephonyConstants.EVENT_VIDEO_INVALID;
1322     }
1323 
getCallStateString(int state)1324     public static String getCallStateString(int state) {
1325         switch (state) {
1326             case Call.STATE_NEW:
1327                 return TelephonyConstants.CALL_STATE_NEW;
1328             case Call.STATE_DIALING:
1329                 return TelephonyConstants.CALL_STATE_DIALING;
1330             case Call.STATE_RINGING:
1331                 return TelephonyConstants.CALL_STATE_RINGING;
1332             case Call.STATE_HOLDING:
1333                 return TelephonyConstants.CALL_STATE_HOLDING;
1334             case Call.STATE_ACTIVE:
1335                 return TelephonyConstants.CALL_STATE_ACTIVE;
1336             case Call.STATE_DISCONNECTED:
1337                 return TelephonyConstants.CALL_STATE_DISCONNECTED;
1338             case Call.STATE_PRE_DIAL_WAIT:
1339                 return TelephonyConstants.CALL_STATE_PRE_DIAL_WAIT;
1340             case Call.STATE_CONNECTING:
1341                 return TelephonyConstants.CALL_STATE_CONNECTING;
1342             case Call.STATE_DISCONNECTING:
1343                 return TelephonyConstants.CALL_STATE_DISCONNECTING;
1344             case STATE_INVALID:
1345                 return TelephonyConstants.CALL_STATE_INVALID;
1346             default:
1347                 return TelephonyConstants.CALL_STATE_UNKNOWN;
1348         }
1349     }
1350 
getAudioRoute(String audioRoute)1351     private static int getAudioRoute(String audioRoute) {
1352         switch (audioRoute.toUpperCase()) {
1353             case TelephonyConstants.AUDIO_ROUTE_BLUETOOTH:
1354                 return CallAudioState.ROUTE_BLUETOOTH;
1355             case TelephonyConstants.AUDIO_ROUTE_EARPIECE:
1356                 return CallAudioState.ROUTE_EARPIECE;
1357             case TelephonyConstants.AUDIO_ROUTE_SPEAKER:
1358                 return CallAudioState.ROUTE_SPEAKER;
1359             case TelephonyConstants.AUDIO_ROUTE_WIRED_HEADSET:
1360                 return CallAudioState.ROUTE_WIRED_HEADSET;
1361             case TelephonyConstants.AUDIO_ROUTE_WIRED_OR_EARPIECE:
1362                 return CallAudioState.ROUTE_WIRED_OR_EARPIECE;
1363             default:
1364                 return INVALID_AUDIO_ROUTE;
1365         }
1366     }
1367 
getAudioRouteString(int audioRoute)1368     public static String getAudioRouteString(int audioRoute) {
1369         return CallAudioState.audioRouteToString(audioRoute);
1370     }
1371 
getVideoCallSessionEventString(int event)1372     public static String getVideoCallSessionEventString(int event) {
1373 
1374         switch (event) {
1375             case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
1376                 return TelephonyConstants.SESSION_EVENT_RX_PAUSE;
1377             case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
1378                 return TelephonyConstants.SESSION_EVENT_RX_RESUME;
1379             case Connection.VideoProvider.SESSION_EVENT_TX_START:
1380                 return TelephonyConstants.SESSION_EVENT_TX_START;
1381             case Connection.VideoProvider.SESSION_EVENT_TX_STOP:
1382                 return TelephonyConstants.SESSION_EVENT_TX_STOP;
1383             case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
1384                 return TelephonyConstants.SESSION_EVENT_CAMERA_FAILURE;
1385             case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY:
1386                 return TelephonyConstants.SESSION_EVENT_CAMERA_READY;
1387             default:
1388                 return TelephonyConstants.SESSION_EVENT_UNKNOWN;
1389         }
1390     }
1391 
getCallCapabilityString(int capability)1392     public static String getCallCapabilityString(int capability) {
1393         switch (capability) {
1394             case Call.Details.CAPABILITY_HOLD:
1395                 return TelephonyConstants.CALL_CAPABILITY_HOLD;
1396             case Call.Details.CAPABILITY_SUPPORT_HOLD:
1397                 return TelephonyConstants.CALL_CAPABILITY_SUPPORT_HOLD;
1398             case Call.Details.CAPABILITY_MERGE_CONFERENCE:
1399                 return TelephonyConstants.CALL_CAPABILITY_MERGE_CONFERENCE;
1400             case Call.Details.CAPABILITY_SWAP_CONFERENCE:
1401                 return TelephonyConstants.CALL_CAPABILITY_SWAP_CONFERENCE;
1402             case Call.Details.CAPABILITY_UNUSED_1:
1403                 return TelephonyConstants.CALL_CAPABILITY_UNUSED_1;
1404             case Call.Details.CAPABILITY_RESPOND_VIA_TEXT:
1405                 return TelephonyConstants.CALL_CAPABILITY_RESPOND_VIA_TEXT;
1406             case Call.Details.CAPABILITY_MUTE:
1407                 return TelephonyConstants.CALL_CAPABILITY_MUTE;
1408             case Call.Details.CAPABILITY_MANAGE_CONFERENCE:
1409                 return TelephonyConstants.CALL_CAPABILITY_MANAGE_CONFERENCE;
1410             case Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_RX:
1411                 return TelephonyConstants.CALL_CAPABILITY_SUPPORTS_VT_LOCAL_RX;
1412             case Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX:
1413                 return TelephonyConstants.CALL_CAPABILITY_SUPPORTS_VT_LOCAL_TX;
1414             case Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL:
1415                 return TelephonyConstants.CALL_CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL;
1416             case Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX:
1417                 return TelephonyConstants.CALL_CAPABILITY_SUPPORTS_VT_REMOTE_RX;
1418             case Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_TX:
1419                 return TelephonyConstants.CALL_CAPABILITY_SUPPORTS_VT_REMOTE_TX;
1420             case Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL:
1421                 return TelephonyConstants.CALL_CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL;
1422             case Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE:
1423                 return TelephonyConstants.CALL_CAPABILITY_SEPARATE_FROM_CONFERENCE;
1424             case Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE:
1425                 return TelephonyConstants.CALL_CAPABILITY_DISCONNECT_FROM_CONFERENCE;
1426             case Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO:
1427                 return TelephonyConstants.CALL_CAPABILITY_SPEED_UP_MT_AUDIO;
1428             case Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO:
1429                 return TelephonyConstants.CALL_CAPABILITY_CAN_UPGRADE_TO_VIDEO;
1430             case Call.Details.CAPABILITY_CAN_PAUSE_VIDEO:
1431                 return TelephonyConstants.CALL_CAPABILITY_CAN_PAUSE_VIDEO;
1432         }
1433         return TelephonyConstants.CALL_CAPABILITY_UNKOWN;
1434     }
1435 
getCallCapabilitiesString(int capabilities)1436     public static List<String> getCallCapabilitiesString(int capabilities) {
1437         final int[] capabilityConstants = new int[] {
1438                 Call.Details.CAPABILITY_HOLD,
1439                 Call.Details.CAPABILITY_SUPPORT_HOLD,
1440                 Call.Details.CAPABILITY_MERGE_CONFERENCE,
1441                 Call.Details.CAPABILITY_SWAP_CONFERENCE,
1442                 Call.Details.CAPABILITY_UNUSED_1,
1443                 Call.Details.CAPABILITY_RESPOND_VIA_TEXT,
1444                 Call.Details.CAPABILITY_MUTE,
1445                 Call.Details.CAPABILITY_MANAGE_CONFERENCE,
1446                 Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
1447                 Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
1448                 Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
1449                 Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
1450                 Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
1451                 Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
1452                 Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE,
1453                 Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
1454                 Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO,
1455                 Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
1456                 Call.Details.CAPABILITY_CAN_PAUSE_VIDEO
1457         };
1458 
1459         List<String> capabilityList = new ArrayList<String>();
1460 
1461         for (int capability : capabilityConstants) {
1462             if ((capabilities & capability) == capability) {
1463                 capabilityList.add(getCallCapabilityString(capability));
1464             }
1465         }
1466         return capabilityList;
1467     }
1468 
getCallPropertyString(int property)1469     public static String getCallPropertyString(int property) {
1470 
1471         switch (property) {
1472             case Call.Details.PROPERTY_CONFERENCE:
1473                 return TelephonyConstants.CALL_PROPERTY_CONFERENCE;
1474             case Call.Details.PROPERTY_GENERIC_CONFERENCE:
1475                 return TelephonyConstants.CALL_PROPERTY_GENERIC_CONFERENCE;
1476             case Call.Details.PROPERTY_EMERGENCY_CALLBACK_MODE:
1477                 return TelephonyConstants.CALL_PROPERTY_EMERGENCY_CALLBACK_MODE;
1478             case Call.Details.PROPERTY_WIFI:
1479                 return TelephonyConstants.CALL_PROPERTY_WIFI;
1480             case Call.Details.PROPERTY_HIGH_DEF_AUDIO:
1481                 return TelephonyConstants.CALL_PROPERTY_HIGH_DEF_AUDIO;
1482             default:
1483                 return TelephonyConstants.CALL_PROPERTY_UNKNOWN;
1484         }
1485     }
1486 
getCallPropertiesString(int properties)1487     public static List<String> getCallPropertiesString(int properties) {
1488         final int[] propertyConstants = new int[] {
1489                 Call.Details.PROPERTY_CONFERENCE,
1490                 Call.Details.PROPERTY_GENERIC_CONFERENCE,
1491                 Call.Details.PROPERTY_EMERGENCY_CALLBACK_MODE,
1492                 Call.Details.PROPERTY_WIFI,
1493                 Call.Details.PROPERTY_HIGH_DEF_AUDIO
1494         };
1495 
1496         List<String> propertyList = new ArrayList<String>();
1497 
1498         for (int property : propertyConstants) {
1499             if ((properties & property) == property) {
1500                 propertyList.add(getCallPropertyString(property));
1501             }
1502         }
1503 
1504         return propertyList;
1505     }
1506 
1507     /**
1508      * Plays an audio file specified by {@code audioFileName} during a phone call.
1509      *
1510      * The method first checks if {@link #Call}, {@code audioFileName} and {@link #AudioDeviceInfo}
1511      * exist. Finally, it creates a {@link #PlayAudioInCall} which creates a thread to perform
1512      * audio playing.
1513      * The method is called by {@link TelephonyManagerFacade#telephonyPlayAudioFile()}.
1514      *
1515      * @return {@code true} if the audio file is successfully played. Otherwise, {@code false}
1516      */
playAudioFile(String audioFileName)1517     public static boolean playAudioFile(String audioFileName) {
1518         Log.d(String.format("Playing audio file \"%s\"...", audioFileName));
1519         if (playAudioInCallState.equals(RUN)) {
1520             Log.d("Playing is ongoing!");
1521             return false;
1522         }
1523         if (getService() == null) {
1524             Log.d("InCallService isn't activated yet");
1525             return false;
1526         }
1527         audioTelephonyInfo = getAudioDeviceInfo();
1528         if (audioTelephonyInfo == null) {
1529             Log.d("No Telephony AudioDeviceInfo!");
1530             return false;
1531         }
1532         playAudioFile = new File(getService().getFilesDir(), audioFileName);
1533         if (!playAudioFile.exists()) {
1534             Log.d(String.format("%s not found in files folder!",audioFileName));
1535             return false;
1536         }
1537         playAudioInCall = new PlayAudioInCall(mEventFacade,
1538             playRecordCall, playAudioFile, audioTelephonyInfo);
1539         return playAudioInCall.playAudioFile();
1540     }
1541 
1542     /** Gets the audio telephony device during in a call.
1543      *
1544      * @return null if not found audio telephony device. Otherwise, an instance of
1545      * {@link #AudioDeviceInfo}
1546      * */
getAudioDeviceInfo()1547     public static AudioDeviceInfo getAudioDeviceInfo() {
1548         AudioDeviceInfo[] audioDeviceInfoList = mAudioManager.getDevices(
1549             AudioManager.GET_DEVICES_OUTPUTS);
1550         for (AudioDeviceInfo info : audioDeviceInfoList) {
1551             if (info.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
1552                 Log.d(String.format("Found audio telephony device: %d", info.getType()));
1553                 return info;
1554             }
1555         }
1556         return null;
1557     }
1558 
getPlayAudioInCallState()1559     public static HandleVoiceThreadState getPlayAudioInCallState() {
1560         return playAudioInCallState;
1561     }
1562 
setPlayAudioInCallState(HandleVoiceThreadState state)1563     public static void setPlayAudioInCallState(HandleVoiceThreadState state) {
1564         playAudioInCallState = state;
1565     }
1566 
stopPlayAudioFile()1567     public static void stopPlayAudioFile() {
1568         if (playAudioInCallState.equals(RUN) && playAudioInCall != null) {
1569             Log.d("Stop playing audio successfully!");
1570             playAudioInCallState = TERMINATE;
1571         }
1572     }
1573 
1574     /**
1575      * Records voice during a phone call.
1576      *
1577      * The method checks the following items before creating a thread to record voice.
1578      * <ol>
1579      *   <li>Check recoding state. If there is already a voice recording, ignore the request.</li>
1580      *   <li>Check if call has been established.</li>
1581      *   <li>Check the input sample rate and channel count to meet constraints.</li>
1582      *   <li>Check if the record wav file is created successfully.</li>
1583      * </ol>
1584      * @param recordWavFile indicates the wav file name of the recording voice
1585      * @param sampleRate indicates sampling rate of the recording voice
1586      * @param channelCount indicates voice channel number to be recorded
1587      * @return {@code true} if voice is successfully recorded. Otherwise, {@code false}
1588      */
recordVoice( String recordWavFile, int sampleRate, int channelCount, boolean cancelNoiseEcho)1589     public static boolean recordVoice(
1590         String recordWavFile, int sampleRate, int channelCount, boolean cancelNoiseEcho) {
1591         Log.d(String.format("Recording voice to  the \"%s\" file...", recordWavFile));
1592         if (getRecordVoiceInCallState().equals(RUN)) {
1593             Log.d("Recording is ongoing!");
1594             return false;
1595         }
1596         if (getService() == null) {
1597             Log.d("InCallService isn't activated yet");
1598             return false;
1599         }
1600         if (sampleRate != SAMPLE_RATE_16K && sampleRate != SAMPLE_RATE_48K) {
1601             Log.e(String.format("Don't support sample rate: %d", sampleRate));
1602             return false;
1603         }
1604         if (channelCount != MONO_CHANNEL && channelCount != STEREO_CHANNEL) {
1605             Log.e(String.format("Don't support channel count: %d", channelCount));
1606             return false;
1607         }
1608         recordVoiceFile = new File(getService().getFilesDir(), recordWavFile);
1609         if (!recordVoiceFile.exists()) {
1610             try {
1611                 Log.d(String.format("Creates a empty %s wav file to store voice data!",
1612                     recordWavFile));
1613                 recordVoiceFile.createNewFile();
1614             } catch (IOException e) {
1615                 Log.e(String.format("Failed to create %s wav file!", recordWavFile));
1616                 return false;
1617             }
1618         }
1619         Log.d(String.format("The voice recording info: wav file: %s, Sampling rate: %d, channel count: %d",
1620             recordWavFile, sampleRate, channelCount));
1621         recordVoiceInCall = new RecordVoiceInCall(mEventFacade, playRecordCall, recordVoiceFile,
1622             sampleRate, channelCount, cancelNoiseEcho);
1623         return recordVoiceInCall.recordVoice();
1624     }
1625 
stopRecordVoice()1626     public static void stopRecordVoice() {
1627         if (getRecordVoiceInCallState().equals(RUN) && playAudioInCall != null) {
1628             Log.d("Stop recording voice successfully!");
1629             setRecordVoiceInCallState(TERMINATE);
1630         }
1631     }
1632 
getRecordVoiceInCallState()1633     public static HandleVoiceThreadState getRecordVoiceInCallState() {
1634         return recordVoiceInCallState;
1635     }
1636 
setRecordVoiceInCallState(HandleVoiceThreadState state)1637     public static void setRecordVoiceInCallState(HandleVoiceThreadState state) {
1638         recordVoiceInCallState = state;
1639     }
1640 
getCallPresentationInfoString(int presentation)1641     public static String getCallPresentationInfoString(int presentation) {
1642         switch (presentation) {
1643             case TelecomManager.PRESENTATION_ALLOWED:
1644                 return TelephonyConstants.CALL_PRESENTATION_ALLOWED;
1645             case TelecomManager.PRESENTATION_RESTRICTED:
1646                 return TelephonyConstants.CALL_PRESENTATION_RESTRICTED;
1647             case TelecomManager.PRESENTATION_PAYPHONE:
1648                 return TelephonyConstants.CALL_PRESENTATION_PAYPHONE;
1649             default:
1650                 return TelephonyConstants.CALL_PRESENTATION_UNKNOWN;
1651         }
1652     }
1653 }
1654