Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 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 | R* limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.telecomm; |
| 18 | |
| 19 | import android.content.ComponentName; |
| 20 | import android.net.Uri; |
| 21 | import android.os.IBinder.DeathRecipient; |
| 22 | import android.os.RemoteException; |
| 23 | import android.telephony.DisconnectCause; |
| 24 | |
| 25 | import android.text.TextUtils; |
| 26 | |
| 27 | import com.android.internal.telecomm.ICallService; |
| 28 | import com.android.internal.telecomm.ICallServiceAdapter; |
Andrew Lee | 5ffbe8b | 2014-06-20 16:29:33 -0700 | [diff] [blame] | 29 | import com.android.internal.telecomm.ICallVideoProvider; |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 30 | import com.android.internal.telecomm.RemoteServiceCallback; |
| 31 | |
| 32 | import java.util.ArrayList; |
| 33 | import java.util.Collections; |
| 34 | import java.util.LinkedList; |
| 35 | import java.util.List; |
| 36 | import java.util.UUID; |
| 37 | |
| 38 | /** |
| 39 | * Remote connection service which other connection services can use to place calls on their behalf. |
Sailesh Nepal | 091768c | 2014-06-30 15:15:23 -0700 | [diff] [blame^] | 40 | * |
| 41 | * @hide |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 42 | */ |
Sailesh Nepal | 091768c | 2014-06-30 15:15:23 -0700 | [diff] [blame^] | 43 | public final class RemoteConnectionService implements DeathRecipient { |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 44 | private final ICallService mCallService; |
| 45 | private final ComponentName mComponentName; |
| 46 | |
| 47 | private String mConnectionId; |
| 48 | private ConnectionRequest mPendingRequest; |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 49 | private ConnectionService.OutgoingCallResponse<RemoteConnection> mPendingOutgoingCallResponse; |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 50 | // Remote connection services only support a single connection. |
| 51 | private RemoteConnection mConnection; |
| 52 | |
| 53 | private final ICallServiceAdapter mAdapter = new ICallServiceAdapter.Stub() { |
| 54 | |
| 55 | /** ${inheritDoc} */ |
| 56 | @Override |
| 57 | public void notifyIncomingCall(CallInfo callInfo) { |
| 58 | Log.w(this, "notifyIncomingCall not implemented in Remote connection"); |
| 59 | } |
| 60 | |
| 61 | /** ${inheritDoc} */ |
| 62 | @Override |
| 63 | public void handleSuccessfulOutgoingCall(String connectionId) { |
| 64 | if (isPendingConnection(connectionId)) { |
| 65 | mConnection = new RemoteConnection(mCallService, connectionId); |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 66 | mPendingOutgoingCallResponse.onSuccess(mPendingRequest, mConnection); |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 67 | clearPendingInformation(); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /** ${inheritDoc} */ |
| 72 | @Override |
| 73 | public void handleFailedOutgoingCall( |
| 74 | ConnectionRequest request, int errorCode, String errorMessage) { |
| 75 | if (isPendingConnection(request.getCallId())) { |
Sailesh Nepal | 091768c | 2014-06-30 15:15:23 -0700 | [diff] [blame^] | 76 | mPendingOutgoingCallResponse.onFailure(request, errorCode, errorMessage); |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 77 | mConnectionId = null; |
| 78 | clearPendingInformation(); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | /** ${inheritDoc} */ |
| 83 | @Override |
| 84 | public void cancelOutgoingCall(String connectionId) { |
| 85 | if (isPendingConnection(connectionId)) { |
| 86 | mPendingOutgoingCallResponse.onCancel(mPendingRequest); |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 87 | mConnectionId = null; |
| 88 | clearPendingInformation(); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | /** ${inheritDoc} */ |
| 93 | @Override |
| 94 | public void setActive(String connectionId) { |
| 95 | if (isCurrentConnection(connectionId)) { |
| 96 | mConnection.setState(Connection.State.ACTIVE); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /** ${inheritDoc} */ |
| 101 | @Override |
| 102 | public void setRinging(String connectionId) { |
| 103 | if (isCurrentConnection(connectionId)) { |
| 104 | mConnection.setState(Connection.State.RINGING); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | /** ${inheritDoc} */ |
Andrew Lee | 5ffbe8b | 2014-06-20 16:29:33 -0700 | [diff] [blame] | 109 | public void setCallVideoProvider( |
| 110 | String connectionId, ICallVideoProvider callVideoProvider) { |
| 111 | // not supported for remote connections. |
| 112 | } |
| 113 | |
| 114 | /** ${inheritDoc} */ |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 115 | @Override |
| 116 | public void setDialing(String connectionId) { |
| 117 | if (isCurrentConnection(connectionId)) { |
| 118 | mConnection.setState(Connection.State.DIALING); |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | /** ${inheritDoc} */ |
| 123 | @Override |
| 124 | public void setDisconnected( |
| 125 | String connectionId, int disconnectCause, String disconnectMessage) { |
| 126 | if (isCurrentConnection(connectionId)) { |
| 127 | mConnection.setDisconnected(disconnectCause, disconnectMessage); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | /** ${inheritDoc} */ |
| 132 | @Override |
| 133 | public void setOnHold(String connectionId) { |
| 134 | if (isCurrentConnection(connectionId)) { |
| 135 | mConnection.setState(Connection.State.HOLDING); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | /** ${inheritDoc} */ |
| 140 | @Override |
| 141 | public void setRequestingRingback(String connectionId, boolean isRequestingRingback) { |
| 142 | if (isCurrentConnection(connectionId)) { |
| 143 | mConnection.setRequestingRingback(isRequestingRingback); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | /** ${inheritDoc} */ |
| 148 | @Override |
| 149 | public void setCanConference(String connectionId, boolean canConference) { |
| 150 | // not supported for remote connections. |
| 151 | } |
| 152 | |
| 153 | /** ${inheritDoc} */ |
| 154 | @Override |
| 155 | public void setIsConferenced(String connectionId, String conferenceConnectionId) { |
| 156 | // not supported for remote connections. |
| 157 | } |
| 158 | |
| 159 | /** ${inheritDoc} */ |
| 160 | @Override |
| 161 | public void addConferenceCall(String connectionId, CallInfo callInfo) { |
| 162 | // not supported for remote connections. |
| 163 | } |
| 164 | |
| 165 | /** ${inheritDoc} */ |
| 166 | @Override |
| 167 | public void removeCall(String connectionId) { |
| 168 | if (isCurrentConnection(connectionId)) { |
| 169 | destroyConnection(); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | /** ${inheritDoc} */ |
| 174 | @Override |
| 175 | public void onPostDialWait(String connectionId, String remainingDigits) { |
| 176 | if (isCurrentConnection(connectionId)) { |
| 177 | mConnection.setPostDialWait(remainingDigits); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | /** ${inheritDoc} */ |
| 182 | @Override |
| 183 | public void handoffCall(String connectionId) { |
| 184 | // unnecessary. |
| 185 | } |
| 186 | |
| 187 | /** ${inheritDoc} */ |
| 188 | @Override |
| 189 | public void queryRemoteConnectionServices(RemoteServiceCallback callback) { |
| 190 | try { |
| 191 | // Not supported from remote connection service. |
| 192 | callback.onError(); |
| 193 | } catch (RemoteException e) { |
| 194 | } |
| 195 | } |
| 196 | }; |
| 197 | |
| 198 | RemoteConnectionService(ComponentName componentName, ICallService callService) |
| 199 | throws RemoteException { |
| 200 | mComponentName = componentName; |
| 201 | mCallService = callService; |
| 202 | |
| 203 | // TODO(santoscordon): Rename from setCallServiceAdapter to addCallServiceAdapter. |
| 204 | mCallService.setCallServiceAdapter(mAdapter); |
| 205 | mCallService.asBinder().linkToDeath(this, 0); |
| 206 | } |
| 207 | |
| 208 | @Override |
| 209 | public String toString() { |
| 210 | return "[RemoteCS - " + mCallService.asBinder().toString() + "]"; |
| 211 | } |
| 212 | |
| 213 | /** ${inheritDoc} */ |
| 214 | @Override |
Sailesh Nepal | 091768c | 2014-06-30 15:15:23 -0700 | [diff] [blame^] | 215 | public final void binderDied() { |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 216 | if (mConnection != null) { |
| 217 | destroyConnection(); |
| 218 | } |
| 219 | |
| 220 | release(); |
| 221 | } |
| 222 | |
| 223 | /** |
| 224 | * Places an outgoing call. |
| 225 | */ |
Sailesh Nepal | 091768c | 2014-06-30 15:15:23 -0700 | [diff] [blame^] | 226 | public final void createOutgoingConnection( |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 227 | ConnectionRequest request, |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 228 | ConnectionService.OutgoingCallResponse<RemoteConnection> response) { |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 229 | |
| 230 | if (mConnectionId == null) { |
| 231 | String id = UUID.randomUUID().toString(); |
| 232 | CallInfo callInfo = new CallInfo(id, CallState.NEW, request.getHandle()); |
| 233 | try { |
| 234 | mCallService.call(callInfo); |
| 235 | mConnectionId = id; |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 236 | mPendingOutgoingCallResponse = response; |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 237 | mPendingRequest = request; |
| 238 | } catch (RemoteException e) { |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 239 | response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, e.toString()); |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 240 | } |
| 241 | } else { |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 242 | response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, null); |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 243 | } |
| 244 | } |
| 245 | |
| 246 | // TODO(santoscordon): Handle incoming connections |
Sailesh Nepal | 091768c | 2014-06-30 15:15:23 -0700 | [diff] [blame^] | 247 | // public final void handleIncomingConnection() {} |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 248 | |
Sailesh Nepal | 091768c | 2014-06-30 15:15:23 -0700 | [diff] [blame^] | 249 | public final List<Subscription> lookupSubscriptions(Uri handle) { |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 250 | // TODO(santoscordon): Update this so that is actually calls into the RemoteConnection |
| 251 | // each time. |
| 252 | List<Subscription> subscriptions = new LinkedList<>(); |
| 253 | subscriptions.add(new Subscription( |
| 254 | mComponentName, |
| 255 | null /* id */, |
| 256 | null /* handle */, |
| 257 | 0 /* labelResId */, |
| 258 | 0 /* shortDescriptionResId */, |
| 259 | 0 /* iconResId */, |
| 260 | true /* isEnabled */, |
| 261 | false /* isSystemDefault */)); |
| 262 | return subscriptions; |
| 263 | } |
| 264 | |
| 265 | /** |
| 266 | * Releases the resources associated with this Remote connection service. Should be called when |
| 267 | * the remote service is no longer being used. |
| 268 | */ |
| 269 | void release() { |
| 270 | mCallService.asBinder().unlinkToDeath(this, 0); |
| 271 | } |
| 272 | |
| 273 | private boolean isPendingConnection(String id) { |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 274 | return TextUtils.equals(mConnectionId, id) && mPendingOutgoingCallResponse != null; |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 275 | } |
| 276 | |
| 277 | private boolean isCurrentConnection(String id) { |
| 278 | return mConnection != null && TextUtils.equals(mConnectionId, id); |
| 279 | } |
| 280 | |
| 281 | private void clearPendingInformation() { |
| 282 | mPendingRequest = null; |
Sailesh Nepal | 506e386 | 2014-06-25 13:35:14 -0700 | [diff] [blame] | 283 | mPendingOutgoingCallResponse = null; |
Santos Cordon | 52d8a15 | 2014-06-17 19:08:45 -0700 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | private void destroyConnection() { |
| 287 | mConnection.setDestroyed(); |
| 288 | mConnection = null; |
| 289 | mConnectionId = null; |
| 290 | } |
| 291 | } |