HfpClientConnection.java revision 05462fe44531f887fe91ac173ec645e91b236025
1/* 2 * Copyright (C) 2016 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 */ 16package com.android.bluetooth.hfpclient.connserv; 17 18import android.bluetooth.BluetoothDevice; 19import android.bluetooth.BluetoothHeadsetClient; 20import android.bluetooth.BluetoothHeadsetClientCall; 21import android.bluetooth.BluetoothProfile; 22import android.content.Context; 23import android.net.Uri; 24import android.telecom.Connection; 25import android.telecom.DisconnectCause; 26import android.telecom.TelecomManager; 27import android.util.Log; 28 29import java.util.UUID; 30 31public class HfpClientConnection extends Connection { 32 private static final String TAG = "HfpClientConnection"; 33 34 private final Context mContext; 35 private final BluetoothDevice mDevice; 36 37 private BluetoothHeadsetClient mHeadsetProfile; 38 private BluetoothHeadsetClientCall mCurrentCall; 39 private boolean mClosed; 40 private boolean mLocalDisconnect; 41 private boolean mClientHasEcc; 42 private boolean mAdded; 43 44 public HfpClientConnection(Context context, BluetoothDevice device, 45 BluetoothHeadsetClient client, BluetoothHeadsetClientCall call, Uri number) { 46 mDevice = device; 47 mContext = context; 48 mHeadsetProfile = client; 49 50 setInitializing(); 51 52 if (call != null) { 53 mCurrentCall = call; 54 handleCallChanged(); 55 } else if (mHeadsetProfile != null) { 56 mCurrentCall = mHeadsetProfile.dial( 57 mDevice, number.getSchemeSpecificPart()); 58 setDialing(); 59 } 60 61 if (mHeadsetProfile != null) { 62 mClientHasEcc = HfpClientConnectionService.hasHfpClientEcc(mHeadsetProfile, mDevice); 63 } 64 setAudioModeIsVoip(false); 65 setAddress(number, TelecomManager.PRESENTATION_ALLOWED); 66 setConnectionCapabilities(CAPABILITY_SUPPORT_HOLD | CAPABILITY_MUTE | 67 CAPABILITY_SEPARATE_FROM_CONFERENCE | CAPABILITY_DISCONNECT_FROM_CONFERENCE | 68 (getState() == STATE_ACTIVE || getState() == STATE_HOLDING ? CAPABILITY_HOLD : 0)); 69 } 70 71 public UUID getUUID() { 72 return mCurrentCall.getUUID(); 73 } 74 75 public void onHfpConnected(BluetoothHeadsetClient client) { 76 mHeadsetProfile = client; 77 mClientHasEcc = HfpClientConnectionService.hasHfpClientEcc(mHeadsetProfile, mDevice); 78 handleCallChanged(); 79 } 80 81 public void onHfpDisconnected() { 82 mHeadsetProfile = null; 83 close(DisconnectCause.ERROR); 84 } 85 86 public void onAdded() { 87 mAdded = true; 88 } 89 90 public BluetoothHeadsetClientCall getCall() { 91 return mCurrentCall; 92 } 93 94 public boolean inConference() { 95 return mAdded && mCurrentCall != null && mCurrentCall.isMultiParty() && 96 getState() != Connection.STATE_DISCONNECTED; 97 } 98 99 public void enterPrivateMode() { 100 mHeadsetProfile.enterPrivateMode(mDevice, mCurrentCall.getId()); 101 setActive(); 102 } 103 104 public void updateCall(BluetoothHeadsetClientCall call) { 105 mCurrentCall = call; 106 } 107 108 public void handleCallChanged() { 109 HfpClientConference conference = (HfpClientConference) getConference(); 110 int state = mCurrentCall.getState(); 111 112 Log.d(TAG, "Got call state change to " + state); 113 switch (state) { 114 case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE: 115 setActive(); 116 if (conference != null) { 117 conference.setActive(); 118 } 119 break; 120 case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: 121 case BluetoothHeadsetClientCall.CALL_STATE_HELD: 122 setOnHold(); 123 if (conference != null) { 124 conference.setOnHold(); 125 } 126 break; 127 case BluetoothHeadsetClientCall.CALL_STATE_DIALING: 128 case BluetoothHeadsetClientCall.CALL_STATE_ALERTING: 129 setDialing(); 130 break; 131 case BluetoothHeadsetClientCall.CALL_STATE_INCOMING: 132 case BluetoothHeadsetClientCall.CALL_STATE_WAITING: 133 setRinging(); 134 break; 135 case BluetoothHeadsetClientCall.CALL_STATE_TERMINATED: 136 // TODO Use more specific causes 137 close(mLocalDisconnect ? DisconnectCause.LOCAL : DisconnectCause.REMOTE); 138 break; 139 default: 140 Log.wtf(TAG, "Unexpected phone state " + state); 141 } 142 } 143 144 private void close(int cause) { 145 Log.d(TAG, "Closing " + mClosed); 146 if (mClosed) { 147 return; 148 } 149 150 setDisconnected(new DisconnectCause(cause)); 151 152 mClosed = true; 153 mCurrentCall = null; 154 155 destroy(); 156 } 157 158 @Override 159 public void onPlayDtmfTone(char c) { 160 Log.d(TAG, "onPlayDtmfTone " + c + " " + mCurrentCall); 161 if (!mClosed && mHeadsetProfile != null) { 162 mHeadsetProfile.sendDTMF(mDevice, (byte) c); 163 } 164 } 165 166 @Override 167 public synchronized void onDisconnect() { 168 Log.d(TAG, "onDisconnect " + mCurrentCall); 169 // In this state we can close the call without problems. 170 if (mHeadsetProfile != null) { 171 mHeadsetProfile.terminateCall(mDevice, mCurrentCall); 172 mLocalDisconnect = true; 173 } else if (mCurrentCall == null) { 174 Log.w(TAG, "Call disconnected but call handle not received."); 175 } 176 } 177 178 @Override 179 public void onAbort() { 180 Log.d(TAG, "onAbort " + mCurrentCall); 181 onDisconnect(); 182 } 183 184 @Override 185 public void onHold() { 186 Log.d(TAG, "onHold " + mCurrentCall); 187 if (!mClosed && mHeadsetProfile != null) { 188 mHeadsetProfile.holdCall(mDevice); 189 } 190 } 191 192 @Override 193 public void onUnhold() { 194 if (getConnectionService().getAllConnections().size() > 1) { 195 Log.w(TAG, "Ignoring unhold; call hold on the foreground call"); 196 return; 197 } 198 Log.d(TAG, "onUnhold " + mCurrentCall); 199 if (!mClosed && mHeadsetProfile != null) { 200 mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_HOLD); 201 } 202 } 203 204 @Override 205 public void onAnswer() { 206 Log.d(TAG, "onAnswer " + mCurrentCall); 207 if (!mClosed) { 208 mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_NONE); 209 } 210 } 211 212 @Override 213 public void onReject() { 214 Log.d(TAG, "onReject " + mCurrentCall); 215 if (!mClosed) { 216 mHeadsetProfile.rejectCall(mDevice); 217 } 218 } 219 220 @Override 221 public boolean equals(Object o) { 222 if (!(o instanceof HfpClientConnection)) { 223 return false; 224 } 225 Uri otherAddr = ((HfpClientConnection) o).getAddress(); 226 return getAddress() == otherAddr || otherAddr != null && otherAddr.equals(getAddress()); 227 } 228 229 @Override 230 public int hashCode() { 231 return getAddress() == null ? 0 : getAddress().hashCode(); 232 } 233 234 @Override 235 public String toString() { 236 return "HfpClientConnection{" + getAddress() + "," + stateToString(getState()) + "," + 237 mCurrentCall + "}"; 238 } 239} 240