OSDN Git Service

79193c246210f8ad17440eebd0c6bc5002facc2c
[android-x86/frameworks-base.git] / telecomm / java / android / telecomm / RemoteConnectionService.java
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
17 package android.telecomm;
18
19 import android.app.PendingIntent;
20 import android.net.Uri;
21 import android.os.IBinder;
22 import android.os.IBinder.DeathRecipient;
23 import android.os.RemoteException;
24 import android.telephony.DisconnectCause;
25
26 import com.android.internal.telecomm.IConnectionService;
27 import com.android.internal.telecomm.IConnectionServiceAdapter;
28 import com.android.internal.telecomm.IVideoProvider;
29 import com.android.internal.telecomm.RemoteServiceCallback;
30
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.List;
37 import java.util.UUID;
38
39 /**
40  * Remote connection service which other connection services can use to place calls on their behalf.
41  *
42  * @hide
43  */
44 final class RemoteConnectionService {
45
46     private static final RemoteConnection NULL_CONNECTION =
47             new RemoteConnection("NULL", null, null);
48
49     private static final RemoteConference NULL_CONFERENCE =
50             new RemoteConference("NULL", null);
51
52     private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() {
53         @Override
54         public void handleCreateConnectionComplete(
55                 String id,
56                 ConnectionRequest request,
57                 ParcelableConnection parcel) {
58             RemoteConnection connection =
59                     findConnectionForAction(id, "handleCreateConnectionSuccessful");
60             if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) {
61                 mPendingConnections.remove(connection);
62                 // Unconditionally initialize the connection ...
63                 connection.setState(parcel.getState());
64                 connection.setCallCapabilities(parcel.getCapabilities());
65                 connection.setHandle(
66                         parcel.getHandle(), parcel.getHandlePresentation());
67                 connection.setCallerDisplayName(
68                         parcel.getCallerDisplayName(),
69                         parcel.getCallerDisplayNamePresentation());
70                 List<RemoteConnection> conferenceable = new ArrayList<>();
71                 for (String confId : parcel.getConferenceableConnectionIds()) {
72                     if (mConnectionById.containsKey(confId)) {
73                         conferenceable.add(mConnectionById.get(confId));
74                     }
75                 }
76                 connection.setConferenceableConnections(conferenceable);
77                 // TODO: Do we need to support video providers for remote connections?
78                 if (connection.getState() == Connection.STATE_DISCONNECTED) {
79                     // ... then, if it was created in a disconnected state, that indicates
80                     // failure on the providing end, so immediately mark it destroyed
81                     connection.setDestroyed();
82                 }
83             }
84         }
85
86         @Override
87         public void setActive(String callId) {
88             if (mConnectionById.containsKey(callId)) {
89                 findConnectionForAction(callId, "setActive")
90                         .setState(Connection.STATE_ACTIVE);
91             } else {
92                 findConferenceForAction(callId, "setActive")
93                         .setState(Connection.STATE_ACTIVE);
94             }
95         }
96
97         @Override
98         public void setRinging(String callId) {
99             findConnectionForAction(callId, "setRinging")
100                     .setState(Connection.STATE_RINGING);
101         }
102
103         @Override
104         public void setDialing(String callId) {
105             findConnectionForAction(callId, "setDialing")
106                     .setState(Connection.STATE_DIALING);
107         }
108
109         @Override
110         public void setDisconnected(String callId, int disconnectCause,
111                 String disconnectMessage) {
112             if (mConnectionById.containsKey(callId)) {
113                 findConnectionForAction(callId, "setDisconnected")
114                         .setDisconnected(disconnectCause, disconnectMessage);
115             } else {
116                 findConferenceForAction(callId, "setDisconnected")
117                         .setDisconnected(disconnectCause, disconnectMessage);
118             }
119         }
120
121         @Override
122         public void setOnHold(String callId) {
123             if (mConnectionById.containsKey(callId)) {
124                 findConnectionForAction(callId, "setOnHold")
125                         .setState(Connection.STATE_HOLDING);
126             } else {
127                 findConferenceForAction(callId, "setOnHold")
128                         .setState(Connection.STATE_HOLDING);
129             }
130         }
131
132         @Override
133         public void setRequestingRingback(String callId, boolean ringing) {
134             findConnectionForAction(callId, "setRequestingRingback")
135                     .setRequestingRingback(ringing);
136         }
137
138         @Override
139         public void setCallCapabilities(String callId, int callCapabilities) {
140             if (mConnectionById.containsKey(callId)) {
141                 findConnectionForAction(callId, "setCallCapabilities")
142                         .setCallCapabilities(callCapabilities);
143             } else {
144                 findConferenceForAction(callId, "setCallCapabilities")
145                         .setCallCapabilities(callCapabilities);
146             }
147         }
148
149         @Override
150         public void setIsConferenced(String callId, String conferenceCallId) {
151             // Note: callId should not be null; conferenceCallId may be null
152             RemoteConnection connection =
153                     findConnectionForAction(callId, "setIsConferenced");
154             if (connection != NULL_CONNECTION) {
155                 if (conferenceCallId == null) {
156                     // 'connection' is being split from its conference
157                     if (connection.getConference() != null) {
158                         connection.getConference().removeConnection(connection);
159                     }
160                 } else {
161                     RemoteConference conference =
162                             findConferenceForAction(conferenceCallId, "setIsConferenced");
163                     if (conference != NULL_CONFERENCE) {
164                         conference.addConnection(connection);
165                     }
166                 }
167             }
168         }
169
170         @Override
171         public void addConferenceCall(
172                 final String callId,
173                 ParcelableConference parcel) {
174             RemoteConference conference = new RemoteConference(callId,
175                     mOutgoingConnectionServiceRpc);
176
177             for (String id : parcel.getConnectionIds()) {
178                 RemoteConnection c = mConnectionById.get(id);
179                 if (c != null) {
180                     conference.addConnection(c);
181                 }
182             }
183
184             if (conference.getConnections().size() == 0) {
185                 // A conference was created, but none of its connections are ones that have been
186                 // created by, and therefore being tracked by, this remote connection service. It
187                 // is of no interest to us.
188                 return;
189             }
190
191             conference.setState(parcel.getState());
192             conference.setCallCapabilities(parcel.getCapabilities());
193             mConferenceById.put(callId, conference);
194             conference.addCallback(new RemoteConference.Callback() {
195                 @Override
196                 public void onDestroyed(RemoteConference c) {
197                     mConferenceById.remove(callId);
198                     maybeDisconnectAdapter();
199                 }
200             });
201
202             mOurConnectionServiceImpl.addRemoteConference(conference);
203         }
204
205         @Override
206         public void removeCall(String callId) {
207             if (mConnectionById.containsKey(callId)) {
208                 findConnectionForAction(callId, "removeCall")
209                         .setDestroyed();
210             } else {
211                 findConferenceForAction(callId, "removeCall")
212                         .setDestroyed();
213             }
214         }
215
216         @Override
217         public void onPostDialWait(String callId, String remaining) {
218             findConnectionForAction(callId, "onPostDialWait")
219                     .setPostDialWait(remaining);
220         }
221
222         @Override
223         public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
224             // Not supported from remote connection service.
225         }
226
227         @Override
228         public void setVideoProvider(String callId, IVideoProvider videoProvider) {
229             // not supported for remote connections.
230         }
231
232         @Override
233         public void setVideoState(String callId, int videoState) {
234             findConnectionForAction(callId, "setVideoState")
235                     .setVideoState(videoState);
236         }
237
238         @Override
239         public void setAudioModeIsVoip(String callId, boolean isVoip) {
240             findConnectionForAction(callId, "setAudioModeIsVoip")
241                     .setAudioModeIsVoip(isVoip);
242         }
243
244         @Override
245         public void setStatusHints(String callId, StatusHints statusHints) {
246             findConnectionForAction(callId, "setStatusHints")
247                     .setStatusHints(statusHints);
248         }
249
250         @Override
251         public void setHandle(String callId, Uri handle, int presentation) {
252             findConnectionForAction(callId, "setHandle")
253                     .setHandle(handle, presentation);
254         }
255
256         @Override
257         public void setCallerDisplayName(String callId, String callerDisplayName,
258                 int presentation) {
259             findConnectionForAction(callId, "setCallerDisplayName")
260                     .setCallerDisplayName(callerDisplayName, presentation);
261         }
262
263         @Override
264         public IBinder asBinder() {
265             throw new UnsupportedOperationException();
266         }
267
268         @Override
269         public final void setConferenceableConnections(
270                 String callId, List<String> conferenceableConnectionIds) {
271             List<RemoteConnection> conferenceable = new ArrayList<>();
272             for (String id : conferenceableConnectionIds) {
273                 if (mConnectionById.containsKey(id)) {
274                     conferenceable.add(mConnectionById.get(id));
275                 }
276             }
277
278             findConnectionForAction(callId, "setConferenceableConnections")
279                     .setConferenceableConnections(conferenceable);
280         }
281     };
282
283     private final ConnectionServiceAdapterServant mServant =
284             new ConnectionServiceAdapterServant(mServantDelegate);
285
286     private final DeathRecipient mDeathRecipient = new DeathRecipient() {
287         @Override
288         public void binderDied() {
289             for (RemoteConnection c : mConnectionById.values()) {
290                 c.setDestroyed();
291             }
292             for (RemoteConference c : mConferenceById.values()) {
293                 c.setDestroyed();
294             }
295             mConnectionById.clear();
296             mConferenceById.clear();
297             mPendingConnections.clear();
298             mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0);
299         }
300     };
301
302     private final IConnectionService mOutgoingConnectionServiceRpc;
303     private final ConnectionService mOurConnectionServiceImpl;
304     private final Map<String, RemoteConnection> mConnectionById = new HashMap<>();
305     private final Map<String, RemoteConference> mConferenceById = new HashMap<>();
306     private final Set<RemoteConnection> mPendingConnections = new HashSet<>();
307
308     RemoteConnectionService(
309             IConnectionService outgoingConnectionServiceRpc,
310             ConnectionService ourConnectionServiceImpl) throws RemoteException {
311         mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc;
312         mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0);
313         mOurConnectionServiceImpl = ourConnectionServiceImpl;
314     }
315
316     @Override
317     public String toString() {
318         return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]";
319     }
320
321     final RemoteConnection createRemoteConnection(
322             PhoneAccountHandle connectionManagerPhoneAccount,
323             ConnectionRequest request,
324             boolean isIncoming) {
325         final String id = UUID.randomUUID().toString();
326         final ConnectionRequest newRequest = new ConnectionRequest(
327                 request.getAccountHandle(),
328                 request.getHandle(),
329                 request.getHandlePresentation(),
330                 request.getExtras(),
331                 request.getVideoState());
332         try {
333             if (mConnectionById.isEmpty()) {
334                 mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub());
335             }
336             RemoteConnection connection =
337                     new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest);
338             mPendingConnections.add(connection);
339             mConnectionById.put(id, connection);
340             mOutgoingConnectionServiceRpc.createConnection(
341                     connectionManagerPhoneAccount,
342                     id,
343                     newRequest,
344                     isIncoming);
345             connection.addListener(new RemoteConnection.Listener() {
346                 @Override
347                 public void onDestroyed(RemoteConnection connection) {
348                     mConnectionById.remove(id);
349                     maybeDisconnectAdapter();
350                 }
351             });
352             return connection;
353         } catch (RemoteException e) {
354             return RemoteConnection
355                     .failure(DisconnectCause.ERROR_UNSPECIFIED, e.toString());
356         }
357     }
358
359     private RemoteConnection findConnectionForAction(
360             String callId, String action) {
361         if (mConnectionById.containsKey(callId)) {
362             return mConnectionById.get(callId);
363         }
364         Log.w(this, "%s - Cannot find Connection %s", action, callId);
365         return NULL_CONNECTION;
366     }
367
368     private RemoteConference findConferenceForAction(
369             String callId, String action) {
370         if (mConferenceById.containsKey(callId)) {
371             return mConferenceById.get(callId);
372         }
373         Log.w(this, "%s - Cannot find Conference %s", action, callId);
374         return NULL_CONFERENCE;
375     }
376
377     private void maybeDisconnectAdapter() {
378         if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) {
379             try {
380                 mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub());
381             } catch (RemoteException e) {
382             }
383         }
384     }
385 }