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 17 package com.android.phone; 18 19 import android.bluetooth.IBluetoothHeadsetPhone; 20 import android.content.Context; 21 import android.os.RemoteException; 22 import android.os.SystemProperties; 23 import android.util.Log; 24 25 import com.android.internal.telephony.CallManager; 26 import com.android.internal.telephony.PhoneConstants; 27 import com.android.phone.CallModeler.CallResult; 28 import com.android.phone.NotificationMgr.StatusBarHelper; 29 import com.android.services.telephony.common.Call; 30 import com.android.services.telephony.common.ICallCommandService; 31 32 /** 33 * Service interface used by in-call ui to control phone calls using commands exposed as methods. 34 * Instances of this class are handed to in-call UI via CallMonitorService. 35 */ 36 class CallCommandService extends ICallCommandService.Stub { 37 private static final String TAG = CallCommandService.class.getSimpleName(); 38 private static final boolean DBG = 39 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 40 41 private final Context mContext; 42 private final CallManager mCallManager; 43 private final CallModeler mCallModeler; 44 private final DTMFTonePlayer mDtmfTonePlayer; 45 private final AudioRouter mAudioRouter; 46 47 public CallCommandService(Context context, CallManager callManager, CallModeler callModeler, 48 DTMFTonePlayer dtmfTonePlayer, AudioRouter audioRouter) { 49 mContext = context; 50 mCallManager = callManager; 51 mCallModeler = callModeler; 52 mDtmfTonePlayer = dtmfTonePlayer; 53 mAudioRouter = audioRouter; 54 } 55 56 /** 57 * TODO: Add a confirmation callback parameter. 58 */ 59 @Override 60 public void answerCall(int callId) { 61 try { 62 CallResult result = mCallModeler.getCallWithId(callId); 63 if (result != null) { 64 PhoneUtils.answerCall(result.getConnection().getCall()); 65 } 66 } catch (Exception e) { 67 Log.e(TAG, "Error during answerCall().", e); 68 } 69 } 70 71 /** 72 * TODO: Add a confirmation callback parameter. 73 */ 74 @Override 75 public void rejectCall(Call call, boolean rejectWithMessage, String message) { 76 try { 77 int callId = Call.INVALID_CALL_ID; 78 String phoneNumber = ""; 79 if (call != null) { 80 callId = call.getCallId(); 81 phoneNumber = call.getNumber(); 82 } 83 CallResult result = mCallModeler.getCallWithId(callId); 84 85 if (result != null) { 86 phoneNumber = result.getConnection().getAddress(); 87 88 Log.v(TAG, "Hanging up"); 89 PhoneUtils.hangupRingingCall(result.getConnection().getCall()); 90 } 91 92 if (rejectWithMessage && !phoneNumber.isEmpty()) { 93 RejectWithTextMessageManager.rejectCallWithMessage(phoneNumber, message); 94 } 95 } catch (Exception e) { 96 Log.e(TAG, "Error during rejectCall().", e); 97 } 98 } 99 100 @Override 101 public void disconnectCall(int callId) { 102 try { 103 CallResult result = mCallModeler.getCallWithId(callId); 104 if (DBG) Log.d(TAG, "disconnectCall " + result.getCall()); 105 106 if (result != null) { 107 int state = result.getCall().getState(); 108 if (Call.State.ACTIVE == state || 109 Call.State.ONHOLD == state || 110 Call.State.DIALING == state) { 111 result.getConnection().getCall().hangup(); 112 } else if (Call.State.CONFERENCED == state) { 113 result.getConnection().hangup(); 114 } 115 } 116 } catch (Exception e) { 117 Log.e(TAG, "Error during disconnectCall().", e); 118 } 119 } 120 121 @Override 122 public void separateCall(int callId) { 123 try { 124 CallResult result = mCallModeler.getCallWithId(callId); 125 if (DBG) Log.d(TAG, "disconnectCall " + result.getCall()); 126 127 if (result != null) { 128 int state = result.getCall().getState(); 129 if (Call.State.CONFERENCED == state) { 130 result.getConnection().separate(); 131 } 132 } 133 } catch (Exception e) { 134 Log.e(TAG, "Error trying to separate call.", e); 135 } 136 } 137 138 @Override 139 public void hold(int callId, boolean hold) { 140 try { 141 CallResult result = mCallModeler.getCallWithId(callId); 142 if (result != null) { 143 int state = result.getCall().getState(); 144 if (hold && Call.State.ACTIVE == state) { 145 PhoneUtils.switchHoldingAndActive(mCallManager.getFirstActiveBgCall()); 146 } else if (!hold && Call.State.ONHOLD == state) { 147 PhoneUtils.switchHoldingAndActive(result.getConnection().getCall()); 148 } 149 } 150 } catch (Exception e) { 151 Log.e(TAG, "Error trying to place call on hold.", e); 152 } 153 } 154 155 @Override 156 public void merge() { 157 if (PhoneUtils.okToMergeCalls(mCallManager)) { 158 PhoneUtils.mergeCalls(mCallManager); 159 } 160 } 161 162 @Override 163 public void addCall() { 164 // start new call checks okToAddCall() already 165 PhoneUtils.startNewCall(mCallManager); 166 } 167 168 169 @Override 170 public void swap() { 171 if (!PhoneUtils.okToSwapCalls(mCallManager)) { 172 // TODO: throw an error instead? 173 return; 174 } 175 176 // Swap the fg and bg calls. 177 // In the future we may provides some way for user to choose among 178 // multiple background calls, for now, always act on the first background calll. 179 PhoneUtils.switchHoldingAndActive(mCallManager.getFirstActiveBgCall()); 180 181 final PhoneGlobals mApp = PhoneGlobals.getInstance(); 182 183 // If we have a valid BluetoothPhoneService then since CDMA network or 184 // Telephony FW does not send us information on which caller got swapped 185 // we need to update the second call active state in BluetoothPhoneService internally 186 if (mCallManager.getBgPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 187 final IBluetoothHeadsetPhone btPhone = mApp.getBluetoothPhoneService(); 188 if (btPhone != null) { 189 try { 190 btPhone.cdmaSwapSecondCallState(); 191 } catch (RemoteException e) { 192 Log.e(TAG, Log.getStackTraceString(new Throwable())); 193 } 194 } 195 } 196 } 197 198 @Override 199 public void mute(boolean onOff) { 200 try { 201 PhoneUtils.setMute(onOff); 202 } catch (Exception e) { 203 Log.e(TAG, "Error during mute().", e); 204 } 205 } 206 207 @Override 208 public void speaker(boolean onOff) { 209 try { 210 PhoneUtils.turnOnSpeaker(mContext, onOff, true); 211 } catch (Exception e) { 212 Log.e(TAG, "Error during speaker().", e); 213 } 214 } 215 216 @Override 217 public void playDtmfTone(char digit, boolean timedShortTone) { 218 try { 219 mDtmfTonePlayer.playDtmfTone(digit, timedShortTone); 220 } catch (Exception e) { 221 Log.e(TAG, "Error playing DTMF tone.", e); 222 } 223 } 224 225 @Override 226 public void stopDtmfTone() { 227 try { 228 mDtmfTonePlayer.stopDtmfTone(); 229 } catch (Exception e) { 230 Log.e(TAG, "Error stopping DTMF tone.", e); 231 } 232 } 233 234 @Override 235 public void setAudioMode(int mode) { 236 try { 237 mAudioRouter.setAudioMode(mode); 238 } catch (Exception e) { 239 Log.e(TAG, "Error setting the audio mode.", e); 240 } 241 } 242 243 @Override 244 public void postDialCancel(int callId) throws RemoteException { 245 final CallResult result = mCallModeler.getCallWithId(callId); 246 if (result != null) { 247 result.getConnection().cancelPostDial(); 248 } 249 } 250 251 @Override 252 public void postDialWaitContinue(int callId) throws RemoteException { 253 final CallResult result = mCallModeler.getCallWithId(callId); 254 if (result != null) { 255 result.getConnection().proceedAfterWaitChar(); 256 } 257 } 258 259 @Override 260 public void setSystemBarNavigationEnabled(boolean enable) { 261 try { 262 final StatusBarHelper statusBarHelper = PhoneGlobals.getInstance().notificationMgr. 263 statusBarHelper; 264 statusBarHelper.enableSystemBarNavigation(enable); 265 statusBarHelper.enableExpandedView(enable); 266 } catch (Exception e) { 267 Log.e(TAG, "Error enabling or disabling system bar navigation", e); 268 } 269 } 270 271 } 272