TelephonyConnectionService.java revision 213c6162947edb4ba729f1fd3d583b76b110c0ad
1/* 2 * Copyright (C) 2014 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 17package com.android.services.telephony; 18 19import android.content.ComponentName; 20import android.content.Context; 21import android.net.Uri; 22import android.os.Debug; 23import android.telephony.DisconnectCause; 24import android.telephony.ServiceState; 25import android.text.TextUtils; 26import android.telecomm.CallCapabilities; 27import android.telecomm.Connection; 28import android.telecomm.ConnectionRequest; 29import android.telecomm.ConnectionService; 30import android.telecomm.PhoneAccount; 31import android.telecomm.Response; 32import android.telephony.PhoneNumberUtils; 33import android.telephony.TelephonyManager; 34 35import com.android.internal.telephony.Call; 36import com.android.internal.telephony.CallStateException; 37import com.android.internal.telephony.Phone; 38import com.android.internal.telephony.PhoneFactory; 39 40/** 41 * Service for making GSM and CDMA connections. 42 */ 43public class TelephonyConnectionService extends ConnectionService { 44 private static String SCHEME_TEL = "tel"; 45 46 private EmergencyCallHelper mEmergencyCallHelper; 47 48 static PhoneAccount getPhoneAccount(Context context) { 49 return new PhoneAccount( 50 new ComponentName(context, TelephonyConnectionService.class), 51 null /* id */, 52 null, 53 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION); 54 } 55 56 @Override 57 protected void onCreateOutgoingConnection( 58 final ConnectionRequest request, 59 final CreateConnectionResponse<Connection> response) { 60 Log.v(this, "onCreateOutgoingConnection, request: " + request); 61 62 Uri handle = request.getHandle(); 63 if (handle == null) { 64 Log.d(this, "onCreateOutgoingConnection, handle is null"); 65 response.onFailure(request, DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, "Handle is null"); 66 return; 67 } 68 69 if (!SCHEME_TEL.equals(handle.getScheme())) { 70 Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", 71 handle.getScheme()); 72 response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, 73 "Handle scheme is not type tel"); 74 return; 75 } 76 77 final String number = handle.getSchemeSpecificPart(); 78 if (TextUtils.isEmpty(number)) { 79 Log.d(this, "onCreateOutgoingConnection, unable to parse number"); 80 response.onFailure(request, DisconnectCause.INVALID_NUMBER, "Unable to parse number"); 81 return; 82 } 83 84 final Phone phone = PhoneFactory.getDefaultPhone(); 85 if (phone == null) { 86 Log.d(this, "onCreateOutgoingConnection, phone is null"); 87 response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, "Phone is null"); 88 return; 89 } 90 91 boolean isEmergencyNumber = PhoneNumberUtils.isPotentialEmergencyNumber(number); 92 if (!isEmergencyNumber) { 93 int state = phone.getServiceState().getState(); 94 switch (state) { 95 case ServiceState.STATE_IN_SERVICE: 96 break; 97 case ServiceState.STATE_OUT_OF_SERVICE: 98 response.onFailure(request, DisconnectCause.OUT_OF_SERVICE, 99 "ServiceState.STATE_OUT_OF_SERVICE"); 100 return; 101 case ServiceState.STATE_EMERGENCY_ONLY: 102 response.onFailure(request, DisconnectCause.EMERGENCY_ONLY, 103 "ServiceState.STATE_EMERGENCY_ONLY"); 104 return; 105 case ServiceState.STATE_POWER_OFF: 106 response.onFailure(request, DisconnectCause.POWER_OFF, 107 "ServiceState.STATE_POWER_OFF"); 108 return; 109 default: 110 Log.d(this, "onCreateOutgoingConnection, unkown service state: %d", state); 111 response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, 112 "Unkown service state " + state); 113 return; 114 } 115 } 116 117 if (isEmergencyNumber) { 118 Log.d(this, "onCreateOutgoingConnection, doing startTurnOnRadioSequence for " + 119 "emergency number"); 120 if (mEmergencyCallHelper == null) { 121 mEmergencyCallHelper = new EmergencyCallHelper(this); 122 } 123 mEmergencyCallHelper.startTurnOnRadioSequence(phone, 124 new EmergencyCallHelper.Callback() { 125 @Override 126 public void onComplete(boolean isRadioReady) { 127 if (isRadioReady) { 128 startOutgoingCall(request, response, phone, number); 129 } else { 130 Log.d(this, "onCreateOutgoingConnection, failed to turn on radio"); 131 response.onFailure(request, DisconnectCause.POWER_OFF, 132 "Failed to turn on radio."); 133 } 134 } 135 }); 136 return; 137 } 138 139 startOutgoingCall(request, response, phone, number); 140 } 141 142 @Override 143 protected void onCreateConferenceConnection( 144 String token, 145 Connection connection, 146 Response<String, Connection> response) { 147 Log.v(this, "onCreateConferenceConnection, connection: " + connection); 148 if (connection instanceof GsmConnection || connection instanceof ConferenceConnection) { 149 if ((connection.getCallCapabilities() & CallCapabilities.MERGE_CALLS) != 0) { 150 response.onResult(token, 151 GsmConferenceController.createConferenceConnection(connection)); 152 } 153 } 154 } 155 156 @Override 157 protected void onCreateIncomingConnection( 158 ConnectionRequest request, 159 CreateConnectionResponse<Connection> response) { 160 Log.v(this, "onCreateIncomingConnection, request: " + request); 161 162 Phone phone = PhoneFactory.getDefaultPhone(); 163 Call call = phone.getRingingCall(); 164 if (!call.getState().isRinging()) { 165 Log.v(this, "onCreateIncomingConnection, no ringing call"); 166 response.onFailure(request, DisconnectCause.INCOMING_MISSED, "Found no ringing call"); 167 return; 168 } 169 170 com.android.internal.telephony.Connection originalConnection = call.getEarliestConnection(); 171 if (isOriginalConnectionKnown(originalConnection)) { 172 Log.v(this, "onCreateIncomingConnection, original connection already registered"); 173 response.onCancel(request); 174 return; 175 } 176 177 Uri handle = getHandleFromAddress(originalConnection.getAddress()); 178 ConnectionRequest telephonyRequest = new ConnectionRequest( 179 getPhoneAccount(this), 180 request.getCallId(), 181 handle, 182 originalConnection.getNumberPresentation(), 183 request.getExtras(), 184 request.getVideoState()); 185 186 if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { 187 response.onSuccess(telephonyRequest, new GsmConnection(originalConnection)); 188 } else if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 189 response.onSuccess(telephonyRequest, new CdmaConnection(originalConnection)); 190 } else { 191 response.onCancel(request); 192 } 193 } 194 195 @Override 196 protected void onConnectionAdded(Connection connection) { 197 Log.v(this, "onConnectionAdded, connection: " + connection); 198 if (connection instanceof TelephonyConnection) { 199 ((TelephonyConnection) connection).onAddedToCallService(this); 200 } 201 } 202 203 @Override 204 protected void onConnectionRemoved(Connection connection) { 205 Log.v(this, "onConnectionRemoved, connection: " + connection); 206 if (connection instanceof TelephonyConnection) { 207 ((TelephonyConnection) connection).onRemovedFromCallService(); 208 } 209 } 210 211 private void startOutgoingCall( 212 ConnectionRequest request, 213 CreateConnectionResponse<Connection> response, 214 Phone phone, 215 String number) { 216 Log.v(this, "startOutgoingCall"); 217 218 com.android.internal.telephony.Connection originalConnection; 219 try { 220 originalConnection = phone.dial(number, request.getVideoState()); 221 } catch (CallStateException e) { 222 Log.e(this, e, "startOutgoingCall, phone.dial exception: " + e); 223 response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, e.getMessage()); 224 return; 225 } 226 227 if (originalConnection == null) { 228 Log.d(this, "startOutgoingCall, phone.dial returned null"); 229 response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, "Connection is null"); 230 return; 231 } 232 233 ConnectionRequest telephonyRequest = new ConnectionRequest( 234 getPhoneAccount(this), 235 request.getCallId(), 236 request.getHandle(), 237 request.getHandlePresentation(), 238 request.getExtras(), 239 request.getVideoState()); 240 241 if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { 242 response.onSuccess(telephonyRequest, new GsmConnection(originalConnection)); 243 } else if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 244 response.onSuccess(telephonyRequest, new CdmaConnection(originalConnection)); 245 } else { 246 // TODO(ihab): Tear down 'originalConnection' here, or move recognition of 247 // getPhoneType() earlier in this method before we've already asked phone to dial() 248 response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, "Invalid phone type"); 249 } 250 } 251 252 private boolean isOriginalConnectionKnown( 253 com.android.internal.telephony.Connection originalConnection) { 254 for (Connection connection : getAllConnections()) { 255 TelephonyConnection telephonyConnection = (TelephonyConnection) connection; 256 if (connection instanceof TelephonyConnection) { 257 if (telephonyConnection.getOriginalConnection() == originalConnection) { 258 return true; 259 } 260 } 261 } 262 return false; 263 } 264 265 static Uri getHandleFromAddress(String address) { 266 // Address can be null for blocked calls. 267 if (address == null) { 268 address = ""; 269 } 270 return Uri.fromParts(SCHEME_TEL, address, null); 271 } 272} 273