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