PageRenderTime 129ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/android/server/telecom/CallsManager.java

https://gitlab.com/Atomic-ROM/packages_services_Telecomm
Java | 1211 lines | 830 code | 134 blank | 247 comment | 197 complexity | bb6d4cbacd50734cb04d7a880325b5c7 MD5 | raw file
  1. /*
  2. * Copyright (C) 2013 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. package com.android.server.telecom;
  17. import android.content.Context;
  18. import android.net.Uri;
  19. import android.os.Bundle;
  20. import android.os.Handler;
  21. import android.os.Looper;
  22. import android.os.Message;
  23. import android.os.SystemProperties;
  24. import android.os.Trace;
  25. import android.provider.CallLog.Calls;
  26. import android.provider.Settings;
  27. import android.telecom.CallAudioState;
  28. import android.telecom.Conference;
  29. import android.telecom.Connection;
  30. import android.telecom.DisconnectCause;
  31. import android.telecom.GatewayInfo;
  32. import android.telecom.ParcelableConference;
  33. import android.telecom.ParcelableConnection;
  34. import android.telecom.PhoneAccount;
  35. import android.telecom.PhoneAccountHandle;
  36. import android.telecom.TelecomManager;
  37. import android.telecom.VideoProfile;
  38. import android.telephony.PhoneNumberUtils;
  39. import android.telephony.TelephonyManager;
  40. import android.text.TextUtils;
  41. import com.android.internal.annotations.VisibleForTesting;
  42. import com.android.internal.telephony.PhoneConstants;
  43. import com.android.internal.telephony.TelephonyProperties;
  44. import com.android.internal.util.IndentingPrintWriter;
  45. import com.android.server.telecom.ui.ViceNotificationImpl;
  46. import java.util.Collection;
  47. import java.util.Collections;
  48. import java.util.HashSet;
  49. import java.util.List;
  50. import java.util.Objects;
  51. import java.util.Set;
  52. import java.util.concurrent.ConcurrentHashMap;
  53. /**
  54. * Singleton.
  55. *
  56. * NOTE: by design most APIs are package private, use the relevant adapter/s to allow
  57. * access from other packages specifically refraining from passing the CallsManager instance
  58. * beyond the com.android.server.telecom package boundary.
  59. */
  60. @VisibleForTesting
  61. public class CallsManager extends Call.ListenerBase implements VideoProviderProxy.Listener {
  62. // TODO: Consider renaming this CallsManagerPlugin.
  63. interface CallsManagerListener {
  64. void onCallAdded(Call call);
  65. void onCallRemoved(Call call);
  66. void onCallStateChanged(Call call, int oldState, int newState);
  67. void onConnectionServiceChanged(
  68. Call call,
  69. ConnectionServiceWrapper oldService,
  70. ConnectionServiceWrapper newService);
  71. void onIncomingCallAnswered(Call call);
  72. void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage);
  73. void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall);
  74. void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState);
  75. void onRingbackRequested(Call call, boolean ringback);
  76. void onIsConferencedChanged(Call call);
  77. void onIsVoipAudioModeChanged(Call call);
  78. void onVideoStateChanged(Call call);
  79. void onCanAddCallChanged(boolean canAddCall);
  80. void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
  81. void onMergeFailed(Call call);
  82. }
  83. private static final String TAG = "CallsManager";
  84. private static final int MAXIMUM_LIVE_CALLS = 1;
  85. private static final int MAXIMUM_HOLD_CALLS = 1;
  86. private static final int MAXIMUM_RINGING_CALLS = 1;
  87. private static final int MAXIMUM_DIALING_CALLS = 1;
  88. private static final int MAXIMUM_OUTGOING_CALLS = 1;
  89. private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
  90. private static final int MAXIMUM_DSDA_LIVE_CALLS = 2;
  91. private static final int MAXIMUM_DSDA_HOLD_CALLS = 2;
  92. private static final int MAXIMUM_DSDA_TOP_LEVEL_CALLS = 4;
  93. private static final int[] OUTGOING_CALL_STATES =
  94. {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING};
  95. private static final int[] LIVE_CALL_STATES =
  96. {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
  97. CallState.ACTIVE};
  98. /**
  99. * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
  100. * calls are added to the map and removed when the calls move to the disconnected state.
  101. *
  102. * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
  103. * load factor before resizing, 1 means we only expect a single thread to
  104. * access the map so make only a single shard
  105. */
  106. private final Set<Call> mCalls = Collections.newSetFromMap(
  107. new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
  108. private final ConnectionServiceRepository mConnectionServiceRepository;
  109. private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
  110. private final InCallController mInCallController;
  111. private final CallAudioManager mCallAudioManager;
  112. private RespondViaSmsManager mRespondViaSmsManager;
  113. private final Ringer mRinger;
  114. private final InCallWakeLockController mInCallWakeLockController;
  115. // For this set initial table size to 16 because we add 13 listeners in
  116. // the CallsManager constructor.
  117. private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
  118. new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
  119. private final HeadsetMediaButton mHeadsetMediaButton;
  120. private final WiredHeadsetManager mWiredHeadsetManager;
  121. private final DockManager mDockManager;
  122. private final TtyManager mTtyManager;
  123. private final ProximitySensorManager mProximitySensorManager;
  124. private final PhoneStateBroadcaster mPhoneStateBroadcaster;
  125. private final CallLogManager mCallLogManager;
  126. private final Context mContext;
  127. private final TelecomSystem.SyncRoot mLock;
  128. private final ContactsAsyncHelper mContactsAsyncHelper;
  129. private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
  130. private final PhoneAccountRegistrar mPhoneAccountRegistrar;
  131. private final MissedCallNotifier mMissedCallNotifier;
  132. private final ViceNotificationImpl mViceNotificationImpl;
  133. private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
  134. private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
  135. /* Handler tied to thread in which CallManager was initialized. */
  136. private final Handler mHandler = new Handler(Looper.getMainLooper());
  137. private boolean mCanAddCall = true;
  138. /**
  139. * The call the user is currently interacting with. This is the call that should have audio
  140. * focus and be visible in the in-call UI.
  141. */
  142. private Call mForegroundCall;
  143. private static final int LCH_PLAY_DTMF = 100;
  144. private static final int LCH_STOP_DTMF = 101;
  145. private static final int PHONE_START_DSDA_INCALL_TONE = 102;
  146. private static final int LCH_DTMF_PERIODICITY = 3000;
  147. private static final int LCH_DTMF_PERIOD = 500;
  148. private static final String sSupervisoryCallHoldToneConfig =
  149. SystemProperties.get("persist.radio.sch_tone", "none");
  150. private static InCallTonePlayer.Factory mPlayerFactory;
  151. private String mLchSub = null;
  152. private InCallTonePlayer mLocalCallReminderTonePlayer = null;
  153. private String mSubInConversation = null;
  154. private Runnable mStopTone;
  155. /**
  156. * Initializes the required Telecom components.
  157. */
  158. CallsManager(
  159. Context context,
  160. TelecomSystem.SyncRoot lock,
  161. ContactsAsyncHelper contactsAsyncHelper,
  162. CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
  163. MissedCallNotifier missedCallNotifier,
  164. PhoneAccountRegistrar phoneAccountRegistrar,
  165. HeadsetMediaButtonFactory headsetMediaButtonFactory,
  166. ProximitySensorManagerFactory proximitySensorManagerFactory,
  167. InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
  168. ViceNotifier viceNotifier) {
  169. mContext = context;
  170. mLock = lock;
  171. mContactsAsyncHelper = contactsAsyncHelper;
  172. mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
  173. mPhoneAccountRegistrar = phoneAccountRegistrar;
  174. mMissedCallNotifier = missedCallNotifier;
  175. StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
  176. mWiredHeadsetManager = new WiredHeadsetManager(context);
  177. mDockManager = new DockManager(context);
  178. mCallAudioManager = new CallAudioManager(
  179. context, mLock, statusBarNotifier, mWiredHeadsetManager, mDockManager, this);
  180. InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager, lock);
  181. mPlayerFactory = playerFactory;
  182. mRinger = new Ringer(mCallAudioManager, this, playerFactory, context);
  183. mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
  184. mTtyManager = new TtyManager(context, mWiredHeadsetManager);
  185. mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
  186. mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
  187. mCallLogManager = new CallLogManager(context);
  188. mInCallController = new InCallController(context, mLock, this);
  189. mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context);
  190. mConnectionServiceRepository =
  191. new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
  192. mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
  193. mViceNotificationImpl = viceNotifier.create(mContext, this);
  194. mListeners.add(statusBarNotifier);
  195. mListeners.add(mCallLogManager);
  196. mListeners.add(mPhoneStateBroadcaster);
  197. mListeners.add(mInCallController);
  198. mListeners.add(mRinger);
  199. mListeners.add(new RingbackPlayer(this, playerFactory));
  200. mListeners.add(new InCallToneMonitor(playerFactory, this));
  201. mListeners.add(mCallAudioManager);
  202. mListeners.add(missedCallNotifier);
  203. mListeners.add(mDtmfLocalTonePlayer);
  204. mListeners.add(mHeadsetMediaButton);
  205. mListeners.add(mProximitySensorManager);
  206. mListeners.add(mViceNotificationImpl);
  207. mMissedCallNotifier.updateOnStartup(
  208. mLock, this, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory);
  209. }
  210. ViceNotificationImpl getViceNotificationImpl() {
  211. return mViceNotificationImpl;
  212. }
  213. public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) {
  214. if (mRespondViaSmsManager != null) {
  215. mListeners.remove(mRespondViaSmsManager);
  216. }
  217. mRespondViaSmsManager = respondViaSmsManager;
  218. mListeners.add(respondViaSmsManager);
  219. }
  220. public RespondViaSmsManager getRespondViaSmsManager() {
  221. return mRespondViaSmsManager;
  222. }
  223. @Override
  224. public void onSuccessfulOutgoingCall(Call call, int callState) {
  225. Log.v(this, "onSuccessfulOutgoingCall, %s", call);
  226. setCallState(call, callState, "successful outgoing call");
  227. if (!mCalls.contains(call)) {
  228. // Call was not added previously in startOutgoingCall due to it being a potential MMI
  229. // code, so add it now.
  230. addCall(call);
  231. }
  232. // The call's ConnectionService has been updated.
  233. for (CallsManagerListener listener : mListeners) {
  234. listener.onConnectionServiceChanged(call, null, call.getConnectionService());
  235. }
  236. markCallAsDialing(call);
  237. }
  238. @Override
  239. public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
  240. Log.v(this, "onFailedOutgoingCall, call: %s", call);
  241. markCallAsRemoved(call);
  242. }
  243. @Override
  244. public void onSuccessfulIncomingCall(Call incomingCall) {
  245. Log.d(this, "onSuccessfulIncomingCall");
  246. setCallState(incomingCall, CallState.RINGING, "successful incoming call");
  247. if (hasMaximumRingingCalls(incomingCall.getTargetPhoneAccount().getId()) || hasMaximumDialingCalls() ) {
  248. incomingCall.reject(false, null);
  249. // since the call was not added to the list of calls, we have to call the missed
  250. // call notifier and the call logger manually.
  251. mMissedCallNotifier.showMissedCallNotification(incomingCall);
  252. mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
  253. } else {
  254. if (TelephonyManager.getDefault().getMultiSimConfiguration()
  255. == TelephonyManager.MultiSimVariants.DSDA) {
  256. incomingCall.mIsActiveSub = true;
  257. }
  258. addCall(incomingCall);
  259. setActiveSubscription(incomingCall.getTargetPhoneAccount().getId());
  260. }
  261. }
  262. @Override
  263. public void onFailedIncomingCall(Call call) {
  264. setCallState(call, CallState.DISCONNECTED, "failed incoming call");
  265. call.removeListener(this);
  266. }
  267. @Override
  268. public void onSuccessfulUnknownCall(Call call, int callState) {
  269. setCallState(call, callState, "successful unknown call");
  270. Log.i(this, "onSuccessfulUnknownCall for call %s", call);
  271. addCall(call);
  272. }
  273. @Override
  274. public void onFailedUnknownCall(Call call) {
  275. Log.i(this, "onFailedUnknownCall for call %s", call);
  276. setCallState(call, CallState.DISCONNECTED, "failed unknown call");
  277. call.removeListener(this);
  278. }
  279. @Override
  280. public void onRingbackRequested(Call call, boolean ringback) {
  281. for (CallsManagerListener listener : mListeners) {
  282. listener.onRingbackRequested(call, ringback);
  283. }
  284. }
  285. @Override
  286. public void onPostDialWait(Call call, String remaining) {
  287. mInCallController.onPostDialWait(call, remaining);
  288. }
  289. @Override
  290. public void onPostDialChar(final Call call, char nextChar) {
  291. if (PhoneNumberUtils.is12Key(nextChar)) {
  292. // Play tone if it is one of the dialpad digits, canceling out the previously queued
  293. // up stopTone runnable since playing a new tone automatically stops the previous tone.
  294. if (mStopTone != null) {
  295. mHandler.removeCallbacks(mStopTone);
  296. }
  297. mDtmfLocalTonePlayer.playTone(call, nextChar);
  298. // TODO: Create a LockedRunnable class that does the synchronization automatically.
  299. mStopTone = new Runnable() {
  300. @Override
  301. public void run() {
  302. synchronized (mLock) {
  303. // Set a timeout to stop the tone in case there isn't another tone to follow.
  304. mDtmfLocalTonePlayer.stopTone(call);
  305. }
  306. }
  307. };
  308. mHandler.postDelayed(
  309. mStopTone,
  310. Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
  311. } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
  312. nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
  313. // Stop the tone if a tone is playing, removing any other stopTone callbacks since
  314. // the previous tone is being stopped anyway.
  315. if (mStopTone != null) {
  316. mHandler.removeCallbacks(mStopTone);
  317. }
  318. mDtmfLocalTonePlayer.stopTone(call);
  319. } else {
  320. Log.w(this, "onPostDialChar: invalid value %d", nextChar);
  321. }
  322. }
  323. @Override
  324. public void onParentChanged(Call call) {
  325. // parent-child relationship affects which call should be foreground, so do an update.
  326. updateCallsManagerState();
  327. for (CallsManagerListener listener : mListeners) {
  328. listener.onIsConferencedChanged(call);
  329. }
  330. }
  331. @Override
  332. public void onChildrenChanged(Call call) {
  333. // parent-child relationship affects which call should be foreground, so do an update.
  334. updateCallsManagerState();
  335. for (CallsManagerListener listener : mListeners) {
  336. listener.onIsConferencedChanged(call);
  337. }
  338. }
  339. @Override
  340. public void onIsVoipAudioModeChanged(Call call) {
  341. for (CallsManagerListener listener : mListeners) {
  342. listener.onIsVoipAudioModeChanged(call);
  343. }
  344. }
  345. @Override
  346. public void onVideoStateChanged(Call call) {
  347. for (CallsManagerListener listener : mListeners) {
  348. listener.onVideoStateChanged(call);
  349. }
  350. }
  351. @Override
  352. public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) {
  353. mPendingCallsToDisconnect.add(call);
  354. mHandler.postDelayed(new Runnable() {
  355. @Override
  356. public void run() {
  357. synchronized (mLock) {
  358. if (mPendingCallsToDisconnect.remove(call)) {
  359. Log.i(this, "Delayed disconnection of call: %s", call);
  360. call.disconnect();
  361. }
  362. }
  363. }
  364. }, Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver()));
  365. return true;
  366. }
  367. /**
  368. * Handles changes to the {@link Connection.VideoProvider} for a call. Adds the
  369. * {@link CallsManager} as a listener for the {@link VideoProviderProxy} which is created
  370. * in {@link Call#setVideoProvider(IVideoProvider)}. This allows the {@link CallsManager} to
  371. * respond to callbacks from the {@link VideoProviderProxy}.
  372. *
  373. * @param call The call.
  374. */
  375. @Override
  376. public void onVideoCallProviderChanged(Call call) {
  377. VideoProviderProxy videoProviderProxy = call.getVideoProviderProxy();
  378. if (videoProviderProxy == null) {
  379. return;
  380. }
  381. videoProviderProxy.addListener(this);
  382. }
  383. /**
  384. * Handles session modification requests received via the {@link TelecomVideoCallCallback} for
  385. * a call. Notifies listeners of the {@link CallsManager.CallsManagerListener} of the session
  386. * modification request.
  387. *
  388. * @param call The call.
  389. * @param videoProfile The {@link VideoProfile}.
  390. */
  391. @Override
  392. public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
  393. int videoState = videoProfile != null ? videoProfile.getVideoState() :
  394. VideoProfile.STATE_AUDIO_ONLY;
  395. Log.v(TAG, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile
  396. .videoStateToString(videoState));
  397. for (CallsManagerListener listener : mListeners) {
  398. listener.onSessionModifyRequestReceived(call, videoProfile);
  399. }
  400. }
  401. Collection<Call> getCalls() {
  402. return Collections.unmodifiableCollection(mCalls);
  403. }
  404. Call getForegroundCall() {
  405. return mForegroundCall;
  406. }
  407. Ringer getRinger() {
  408. return mRinger;
  409. }
  410. InCallController getInCallController() {
  411. return mInCallController;
  412. }
  413. boolean hasEmergencyCall() {
  414. for (Call call : mCalls) {
  415. if (call.isEmergencyCall()) {
  416. return true;
  417. }
  418. }
  419. return false;
  420. }
  421. boolean hasOnlyDisconnectedCalls() {
  422. for (Call call : mCalls) {
  423. if (!call.isDisconnected()) {
  424. return false;
  425. }
  426. }
  427. return true;
  428. }
  429. boolean hasVideoCall() {
  430. for (Call call : mCalls) {
  431. if (VideoProfile.isVideo(call.getVideoState())) {
  432. return true;
  433. }
  434. }
  435. return false;
  436. }
  437. CallAudioState getAudioState() {
  438. return mCallAudioManager.getCallAudioState();
  439. }
  440. boolean isTtySupported() {
  441. return mTtyManager.isTtySupported();
  442. }
  443. int getCurrentTtyMode() {
  444. return mTtyManager.getCurrentTtyMode();
  445. }
  446. void addListener(CallsManagerListener listener) {
  447. mListeners.add(listener);
  448. }
  449. void removeListener(CallsManagerListener listener) {
  450. mListeners.remove(listener);
  451. }
  452. /**
  453. * Starts the process to attach the call to a connection service.
  454. *
  455. * @param phoneAccountHandle The phone account which contains the component name of the
  456. * connection service to use for this call.
  457. * @param extras The optional extras Bundle passed with the intent used for the incoming call.
  458. */
  459. void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
  460. Log.d(this, "processIncomingCallIntent");
  461. Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
  462. if (handle == null) {
  463. // Required for backwards compatibility
  464. handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
  465. }
  466. Call call = new Call(
  467. mContext,
  468. this,
  469. mLock,
  470. mConnectionServiceRepository,
  471. mContactsAsyncHelper,
  472. mCallerInfoAsyncQueryFactory,
  473. handle,
  474. null /* gatewayInfo */,
  475. null /* connectionManagerPhoneAccount */,
  476. phoneAccountHandle,
  477. true /* isIncoming */,
  478. false /* isConference */);
  479. call.setIntentExtras(extras);
  480. // TODO: Move this to be a part of addCall()
  481. call.addListener(this);
  482. call.startCreateConnection(mPhoneAccountRegistrar);
  483. }
  484. void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
  485. Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
  486. Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
  487. Call call = new Call(
  488. mContext,
  489. this,
  490. mLock,
  491. mConnectionServiceRepository,
  492. mContactsAsyncHelper,
  493. mCallerInfoAsyncQueryFactory,
  494. handle,
  495. null /* gatewayInfo */,
  496. null /* connectionManagerPhoneAccount */,
  497. phoneAccountHandle,
  498. // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
  499. // to the existing connection instead of trying to create a new one.
  500. true /* isIncoming */,
  501. false /* isConference */);
  502. call.setIsUnknown(true);
  503. call.setIntentExtras(extras);
  504. call.addListener(this);
  505. call.startCreateConnection(mPhoneAccountRegistrar);
  506. }
  507. private boolean areHandlesEqual(Uri handle1, Uri handle2) {
  508. if (handle1 == null || handle2 == null) {
  509. return handle1 == handle2;
  510. }
  511. if (!TextUtils.equals(handle1.getScheme(), handle2.getScheme())) {
  512. return false;
  513. }
  514. final String number1 = PhoneNumberUtils.normalizeNumber(handle1.getSchemeSpecificPart());
  515. final String number2 = PhoneNumberUtils.normalizeNumber(handle2.getSchemeSpecificPart());
  516. return TextUtils.equals(number1, number2);
  517. }
  518. private Call getNewOutgoingCall(Uri handle) {
  519. // First check to see if we can reuse any of the calls that are waiting to disconnect.
  520. // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
  521. Call reusedCall = null;
  522. for (Call pendingCall : mPendingCallsToDisconnect) {
  523. if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) {
  524. mPendingCallsToDisconnect.remove(pendingCall);
  525. Log.i(this, "Reusing disconnected call %s", pendingCall);
  526. reusedCall = pendingCall;
  527. } else {
  528. Log.i(this, "Not reusing disconnected call %s", pendingCall);
  529. pendingCall.disconnect();
  530. }
  531. }
  532. if (reusedCall != null) {
  533. return reusedCall;
  534. }
  535. // Create a call with original handle. The handle may be changed when the call is attached
  536. // to a connection service, but in most cases will remain the same.
  537. return new Call(
  538. mContext,
  539. this,
  540. mLock,
  541. mConnectionServiceRepository,
  542. mContactsAsyncHelper,
  543. mCallerInfoAsyncQueryFactory,
  544. handle,
  545. null /* gatewayInfo */,
  546. null /* connectionManagerPhoneAccount */,
  547. null /* phoneAccountHandle */,
  548. false /* isIncoming */,
  549. false /* isConference */);
  550. }
  551. /**
  552. * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
  553. *
  554. * @param handle Handle to connect the call with.
  555. * @param phoneAccountHandle The phone account which contains the component name of the
  556. * connection service to use for this call.
  557. * @param extras The optional extras Bundle passed with the intent used for the incoming call.
  558. */
  559. Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
  560. Call call = getNewOutgoingCall(handle);
  561. if (extras!=null) {
  562. call.setVideoState(extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
  563. VideoProfile.STATE_AUDIO_ONLY));
  564. }
  565. boolean isAddParticipant = ((extras != null) && (extras.getBoolean(
  566. TelephonyProperties.ADD_PARTICIPANT_KEY, false)));
  567. boolean isSkipSchemaOrConfUri = ((extras != null) && (extras.getBoolean(
  568. TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, false) ||
  569. extras.getBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false)));
  570. if (isAddParticipant) {
  571. String number = handle.getSchemeSpecificPart();
  572. if (!isSkipSchemaOrConfUri) {
  573. number = PhoneNumberUtils.stripSeparators(number);
  574. }
  575. addParticipant(number);
  576. mInCallController.bringToForeground(false);
  577. return null;
  578. }
  579. // Force tel scheme for ims conf uri/skip schema calls to avoid selection of sip accounts
  580. String scheme = (isSkipSchemaOrConfUri? PhoneAccount.SCHEME_TEL: handle.getScheme());
  581. Log.d(this, "startOutgoingCall :: isAddParticipant=" + isAddParticipant
  582. + " isSkipSchemaOrConfUri=" + isSkipSchemaOrConfUri + " scheme=" + scheme);
  583. List<PhoneAccountHandle> accounts =
  584. mPhoneAccountRegistrar.getCallCapablePhoneAccounts(scheme, false);
  585. Log.v(this, "startOutgoingCall found accounts = " + accounts);
  586. if (mForegroundCall != null && TelephonyManager.getDefault().getMultiSimConfiguration()
  587. != TelephonyManager.MultiSimVariants.DSDA) {
  588. Call ongoingCall = mForegroundCall;
  589. // If there is an ongoing call, use the same phone account to place this new call.
  590. // If the ongoing call is a conference call, we fetch the phone account from the
  591. // child calls because we don't have targetPhoneAccount set on Conference calls.
  592. // TODO: Set targetPhoneAccount for all conference calls (b/23035408).
  593. if (ongoingCall.getTargetPhoneAccount() == null &&
  594. !ongoingCall.getChildCalls().isEmpty()) {
  595. ongoingCall = ongoingCall.getChildCalls().get(0);
  596. }
  597. if (ongoingCall.getTargetPhoneAccount() != null) {
  598. phoneAccountHandle = ongoingCall.getTargetPhoneAccount();
  599. }
  600. }
  601. // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
  602. // as if a phoneAccount was not specified (does the default behavior instead).
  603. // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
  604. if (phoneAccountHandle != null) {
  605. if (!accounts.contains(phoneAccountHandle)) {
  606. phoneAccountHandle = null;
  607. }
  608. }
  609. if (phoneAccountHandle == null) {
  610. // No preset account, check if default exists that supports the URI scheme for the
  611. // handle.
  612. phoneAccountHandle =
  613. mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(scheme);
  614. }
  615. call.setTargetPhoneAccount(phoneAccountHandle);
  616. boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
  617. // Do not support any more live calls. Our options are to move a call to hold, disconnect
  618. // a call, or cancel this call altogether.
  619. if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, call.isEmergencyCall())) {
  620. // just cancel at this point.
  621. Log.i(this, "No remaining room for outgoing call: %s", call);
  622. if (mCalls.contains(call)) {
  623. // This call can already exist if it is a reused call,
  624. // See {@link #getNewOutgoingCall}.
  625. call.disconnect();
  626. }
  627. return null;
  628. }
  629. boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
  630. !call.isEmergencyCall();
  631. if (needsAccountSelection) {
  632. // This is the state where the user is expected to select an account
  633. call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
  634. // Create our own instance to modify (since extras may be Bundle.EMPTY)
  635. extras = new Bundle(extras);
  636. extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
  637. } else {
  638. call.setState(
  639. CallState.CONNECTING,
  640. phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
  641. }
  642. call.setIntentExtras(extras);
  643. // Do not add the call if it is a potential MMI code.
  644. if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
  645. call.addListener(this);
  646. } else if (!mCalls.contains(call)) {
  647. // We check if mCalls already contains the call because we could potentially be reusing
  648. // a call which was previously added (See {@link #getNewOutgoingCall}).
  649. addCall(call);
  650. }
  651. return call;
  652. }
  653. /**
  654. * Attempts to issue/connect the specified call.
  655. *
  656. * @param handle Handle to connect the call with.
  657. * @param gatewayInfo Optional gateway information that can be used to route the call to the
  658. * actual dialed handle via a gateway provider. May be null.
  659. * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
  660. * @param videoState The desired video state for the outgoing call.
  661. */
  662. void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
  663. int videoState) {
  664. if (call == null) {
  665. // don't do anything if the call no longer exists
  666. Log.i(this, "Canceling unknown call.");
  667. return;
  668. }
  669. final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
  670. if (gatewayInfo == null) {
  671. Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
  672. } else {
  673. Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
  674. Log.pii(uriHandle), Log.pii(handle));
  675. }
  676. call.setHandle(uriHandle);
  677. call.setGatewayInfo(gatewayInfo);
  678. // Auto-enable speakerphone if the originating intent specified to do so, or if the call
  679. // is a video call or if the phone is docked.
  680. call.setStartWithSpeakerphoneOn(speakerphoneOn || isSpeakerphoneAutoEnabled(videoState)
  681. || mDockManager.isDocked());
  682. call.setVideoState(videoState);
  683. if (call.isEmergencyCall()) {
  684. // Emergency -- CreateConnectionProcessor will choose accounts automatically
  685. call.setTargetPhoneAccount(null);
  686. }
  687. if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
  688. if (!call.isEmergencyCall()) {
  689. updateLchStatus(call.getTargetPhoneAccount().getId());
  690. }
  691. // If the account has been set, proceed to place the outgoing call.
  692. // Otherwise the connection will be initiated when the account is set by the user.
  693. call.startCreateConnection(mPhoneAccountRegistrar);
  694. }
  695. }
  696. /**
  697. * Attempts to add participant in a call.
  698. *
  699. * @param number number to connect the call with.
  700. */
  701. private void addParticipant(String number) {
  702. Log.i(this, "addParticipant number ="+number);
  703. if (getForegroundCall() == null) {
  704. // don't do anything if the call no longer exists
  705. Log.i(this, "Canceling unknown call.");
  706. return;
  707. } else {
  708. getForegroundCall().addParticipantWithConference(number);
  709. }
  710. }
  711. /**
  712. * Attempts to start a conference call for the specified call.
  713. *
  714. * @param call The call to conference.
  715. * @param otherCall The other call to conference with.
  716. */
  717. void conference(Call call, Call otherCall) {
  718. call.conferenceWith(otherCall);
  719. }
  720. /**
  721. * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
  722. * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
  723. * the user opting to answer said call.
  724. *
  725. * @param call The call to answer.
  726. * @param videoState The video state in which to answer the call.
  727. */
  728. void answerCall(Call call, int videoState) {
  729. if (!mCalls.contains(call)) {
  730. Log.i(this, "Request to answer a non-existent call %s", call);
  731. } else {
  732. Call activeCall = getFirstCallWithState(call.getTargetPhoneAccount()
  733. .getId(), CallState.ACTIVE, CallState.DIALING);
  734. // If the foreground call is not the ringing call and it is currently isActive() or
  735. // STATE_DIALING, put it on hold before answering the call.
  736. if (activeCall != null && activeCall != call &&
  737. (activeCall.isActive() ||
  738. activeCall.getState() == CallState.DIALING)) {
  739. if (0 == (mForegroundCall.getConnectionCapabilities()
  740. & Connection.CAPABILITY_HOLD)) {
  741. // This call does not support hold. If it is from a different connection
  742. // service, then disconnect it, otherwise allow the connection service to
  743. // figure out the right states.
  744. if (activeCall.getConnectionService() != call.getConnectionService()) {
  745. activeCall.disconnect();
  746. }
  747. } else {
  748. Call heldCall = getHeldCall();
  749. if (heldCall != null) {
  750. Log.v(this, "Disconnecting held call %s before holding active call.",
  751. heldCall);
  752. heldCall.disconnect();
  753. }
  754. Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
  755. mForegroundCall, call);
  756. activeCall.hold();
  757. }
  758. // TODO: Wait until we get confirmation of the active call being
  759. // on-hold before answering the new call.
  760. // TODO: Import logic from CallManager.acceptCall()
  761. }
  762. for (CallsManagerListener listener : mListeners) {
  763. listener.onIncomingCallAnswered(call);
  764. }
  765. updateLchStatus(call.getTargetPhoneAccount().getId());
  766. // We do not update the UI until we get confirmation of the answer() through
  767. // {@link #markCallAsActive}.
  768. call.answer(videoState);
  769. if (isSpeakerphoneAutoEnabled(videoState)) {
  770. call.setStartWithSpeakerphoneOn(true);
  771. }
  772. }
  773. }
  774. /**
  775. * Determines if the speakerphone should be automatically enabled for the call. Speakerphone
  776. * should be enabled if the call is a video call and bluetooth or the wired headset are not in
  777. * use.
  778. *
  779. * @param videoState The video state of the call.
  780. * @return {@code true} if the speakerphone should be enabled.
  781. */
  782. private boolean isSpeakerphoneAutoEnabled(int videoState) {
  783. return VideoProfile.isVideo(videoState) &&
  784. !mWiredHeadsetManager.isPluggedIn() &&
  785. !mCallAudioManager.isBluetoothDeviceAvailable() &&
  786. isSpeakerEnabledForVideoCalls();
  787. }
  788. /**
  789. * Determines if the speakerphone should be automatically enabled for video calls.
  790. *
  791. * @return {@code true} if the speakerphone should automatically be enabled.
  792. */
  793. private static boolean isSpeakerEnabledForVideoCalls() {
  794. return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT,
  795. PhoneConstants.AUDIO_OUTPUT_DEFAULT) ==
  796. PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER);
  797. }
  798. /**
  799. * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
  800. * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
  801. * the user opting to reject said call.
  802. */
  803. void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
  804. if (!mCalls.contains(call)) {
  805. Log.i(this, "Request to reject a non-existent call %s", call);
  806. } else {
  807. for (CallsManagerListener listener : mListeners) {
  808. listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
  809. }
  810. setActiveSubscription(getConversationSub());
  811. call.reject(rejectWithMessage, textMessage);
  812. }
  813. }
  814. /**
  815. * Instructs Telecom to play the specified DTMF tone within the specified call.
  816. *
  817. * @param digit The DTMF digit to play.
  818. */
  819. void playDtmfTone(Call call, char digit) {
  820. if (!mCalls.contains(call)) {
  821. Log.i(this, "Request to play DTMF in a non-existent call %s", call);
  822. } else {
  823. call.playDtmfTone(digit);
  824. mDtmfLocalTonePlayer.playTone(call, digit);
  825. }
  826. }
  827. /**
  828. * Instructs Telecom to stop the currently playing DTMF tone, if any.
  829. */
  830. void stopDtmfTone(Call call) {
  831. if (!mCalls.contains(call)) {
  832. Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
  833. } else {
  834. call.stopDtmfTone();
  835. mDtmfLocalTonePlayer.stopTone(call);
  836. }
  837. }
  838. /**
  839. * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
  840. */
  841. void postDialContinue(Call call, boolean proceed) {
  842. if (!mCalls.contains(call)) {
  843. Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
  844. } else {
  845. call.postDialContinue(proceed);
  846. }
  847. }
  848. /**
  849. * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
  850. * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
  851. * the user hitting the end-call button.
  852. */
  853. void disconnectCall(Call call) {
  854. Log.v(this, "disconnectCall %s", call);
  855. if (!mCalls.contains(call)) {
  856. Log.w(this, "Unknown call (%s) asked to disconnect", call);
  857. } else {
  858. mLocallyDisconnectingCalls.add(call);
  859. call.disconnect();
  860. }
  861. }
  862. /**
  863. * Instructs Telecom to disconnect all calls.
  864. */
  865. void disconnectAllCalls() {
  866. Log.v(this, "disconnectAllCalls");
  867. for (Call call : mCalls) {
  868. disconnectCall(call);
  869. }
  870. }
  871. /**
  872. * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
  873. * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
  874. * the user hitting the hold button during an active call.
  875. */
  876. void holdCall(Call call) {
  877. if (!mCalls.contains(call)) {
  878. Log.w(this, "Unknown call (%s) asked to be put on hold", call);
  879. } else {
  880. Log.d(this, "Putting call on hold: (%s)", call);
  881. call.hold();
  882. }
  883. }
  884. /**
  885. * Instructs Telecom to release the specified call from hold. Intended to be invoked by
  886. * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
  887. * by the user hitting the hold button during a held call.
  888. */
  889. void unholdCall(Call call) {
  890. if (!mCalls.contains(call)) {
  891. Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
  892. } else {
  893. Log.d(this, "unholding call: (%s)", call);
  894. for (Call c : mCalls) {
  895. PhoneAccountHandle ph = call.getTargetPhoneAccount();
  896. PhoneAccountHandle ph1 = c.getTargetPhoneAccount();
  897. // Only attempt to hold parent calls and not the individual children.
  898. // if 'c' is not for same subscription as call, then don't disturb 'c'
  899. if (c != null && c.isAlive() && c != call && c.getParentCall() == null
  900. && (ph != null && ph1 != null &&
  901. isSamePhAccIdOrSipId(ph.getId(), ph1.getId()))) {
  902. c.hold();
  903. }
  904. }
  905. call.unhold();
  906. }
  907. }
  908. /**
  909. * Returns true if the ids are same or one of the ids is sip id.
  910. */
  911. private boolean isSamePhAccIdOrSipId(String id1, String id2) {
  912. boolean ret = ((id1 != null && id2 != null) &&
  913. (id1.equals(id2) || id1.contains("sip") || id2.contains("sip")));
  914. Log.d(this, "isSamePhAccIdOrSipId: id1 = " + id1 + " id2 = " + id2 + " ret = " + ret);
  915. return ret;
  916. }
  917. /** Called by the in-call UI to change the mute state. */
  918. void mute(boolean shouldMute) {
  919. mCallAudioManager.mute(shouldMute);
  920. }
  921. /**
  922. * Called by the in-call UI to change the audio route, for example to change from earpiece to
  923. * speaker phone.
  924. */
  925. void setAudioRoute(int route) {
  926. Log.d(this, "Audio routed by user");
  927. mCallAudioManager.mUserSetAudioRoute = true;
  928. mCallAudioManager.setAudioRoute(route);
  929. mCallAudioManager.mUserSetAudioRoute = false;
  930. }
  931. /** Called by the in-call UI to turn the proximity sensor on. */
  932. void turnOnProximitySensor() {
  933. mProximitySensorManager.turnOn();
  934. }
  935. /**
  936. * Called by the in-call UI to turn the proximity sensor off.
  937. * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
  938. * the screen will be kept off until the proximity sensor goes negative.
  939. */
  940. void turnOffProximitySensor(boolean screenOnImmediately) {
  941. mProximitySensorManager.turnOff(screenOnImmediately);
  942. }
  943. void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
  944. if (!mCalls.contains(call)) {
  945. Log.i(this, "Attempted to add account to unknown call %s", call);
  946. } else {
  947. // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and
  948. // the SELECT_PHONE_ACCOUNT sequence run in parallel, if the user selects an account before the
  949. // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without
  950. // respecting a rewritten number or a canceled number. This is unlikely since
  951. // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting
  952. // a phone account from the in-call UI.
  953. Log.i(this, "phoneAccountSelected , id = %s", account.getId());
  954. updateLchStatus(account.getId());
  955. call.setTargetPhoneAccount(account);
  956. // Note: emergency calls never go through account selection dialog so they never
  957. // arrive here.
  958. if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
  959. call.startCreateConnection(mPhoneAccountRegistrar);
  960. } else {
  961. call.disconnect();
  962. }
  963. if (setDefault) {
  964. mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(account);
  965. }
  966. }
  967. }
  968. /** Called when the audio state changes. */
  969. void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState) {
  970. Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
  971. for (CallsManagerListener listener : mListeners) {
  972. listener.onCallAudioStateChanged(oldAudioState, newAudioState);
  973. }
  974. }
  975. void markCallAsRinging(Call call) {
  976. setCallState(call, CallState.RINGING, "ringing set explicitly");
  977. }
  978. void markCallAsDialing(Call call) {
  979. setCallState(call, CallState.DIALING, "dialing set explicitly");
  980. maybeMoveToEarpiece(call);
  981. maybeMoveToSpeakerPhone(call);
  982. setActiveSubscription(call.getTargetPhoneAccount().getId());
  983. }
  984. void markCallAsActive(Call call) {
  985. setCallState(call, CallState.ACTIVE, "active set explicitly");
  986. maybeMoveToSpeakerPhone(call);
  987. }
  988. void markCallAsOnHold(Call call) {
  989. setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
  990. }
  991. /**
  992. * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
  993. * last live call, then also disconnect from the in-call controller.
  994. *
  995. * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}.
  996. */
  997. void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
  998. // Show MT call in call log as a missed call if immediately disconnected
  999. // before creating a connection.
  1000. if (!mCalls.contains(call) && (DisconnectCause.MISSED == disconnectCause.getCode())) {
  1001. addCall(call);
  1002. mMissedCallNotifier.showMissedCallNotification(call);
  1003. }
  1004. call.setDisconnectCause(disconnectCause);
  1005. int prevState = call.getState();
  1006. setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly");
  1007. String activeSub = getActiveSubscription();
  1008. String conversationSub = getConversationSub();
  1009. String lchSub = IsAnySubInLch();
  1010. PhoneAccount phAcc =
  1011. getPhoneAccountRegistrar().getPhoneAccount(call.getTargetPhoneAccount());
  1012. if (activeSub != null && (call.getTargetPhoneAccount() != null &&
  1013. call.getTargetPhoneAccount().getId().equals(activeSub)) &&
  1014. (phAcc != null) && (phAcc.isSet(PhoneAccount.LCH)) &&
  1015. (conversationSub != null) &&
  1016. (!conversationSub.equals(activeSub))) {
  1017. Log.d(this,"Set active sub to conversation sub");
  1018. setActiveSubscription(conversationSub);
  1019. } else if ((conversationSub == null) && (lchSub != null) &&
  1020. ((prevState == CallState.CONNECTING) || (prevState ==
  1021. CallState.SELECT_PHONE_ACCOUNT)) &&
  1022. (call.getState() == CallState.DISCONNECTED)) {
  1023. Log.d(this,"remove sub with call from LCH");
  1024. updateLchStatus(lchSub);
  1025. setActiveSubscription(lchSub);
  1026. manageDsdaInCallTones(false);
  1027. }
  1028. if ((call.getTargetPhoneAccount() != null) && (phAcc != null) &&
  1029. (phAcc.isSet(PhoneAccount.LCH))) {
  1030. Call activecall = getFirstCallWithState(call.getTargetPhoneAccount().getId(),
  1031. CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
  1032. Log.d(this,"activecall: " + activecall);
  1033. if (activecall == null) {
  1034. phAcc.unSetBit(PhoneAccount.LCH);
  1035. manageDsdaInCallTones(false);
  1036. }
  1037. }
  1038. }
  1039. private String IsAnySubInLch() {
  1040. for (PhoneAccountHandle ph : getPhoneAccountRegistrar().getSimPhoneAccounts()) {
  1041. PhoneAccount phAcc = getPhoneAccountRegistrar().getPhoneAccount(ph);
  1042. if ((phAcc != null) && (phAcc.isSet(PhoneAccount.LCH))) {
  1043. Log.d(this, "Sub in LCH: " + ph.getId());
  1044. return ph.getId();
  1045. }
  1046. }
  1047. return null;
  1048. }
  1049. /**
  1050. * Removes an existing disconnected call, and notifies the in-call app.
  1051. */
  1052. void markCallAsRemoved(Call call) {
  1053. removeCall(call);
  1054. if (!hasAnyCalls()) {
  1055. updateLchStatus(null);
  1056. setActiveSubscription(null);
  1057. manageDsdaInCallTones(false);
  1058. }
  1059. if (mLocallyDisconnectingCalls.contains(call)) {
  1060. mLocallyDisconnectingCalls.remove(call);
  1061. if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) {
  1062. mForegroundCall.unhold();
  1063. }
  1064. }
  1065. }
  1066. /**
  1067. * Cleans up any calls currently associated with the specified connection service when the
  1068. * service binder disconnects unexpectedly.
  1069. *
  1070. * @param service The connection service that disconnected.
  1071. */
  1072. void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
  1073. if (service != null) {
  1074. for (Call call : mCalls) {
  1075. if (call.getConnectionService() == service) {
  1076. if (call.getState() != CallState.DISCONNECTED) {
  1077. markCallAsDisconnected(call, new DisconnectCause(D