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