TelephonyConnectionService.java revision b0baef15d2559af2506ba76c79b072f4d683c81c
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.net.Uri; 20import android.telephony.DisconnectCause; 21import android.telephony.ServiceState; 22import android.telecomm.Subscription; 23import android.text.TextUtils; 24 25import com.android.internal.telephony.CallStateException; 26import com.android.internal.telephony.Connection.PostDialListener; 27import com.android.internal.telephony.Phone; 28 29import android.telecomm.Connection; 30import android.telecomm.ConnectionRequest; 31import android.telecomm.ConnectionService; 32import android.telecomm.Response; 33 34import java.util.HashSet; 35import java.util.Set; 36 37/** 38 * The parent class for Android's built-in connection services. 39 */ 40public abstract class TelephonyConnectionService extends ConnectionService { 41 private static final Set<com.android.internal.telephony.Connection> sKnownConnections 42 = new HashSet<>(); 43 44 /** 45 * Initiates the underlying Telephony call, then creates a {@link TelephonyConnection} 46 * by calling 47 * {@link #createTelephonyConnection( 48 * ConnectionRequest,Phone,com.android.internal.telephony.Connection)} 49 * at the appropriate time. Should be called by the subclass. 50 */ 51 protected void startCallWithPhone( 52 Phone phone, 53 ConnectionRequest request, 54 OutgoingCallResponse<Connection> response) { 55 Log.d(this, "startCallWithPhone: %s.", request); 56 57 if (phone == null) { 58 respondWithError( 59 request, 60 response, 61 DisconnectCause.ERROR_UNSPECIFIED, // Generic internal error 62 "Phone is null"); 63 return; 64 } 65 66 if (request.getHandle() == null) { 67 respondWithError( 68 request, 69 response, 70 DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, 71 "Handle is null"); 72 return; 73 } 74 75 String number = request.getHandle().getSchemeSpecificPart(); 76 if (TextUtils.isEmpty(number)) { 77 respondWithError( 78 request, 79 response, 80 DisconnectCause.INVALID_NUMBER, 81 "Unable to parse number"); 82 return; 83 } 84 85 if (!checkServiceStateForOutgoingCall(phone, request, response)) { 86 return; 87 } 88 89 com.android.internal.telephony.Connection connection; 90 try { 91 connection = phone.dial(number); 92 } catch (CallStateException e) { 93 Log.e(this, e, "Call to Phone.dial failed with exception"); 94 respondWithError( 95 request, 96 response, 97 DisconnectCause.ERROR_UNSPECIFIED, // Generic internal error 98 e.getMessage()); 99 return; 100 } 101 102 if (connection == null) { 103 respondWithError( 104 request, 105 response, 106 DisconnectCause.ERROR_UNSPECIFIED, // Generic internal error 107 "Call to phone.dial failed"); 108 return; 109 } 110 111 try { 112 final TelephonyConnection telephonyConnection = 113 createTelephonyConnection(request, phone, connection); 114 respondWithResult(request, response, telephonyConnection); 115 116 final com.android.internal.telephony.Connection connectionFinal = connection; 117 PostDialListener postDialListener = new PostDialListener() { 118 @Override 119 public void onPostDialWait() { 120 TelephonyConnectionService.this.onPostDialWait(telephonyConnection, 121 connectionFinal.getRemainingPostDialString()); 122 } 123 }; 124 connection.addPostDialListener(postDialListener); 125 } catch (Exception e) { 126 Log.e(this, e, "Call to createConnection failed with exception"); 127 respondWithError( 128 request, 129 response, 130 DisconnectCause.ERROR_UNSPECIFIED, // Generic internal error 131 e.getMessage()); 132 } 133 } 134 135 private boolean checkServiceStateForOutgoingCall( 136 Phone phone, 137 ConnectionRequest request, 138 OutgoingCallResponse<Connection> response) { 139 int state = phone.getServiceState().getState(); 140 switch (state) { 141 case ServiceState.STATE_IN_SERVICE: 142 return true; 143 case ServiceState.STATE_OUT_OF_SERVICE: 144 respondWithError( 145 request, 146 response, 147 DisconnectCause.OUT_OF_SERVICE, 148 null); 149 break; 150 case ServiceState.STATE_EMERGENCY_ONLY: 151 respondWithError( 152 request, 153 response, 154 DisconnectCause.EMERGENCY_ONLY, 155 null); 156 break; 157 case ServiceState.STATE_POWER_OFF: 158 respondWithError( 159 request, 160 response, 161 DisconnectCause.POWER_OFF, 162 null); 163 break; 164 default: 165 // Internal error, but we pass it upwards and do not crash. 166 Log.d(this, "Unrecognized service state %d", state); 167 respondWithError( 168 request, 169 response, 170 DisconnectCause.ERROR_UNSPECIFIED, 171 "Unrecognized service state " + state); 172 break; 173 } 174 return false; 175 } 176 177 protected <REQUEST, RESULT> void respondWithError( 178 REQUEST request, 179 Response<REQUEST, RESULT> response, 180 int errorCode, 181 String errorMsg) { 182 Log.d(this, "respondWithError %s: %d %s", request, errorCode, errorMsg); 183 response.onError(request, errorCode, errorMsg); 184 } 185 186 protected void respondWithResult( 187 ConnectionRequest request, 188 Response<ConnectionRequest, Connection> response, 189 Connection result) { 190 Log.d(this, "respondWithResult %s -> %s", request, result); 191 response.onResult(request, result); 192 } 193 194 protected void respondWithResult( 195 ConnectionRequest request, 196 OutgoingCallResponse<Connection> response, 197 Connection result) { 198 Log.d(this, "respondWithResult %s -> %s", request, result); 199 response.onSuccess(request, result); 200 } 201 202 protected void respondWithError( 203 ConnectionRequest request, 204 OutgoingCallResponse<Connection> response, 205 int errorCode, 206 String errorMsg) { 207 Log.d(this, "respondWithError %s: %d %s", request, errorCode, errorMsg); 208 response.onFailure(request, errorCode, errorMsg); 209 } 210 211 protected final TelephonyConnection createTelephonyConnection( 212 ConnectionRequest request, 213 Phone phone, 214 final com.android.internal.telephony.Connection connection) { 215 final TelephonyConnection telephonyConnection = 216 onCreateTelephonyConnection(request, phone, connection); 217 sKnownConnections.add(connection); 218 telephonyConnection.addConnectionListener(new Connection.ListenerBase() { 219 @Override 220 public void onDestroyed(Connection c) { 221 telephonyConnection.removeConnectionListener(this); 222 sKnownConnections.remove(connection); 223 } 224 }); 225 226 return telephonyConnection; 227 } 228 229 protected static boolean isConnectionKnown( 230 com.android.internal.telephony.Connection connection) { 231 return sKnownConnections.contains(connection); 232 } 233 234 /** 235 * Determine whether this {@link TelephonyConnectionService} can place a call 236 * to the supplied handle (phone number). 237 * 238 * @param handle The proposed handle. 239 * @return {@code true} if the handle can be called. 240 */ 241 protected abstract boolean canCall(Uri handle); 242 243 /** 244 * Create a Telephony-specific {@link Connection} object. 245 * 246 * @param request A request for creating a {@link Connection}. 247 * @param phone A {@code Phone} object to use. 248 * @param connection An underlying Telephony {@link com.android.internal.telephony.Connection} 249 * to use. 250 * @return A new {@link TelephonyConnection}. 251 */ 252 protected abstract TelephonyConnection onCreateTelephonyConnection( 253 ConnectionRequest request, 254 Phone phone, 255 com.android.internal.telephony.Connection connection); 256} 257