1 /*
2  * Copyright 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 com.android.server.telecom;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.IBinder;
25 import android.os.Message;
26 import android.os.RemoteException;
27 import android.os.UserHandle;
28 import android.telecom.AudioState;
29 import android.telecom.Connection;
30 import android.telecom.ConnectionRequest;
31 import android.telecom.ConnectionService;
32 import android.telecom.DisconnectCause;
33 import android.telecom.GatewayInfo;
34 import android.telecom.ParcelableConference;
35 import android.telecom.ParcelableConnection;
36 import android.telecom.PhoneAccount;
37 import android.telecom.PhoneAccountHandle;
38 import android.telecom.StatusHints;
39 import android.telecom.TelecomManager;
40 import android.telecom.VideoProfile;
41 
42 import com.android.internal.os.SomeArgs;
43 import com.android.internal.telecom.IConnectionService;
44 import com.android.internal.telecom.IConnectionServiceAdapter;
45 import com.android.internal.telecom.IVideoProvider;
46 import com.android.internal.telecom.RemoteServiceCallback;
47 import com.android.internal.util.Preconditions;
48 
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Set;
55 import java.util.concurrent.ConcurrentHashMap;
56 
57 /**
58  * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
59  * track of when the object can safely be unbound. Other classes should not use
60  * {@link IConnectionService} directly and instead should use this class to invoke methods of
61  * {@link IConnectionService}.
62  */
63 final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {
64     private static final int MSG_HANDLE_CREATE_CONNECTION_COMPLETE = 1;
65     private static final int MSG_SET_ACTIVE = 2;
66     private static final int MSG_SET_RINGING = 3;
67     private static final int MSG_SET_DIALING = 4;
68     private static final int MSG_SET_DISCONNECTED = 5;
69     private static final int MSG_SET_ON_HOLD = 6;
70     private static final int MSG_SET_RINGBACK_REQUESTED = 7;
71     private static final int MSG_SET_CONNECTION_CAPABILITIES = 8;
72     private static final int MSG_SET_IS_CONFERENCED = 9;
73     private static final int MSG_ADD_CONFERENCE_CALL = 10;
74     private static final int MSG_REMOVE_CALL = 11;
75     private static final int MSG_ON_POST_DIAL_WAIT = 12;
76     private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 13;
77     private static final int MSG_SET_VIDEO_PROVIDER = 14;
78     private static final int MSG_SET_IS_VOIP_AUDIO_MODE = 15;
79     private static final int MSG_SET_STATUS_HINTS = 16;
80     private static final int MSG_SET_ADDRESS = 17;
81     private static final int MSG_SET_CALLER_DISPLAY_NAME = 18;
82     private static final int MSG_SET_VIDEO_STATE = 19;
83     private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20;
84     private static final int MSG_ADD_EXISTING_CONNECTION = 21;
85     private static final int MSG_ON_POST_DIAL_CHAR = 22;
86 
87     private final Handler mHandler = new Handler() {
88         @Override
89         public void handleMessage(Message msg) {
90             Call call;
91             switch (msg.what) {
92                 case MSG_HANDLE_CREATE_CONNECTION_COMPLETE: {
93                     SomeArgs args = (SomeArgs) msg.obj;
94                     try {
95                         String callId = (String) args.arg1;
96                         ConnectionRequest request = (ConnectionRequest) args.arg2;
97                         ParcelableConnection connection = (ParcelableConnection) args.arg3;
98                         handleCreateConnectionComplete(callId, request, connection);
99                     } finally {
100                         args.recycle();
101                     }
102                     break;
103                 }
104                 case MSG_SET_ACTIVE:
105                     call = mCallIdMapper.getCall(msg.obj);
106                     if (call != null) {
107                         mCallsManager.markCallAsActive(call);
108                     } else {
109                         //Log.w(this, "setActive, unknown call id: %s", msg.obj);
110                     }
111                     break;
112                 case MSG_SET_RINGING:
113                     call = mCallIdMapper.getCall(msg.obj);
114                     if (call != null) {
115                         mCallsManager.markCallAsRinging(call);
116                     } else {
117                         //Log.w(this, "setRinging, unknown call id: %s", msg.obj);
118                     }
119                     break;
120                 case MSG_SET_DIALING:
121                     call = mCallIdMapper.getCall(msg.obj);
122                     if (call != null) {
123                         mCallsManager.markCallAsDialing(call);
124                     } else {
125                         //Log.w(this, "setDialing, unknown call id: %s", msg.obj);
126                     }
127                     break;
128                 case MSG_SET_DISCONNECTED: {
129                     SomeArgs args = (SomeArgs) msg.obj;
130                     try {
131                         call = mCallIdMapper.getCall(args.arg1);
132                         DisconnectCause disconnectCause = (DisconnectCause) args.arg2;
133                         Log.d(this, "disconnect call %s %s", disconnectCause, call);
134                         if (call != null) {
135                             mCallsManager.markCallAsDisconnected(call, disconnectCause);
136                         } else {
137                             //Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
138                         }
139                     } finally {
140                         args.recycle();
141                     }
142                     break;
143                 }
144                 case MSG_SET_ON_HOLD:
145                     call = mCallIdMapper.getCall(msg.obj);
146                     if (call != null) {
147                         mCallsManager.markCallAsOnHold(call);
148                     } else {
149                         //Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
150                     }
151                     break;
152                 case MSG_SET_RINGBACK_REQUESTED: {
153                     call = mCallIdMapper.getCall(msg.obj);
154                     if (call != null) {
155                         call.setRingbackRequested(msg.arg1 == 1);
156                     } else {
157                         //Log.w(this, "setRingback, unknown call id: %s", args.arg1);
158                     }
159                     break;
160                 }
161                 case MSG_SET_CONNECTION_CAPABILITIES: {
162                     call = mCallIdMapper.getCall(msg.obj);
163                     if (call != null) {
164                         call.setConnectionCapabilities(msg.arg1);
165                     } else {
166                         //Log.w(ConnectionServiceWrapper.this,
167                         //      "setConnectionCapabilities, unknown call id: %s", msg.obj);
168                     }
169                     break;
170                 }
171                 case MSG_SET_IS_CONFERENCED: {
172                     SomeArgs args = (SomeArgs) msg.obj;
173                     try {
174                         Call childCall = mCallIdMapper.getCall(args.arg1);
175                         Log.d(this, "SET_IS_CONFERENCE: %s %s", args.arg1, args.arg2);
176                         if (childCall != null) {
177                             String conferenceCallId = (String) args.arg2;
178                             if (conferenceCallId == null) {
179                                 Log.d(this, "unsetting parent: %s", args.arg1);
180                                 childCall.setParentCall(null);
181                             } else {
182                                 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
183                                 childCall.setParentCall(conferenceCall);
184                             }
185                         } else {
186                             //Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
187                         }
188                     } finally {
189                         args.recycle();
190                     }
191                     break;
192                 }
193                 case MSG_ADD_CONFERENCE_CALL: {
194                     SomeArgs args = (SomeArgs) msg.obj;
195                     try {
196                         String id = (String) args.arg1;
197                         if (mCallIdMapper.getCall(id) != null) {
198                             Log.w(this, "Attempting to add a conference call using an existing " +
199                                     "call id %s", id);
200                             break;
201                         }
202                         ParcelableConference parcelableConference =
203                                 (ParcelableConference) args.arg2;
204 
205                         // Make sure that there's at least one valid call. For remote connections
206                         // we'll get a add conference msg from both the remote connection service
207                         // and from the real connection service.
208                         boolean hasValidCalls = false;
209                         for (String callId : parcelableConference.getConnectionIds()) {
210                             if (mCallIdMapper.getCall(callId) != null) {
211                                 hasValidCalls = true;
212                             }
213                         }
214                         // But don't bail out if the connection count is 0, because that is a valid
215                         // IMS conference state.
216                         if (!hasValidCalls && parcelableConference.getConnectionIds().size() > 0) {
217                             Log.d(this, "Attempting to add a conference with no valid calls");
218                             break;
219                         }
220 
221                         // need to create a new Call
222                         PhoneAccountHandle phAcc = null;
223                         if (parcelableConference != null &&
224                                 parcelableConference.getPhoneAccount() != null) {
225                             phAcc = parcelableConference.getPhoneAccount();
226                         }
227                         Call conferenceCall = mCallsManager.createConferenceCall(
228                                 phAcc, parcelableConference);
229                         mCallIdMapper.addCall(conferenceCall, id);
230                         conferenceCall.setConnectionService(ConnectionServiceWrapper.this);
231 
232                         Log.d(this, "adding children to conference %s phAcc %s",
233                                 parcelableConference.getConnectionIds(), phAcc);
234                         for (String callId : parcelableConference.getConnectionIds()) {
235                             Call childCall = mCallIdMapper.getCall(callId);
236                             Log.d(this, "found child: %s", callId);
237                             if (childCall != null) {
238                                 childCall.setParentCall(conferenceCall);
239                             }
240                         }
241                     } finally {
242                         args.recycle();
243                     }
244                     break;
245                 }
246                 case MSG_REMOVE_CALL: {
247                     call = mCallIdMapper.getCall(msg.obj);
248                     if (call != null) {
249                         if (call.isAlive()) {
250                             mCallsManager.markCallAsDisconnected(
251                                     call, new DisconnectCause(DisconnectCause.REMOTE));
252                         } else {
253                             mCallsManager.markCallAsRemoved(call);
254                         }
255                     }
256                     break;
257                 }
258                 case MSG_ON_POST_DIAL_WAIT: {
259                     SomeArgs args = (SomeArgs) msg.obj;
260                     try {
261                         call = mCallIdMapper.getCall(args.arg1);
262                         if (call != null) {
263                             String remaining = (String) args.arg2;
264                             call.onPostDialWait(remaining);
265                         } else {
266                             //Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
267                         }
268                     } finally {
269                         args.recycle();
270                     }
271                     break;
272                 }
273                 case MSG_ON_POST_DIAL_CHAR: {
274                     SomeArgs args = (SomeArgs) msg.obj;
275                     try {
276                         call = mCallIdMapper.getCall(args.arg1);
277                         if (call != null) {
278                             char nextChar = (char) args.argi1;
279                             call.onPostDialChar(nextChar);
280                         } else {
281                             //Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
282                         }
283                     } finally {
284                         args.recycle();
285                     }
286                     break;
287                 }
288                 case MSG_QUERY_REMOTE_CALL_SERVICES: {
289                     queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
290                     break;
291                 }
292                 case MSG_SET_VIDEO_PROVIDER: {
293                     SomeArgs args = (SomeArgs) msg.obj;
294                     try {
295                         call = mCallIdMapper.getCall(args.arg1);
296                         IVideoProvider videoProvider = (IVideoProvider) args.arg2;
297                         if (call != null) {
298                             call.setVideoProvider(videoProvider);
299                         }
300                     } finally {
301                         args.recycle();
302                     }
303                     break;
304                 }
305                 case MSG_SET_IS_VOIP_AUDIO_MODE: {
306                     call = mCallIdMapper.getCall(msg.obj);
307                     if (call != null) {
308                         call.setIsVoipAudioMode(msg.arg1 == 1);
309                     }
310                     break;
311                 }
312                 case MSG_SET_STATUS_HINTS: {
313                     SomeArgs args = (SomeArgs) msg.obj;
314                     try {
315                         call = mCallIdMapper.getCall(args.arg1);
316                         StatusHints statusHints = (StatusHints) args.arg2;
317                         if (call != null) {
318                             call.setStatusHints(statusHints);
319                         }
320                     } finally {
321                         args.recycle();
322                     }
323                     break;
324                 }
325                 case MSG_SET_ADDRESS: {
326                     SomeArgs args = (SomeArgs) msg.obj;
327                     try {
328                         call = mCallIdMapper.getCall(args.arg1);
329                         if (call != null) {
330                             call.setHandle((Uri) args.arg2, args.argi1);
331                         }
332                     } finally {
333                         args.recycle();
334                     }
335                     break;
336                 }
337                 case MSG_SET_CALLER_DISPLAY_NAME: {
338                     SomeArgs args = (SomeArgs) msg.obj;
339                     try {
340                         call = mCallIdMapper.getCall(args.arg1);
341                         if (call != null) {
342                             call.setCallerDisplayName((String) args.arg2, args.argi1);
343                         }
344                     } finally {
345                         args.recycle();
346                     }
347                     break;
348                 }
349                 case MSG_SET_VIDEO_STATE: {
350                     call = mCallIdMapper.getCall(msg.obj);
351                     if (call != null) {
352                         call.setVideoState(msg.arg1);
353                     }
354                     break;
355                 }
356                 case MSG_SET_CONFERENCEABLE_CONNECTIONS: {
357                     SomeArgs args = (SomeArgs) msg.obj;
358                     try {
359                         call = mCallIdMapper.getCall(args.arg1);
360                         if (call != null ){
361                             @SuppressWarnings("unchecked")
362                             List<String> conferenceableIds = (List<String>) args.arg2;
363                             List<Call> conferenceableCalls =
364                                     new ArrayList<>(conferenceableIds.size());
365                             for (String otherId : (List<String>) args.arg2) {
366                                 Call otherCall = mCallIdMapper.getCall(otherId);
367                                 if (otherCall != null && otherCall != call) {
368                                     conferenceableCalls.add(otherCall);
369                                 }
370                             }
371                             call.setConferenceableCalls(conferenceableCalls);
372                         }
373                     } finally {
374                         args.recycle();
375                     }
376                     break;
377                 }
378                 case MSG_ADD_EXISTING_CONNECTION: {
379                     SomeArgs args = (SomeArgs) msg.obj;
380                     try {
381                         String callId = (String)args.arg1;
382                         ParcelableConnection connection = (ParcelableConnection)args.arg2;
383                         Call existingCall = mCallsManager.createCallForExistingConnection(callId,
384                                 connection);
385                         mCallIdMapper.addCall(existingCall, callId);
386                         existingCall.setConnectionService(ConnectionServiceWrapper.this);
387                     } finally {
388                         args.recycle();
389                     }
390                 }
391             }
392         }
393     };
394 
395     private final class Adapter extends IConnectionServiceAdapter.Stub {
396 
397         @Override
handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection)398         public void handleCreateConnectionComplete(
399                 String callId,
400                 ConnectionRequest request,
401                 ParcelableConnection connection) {
402             logIncoming("handleCreateConnectionComplete %s", request);
403             if (mCallIdMapper.isValidCallId(callId)) {
404                 SomeArgs args = SomeArgs.obtain();
405                 args.arg1 = callId;
406                 args.arg2 = request;
407                 args.arg3 = connection;
408                 mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_COMPLETE, args)
409                         .sendToTarget();
410             }
411         }
412 
413         @Override
setActive(String callId)414         public void setActive(String callId) {
415             logIncoming("setActive %s", callId);
416             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
417                 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
418             }
419         }
420 
421         @Override
setRinging(String callId)422         public void setRinging(String callId) {
423             logIncoming("setRinging %s", callId);
424             if (mCallIdMapper.isValidCallId(callId)) {
425                 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
426             }
427         }
428 
429         @Override
setVideoProvider(String callId, IVideoProvider videoProvider)430         public void setVideoProvider(String callId, IVideoProvider videoProvider) {
431             logIncoming("setVideoProvider %s", callId);
432             if (mCallIdMapper.isValidCallId(callId)) {
433                 SomeArgs args = SomeArgs.obtain();
434                 args.arg1 = callId;
435                 args.arg2 = videoProvider;
436                 mHandler.obtainMessage(MSG_SET_VIDEO_PROVIDER, args).sendToTarget();
437             }
438         }
439 
440         @Override
setDialing(String callId)441         public void setDialing(String callId) {
442             logIncoming("setDialing %s", callId);
443             if (mCallIdMapper.isValidCallId(callId)) {
444                 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
445             }
446         }
447 
448         @Override
setDisconnected(String callId, DisconnectCause disconnectCause)449         public void setDisconnected(String callId, DisconnectCause disconnectCause) {
450             logIncoming("setDisconnected %s %s", callId, disconnectCause);
451             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
452                 Log.d(this, "disconnect call %s", callId);
453                 SomeArgs args = SomeArgs.obtain();
454                 args.arg1 = callId;
455                 args.arg2 = disconnectCause;
456                 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
457             }
458         }
459 
460         @Override
setOnHold(String callId)461         public void setOnHold(String callId) {
462             logIncoming("setOnHold %s", callId);
463             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
464                 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
465             }
466         }
467 
468         @Override
setRingbackRequested(String callId, boolean ringback)469         public void setRingbackRequested(String callId, boolean ringback) {
470             logIncoming("setRingbackRequested %s %b", callId, ringback);
471             if (mCallIdMapper.isValidCallId(callId)) {
472                 mHandler.obtainMessage(MSG_SET_RINGBACK_REQUESTED, ringback ? 1 : 0, 0, callId)
473                         .sendToTarget();
474             }
475         }
476 
477         @Override
removeCall(String callId)478         public void removeCall(String callId) {
479             logIncoming("removeCall %s", callId);
480             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
481                 mHandler.obtainMessage(MSG_REMOVE_CALL, callId).sendToTarget();
482             }
483         }
484 
485         @Override
setConnectionCapabilities(String callId, int connectionCapabilities)486         public void setConnectionCapabilities(String callId, int connectionCapabilities) {
487             logIncoming("setConnectionCapabilities %s %d", callId, connectionCapabilities);
488             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
489                 mHandler.obtainMessage(MSG_SET_CONNECTION_CAPABILITIES, connectionCapabilities, 0, callId)
490                         .sendToTarget();
491             } else {
492                 Log.w(this, "ID not valid for setCallCapabilities");
493             }
494         }
495 
496         @Override
setIsConferenced(String callId, String conferenceCallId)497         public void setIsConferenced(String callId, String conferenceCallId) {
498             logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
499             SomeArgs args = SomeArgs.obtain();
500             args.arg1 = callId;
501             args.arg2 = conferenceCallId;
502             mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
503         }
504 
505         @Override
addConferenceCall(String callId, ParcelableConference parcelableConference)506         public void addConferenceCall(String callId, ParcelableConference parcelableConference) {
507             logIncoming("addConferenceCall %s %s", callId, parcelableConference);
508             // We do not check call Ids here because we do not yet know the call ID for new
509             // conference calls.
510             SomeArgs args = SomeArgs.obtain();
511             args.arg1 = callId;
512             args.arg2 = parcelableConference;
513             mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, args).sendToTarget();
514         }
515 
516         @Override
onPostDialWait(String callId, String remaining)517         public void onPostDialWait(String callId, String remaining) throws RemoteException {
518             logIncoming("onPostDialWait %s %s", callId, remaining);
519             if (mCallIdMapper.isValidCallId(callId)) {
520                 SomeArgs args = SomeArgs.obtain();
521                 args.arg1 = callId;
522                 args.arg2 = remaining;
523                 mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget();
524             }
525         }
526 
527         @Override
onPostDialChar(String callId, char nextChar)528         public void onPostDialChar(String callId, char nextChar) throws RemoteException {
529             logIncoming("onPostDialChar %s %s", callId, nextChar);
530             if (mCallIdMapper.isValidCallId(callId)) {
531                 SomeArgs args = SomeArgs.obtain();
532                 args.arg1 = callId;
533                 args.argi1 = nextChar;
534                 mHandler.obtainMessage(MSG_ON_POST_DIAL_CHAR, args).sendToTarget();
535             }
536         }
537 
538         @Override
queryRemoteConnectionServices(RemoteServiceCallback callback)539         public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
540             logIncoming("queryRemoteCSs");
541             mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
542         }
543 
544         @Override
setVideoState(String callId, int videoState)545         public void setVideoState(String callId, int videoState) {
546             logIncoming("setVideoState %s %d", callId, videoState);
547             if (mCallIdMapper.isValidCallId(callId)) {
548                 mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, callId).sendToTarget();
549             }
550         }
551 
552         @Override
setIsVoipAudioMode(String callId, boolean isVoip)553         public void setIsVoipAudioMode(String callId, boolean isVoip) {
554             logIncoming("setIsVoipAudioMode %s %b", callId, isVoip);
555             if (mCallIdMapper.isValidCallId(callId)) {
556                 mHandler.obtainMessage(MSG_SET_IS_VOIP_AUDIO_MODE, isVoip ? 1 : 0, 0,
557                         callId).sendToTarget();
558             }
559         }
560 
561         @Override
setStatusHints(String callId, StatusHints statusHints)562         public void setStatusHints(String callId, StatusHints statusHints) {
563             logIncoming("setStatusHints %s %s", callId, statusHints);
564             if (mCallIdMapper.isValidCallId(callId)) {
565                 SomeArgs args = SomeArgs.obtain();
566                 args.arg1 = callId;
567                 args.arg2 = statusHints;
568                 mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget();
569             }
570         }
571 
572         @Override
setAddress(String callId, Uri address, int presentation)573         public void setAddress(String callId, Uri address, int presentation) {
574             logIncoming("setAddress %s %s %d", callId, address, presentation);
575             if (mCallIdMapper.isValidCallId(callId)) {
576                 SomeArgs args = SomeArgs.obtain();
577                 args.arg1 = callId;
578                 args.arg2 = address;
579                 args.argi1 = presentation;
580                 mHandler.obtainMessage(MSG_SET_ADDRESS, args).sendToTarget();
581             }
582         }
583 
584         @Override
setCallerDisplayName( String callId, String callerDisplayName, int presentation)585         public void setCallerDisplayName(
586                 String callId, String callerDisplayName, int presentation) {
587             logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName, presentation);
588             if (mCallIdMapper.isValidCallId(callId)) {
589                 SomeArgs args = SomeArgs.obtain();
590                 args.arg1 = callId;
591                 args.arg2 = callerDisplayName;
592                 args.argi1 = presentation;
593                 mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget();
594             }
595         }
596 
597         @Override
setConferenceableConnections( String callId, List<String> conferenceableCallIds)598         public void setConferenceableConnections(
599                 String callId, List<String> conferenceableCallIds) {
600             logIncoming("setConferenceableConnections %s %s", callId, conferenceableCallIds);
601             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
602                 SomeArgs args = SomeArgs.obtain();
603                 args.arg1 = callId;
604                 args.arg2 = conferenceableCallIds;
605                 mHandler.obtainMessage(MSG_SET_CONFERENCEABLE_CONNECTIONS, args).sendToTarget();
606             }
607         }
608 
609         @Override
addExistingConnection(String callId, ParcelableConnection connection)610         public void addExistingConnection(String callId, ParcelableConnection connection) {
611             logIncoming("addExistingConnection  %s %s", callId, connection);
612             SomeArgs args = SomeArgs.obtain();
613             args.arg1 = callId;
614             args.arg2 = connection;
615             mHandler.obtainMessage(MSG_ADD_EXISTING_CONNECTION, args).sendToTarget();
616         }
617     }
618 
619     private final Adapter mAdapter = new Adapter();
620     private final CallsManager mCallsManager = CallsManager.getInstance();
621     /**
622      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
623      * load factor before resizing, 1 means we only expect a single thread to
624      * access the map so make only a single shard
625      */
626     private final Set<Call> mPendingConferenceCalls = Collections.newSetFromMap(
627             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
628     private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService");
629     private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
630 
631     private Binder mBinder = new Binder();
632     private IConnectionService mServiceInterface;
633     private final ConnectionServiceRepository mConnectionServiceRepository;
634     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
635 
636     /**
637      * Creates a connection service.
638      *
639      * @param componentName The component name of the service with which to bind.
640      * @param connectionServiceRepository Connection service repository.
641      * @param phoneAccountRegistrar Phone account registrar
642      * @param context The context.
643      * @param userHandle The {@link UserHandle} to use when binding.
644      */
ConnectionServiceWrapper( ComponentName componentName, ConnectionServiceRepository connectionServiceRepository, PhoneAccountRegistrar phoneAccountRegistrar, Context context, UserHandle userHandle)645     ConnectionServiceWrapper(
646             ComponentName componentName,
647             ConnectionServiceRepository connectionServiceRepository,
648             PhoneAccountRegistrar phoneAccountRegistrar,
649             Context context,
650             UserHandle userHandle) {
651         super(ConnectionService.SERVICE_INTERFACE, componentName, context, userHandle);
652         mConnectionServiceRepository = connectionServiceRepository;
653         phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
654             // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
655             // To do this, we must proxy remote ConnectionService objects
656         });
657         mPhoneAccountRegistrar = phoneAccountRegistrar;
658     }
659 
660     /** See {@link IConnectionService#addConnectionServiceAdapter}. */
addConnectionServiceAdapter(IConnectionServiceAdapter adapter)661     private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
662         if (isServiceValid("addConnectionServiceAdapter")) {
663             try {
664                 logOutgoing("addConnectionServiceAdapter %s", adapter);
665                 mServiceInterface.addConnectionServiceAdapter(adapter);
666             } catch (RemoteException e) {
667             }
668         }
669     }
670 
671     /**
672      * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
673      */
createConnection(final Call call, final CreateConnectionResponse response)674     void createConnection(final Call call, final CreateConnectionResponse response) {
675         Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
676         BindCallback callback = new BindCallback() {
677             @Override
678             public void onSuccess() {
679                 String callId = mCallIdMapper.getCallId(call);
680                 mPendingResponses.put(callId, response);
681 
682                 GatewayInfo gatewayInfo = call.getGatewayInfo();
683                 Bundle extras = call.getExtras();
684                 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
685                         gatewayInfo.getOriginalAddress() != null) {
686                     extras = (Bundle) extras.clone();
687                     extras.putString(
688                             TelecomManager.GATEWAY_PROVIDER_PACKAGE,
689                             gatewayInfo.getGatewayProviderPackageName());
690                     extras.putParcelable(
691                             TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
692                             gatewayInfo.getOriginalAddress());
693                 }
694 
695                 try {
696                     mServiceInterface.createConnection(
697                             call.getConnectionManagerPhoneAccount(),
698                             callId,
699                             new ConnectionRequest(
700                                     call.getTargetPhoneAccount(),
701                                     call.getHandle(),
702                                     extras,
703                                     call.getVideoState()),
704                             call.isIncoming(),
705                             call.isUnknown());
706                 } catch (RemoteException e) {
707                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
708                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
709                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
710                 }
711             }
712 
713             @Override
714             public void onFailure() {
715                 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
716                 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
717             }
718         };
719 
720         mBinder.bind(callback);
721     }
722 
723     /** @see ConnectionService#abort(String) */
abort(Call call)724     void abort(Call call) {
725         // Clear out any pending outgoing call data
726         final String callId = mCallIdMapper.getCallId(call);
727 
728         // If still bound, tell the connection service to abort.
729         if (callId != null && isServiceValid("abort")) {
730             try {
731                 logOutgoing("abort %s", callId);
732                 mServiceInterface.abort(callId);
733             } catch (RemoteException e) {
734             }
735         }
736 
737         removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
738     }
739 
740     /** @see ConnectionService#hold(String) */
hold(Call call)741     void hold(Call call) {
742         final String callId = mCallIdMapper.getCallId(call);
743         if (callId != null && isServiceValid("hold")) {
744             try {
745                 logOutgoing("hold %s", callId);
746                 mServiceInterface.hold(callId);
747             } catch (RemoteException e) {
748             }
749         }
750     }
751 
752     /** @see ConnectionService#unhold(String) */
unhold(Call call)753     void unhold(Call call) {
754         final String callId = mCallIdMapper.getCallId(call);
755         if (callId != null && isServiceValid("unhold")) {
756             try {
757                 logOutgoing("unhold %s", callId);
758                 mServiceInterface.unhold(callId);
759             } catch (RemoteException e) {
760             }
761         }
762     }
763 
764     /** @see ConnectionService#onAudioStateChanged(String,AudioState) */
onAudioStateChanged(Call activeCall, AudioState audioState)765     void onAudioStateChanged(Call activeCall, AudioState audioState) {
766         final String callId = mCallIdMapper.getCallId(activeCall);
767         if (callId != null && isServiceValid("onAudioStateChanged")) {
768             try {
769                 logOutgoing("onAudioStateChanged %s %s", callId, audioState);
770                 mServiceInterface.onAudioStateChanged(callId, audioState);
771             } catch (RemoteException e) {
772             }
773         }
774     }
775 
776     /** @see ConnectionService#disconnect(String) */
disconnect(Call call)777     void disconnect(Call call) {
778         final String callId = mCallIdMapper.getCallId(call);
779         if (callId != null && isServiceValid("disconnect")) {
780             try {
781                 logOutgoing("disconnect %s", callId);
782                 mServiceInterface.disconnect(callId);
783             } catch (RemoteException e) {
784             }
785         }
786     }
787 
788     /** @see ConnectionService#answer(String,int) */
answer(Call call, int videoState)789     void answer(Call call, int videoState) {
790         final String callId = mCallIdMapper.getCallId(call);
791         if (callId != null && isServiceValid("answer")) {
792             try {
793                 logOutgoing("answer %s %d", callId, videoState);
794                 if (videoState == VideoProfile.VideoState.AUDIO_ONLY) {
795                     mServiceInterface.answer(callId);
796                 } else {
797                     mServiceInterface.answerVideo(callId, videoState);
798                 }
799             } catch (RemoteException e) {
800             }
801         }
802     }
803 
804     /** @see ConnectionService#reject(String) */
reject(Call call)805     void reject(Call call) {
806         final String callId = mCallIdMapper.getCallId(call);
807         if (callId != null && isServiceValid("reject")) {
808             try {
809                 logOutgoing("reject %s", callId);
810                 mServiceInterface.reject(callId);
811             } catch (RemoteException e) {
812             }
813         }
814     }
815 
816     /** @see ConnectionService#playDtmfTone(String,char) */
playDtmfTone(Call call, char digit)817     void playDtmfTone(Call call, char digit) {
818         final String callId = mCallIdMapper.getCallId(call);
819         if (callId != null && isServiceValid("playDtmfTone")) {
820             try {
821                 logOutgoing("playDtmfTone %s %c", callId, digit);
822                 mServiceInterface.playDtmfTone(callId, digit);
823             } catch (RemoteException e) {
824             }
825         }
826     }
827 
828     /** @see ConnectionService#stopDtmfTone(String) */
stopDtmfTone(Call call)829     void stopDtmfTone(Call call) {
830         final String callId = mCallIdMapper.getCallId(call);
831         if (callId != null && isServiceValid("stopDtmfTone")) {
832             try {
833                 logOutgoing("stopDtmfTone %s",callId);
834                 mServiceInterface.stopDtmfTone(callId);
835             } catch (RemoteException e) {
836             }
837         }
838     }
839 
addCall(Call call)840     void addCall(Call call) {
841         if (mCallIdMapper.getCallId(call) == null) {
842             mCallIdMapper.addCall(call);
843         }
844     }
845 
846     /**
847      * Associates newCall with this connection service by replacing callToReplace.
848      */
replaceCall(Call newCall, Call callToReplace)849     void replaceCall(Call newCall, Call callToReplace) {
850         Preconditions.checkState(callToReplace.getConnectionService() == this);
851         mCallIdMapper.replaceCall(newCall, callToReplace);
852     }
853 
removeCall(Call call)854     void removeCall(Call call) {
855         removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
856     }
857 
removeCall(String callId, DisconnectCause disconnectCause)858     void removeCall(String callId, DisconnectCause disconnectCause) {
859         CreateConnectionResponse response = mPendingResponses.remove(callId);
860         if (response != null) {
861             response.handleCreateConnectionFailure(disconnectCause);
862         }
863 
864         mCallIdMapper.removeCall(callId);
865     }
866 
removeCall(Call call, DisconnectCause disconnectCause)867     void removeCall(Call call, DisconnectCause disconnectCause) {
868         CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
869         if (response != null) {
870             response.handleCreateConnectionFailure(disconnectCause);
871         }
872 
873         mCallIdMapper.removeCall(call);
874     }
875 
onPostDialContinue(Call call, boolean proceed)876     void onPostDialContinue(Call call, boolean proceed) {
877         final String callId = mCallIdMapper.getCallId(call);
878         if (callId != null && isServiceValid("onPostDialContinue")) {
879             try {
880                 logOutgoing("onPostDialContinue %s %b", callId, proceed);
881                 mServiceInterface.onPostDialContinue(callId, proceed);
882             } catch (RemoteException ignored) {
883             }
884         }
885     }
886 
conference(final Call call, Call otherCall)887     void conference(final Call call, Call otherCall) {
888         final String callId = mCallIdMapper.getCallId(call);
889         final String otherCallId = mCallIdMapper.getCallId(otherCall);
890         if (callId != null && otherCallId != null && isServiceValid("conference")) {
891             try {
892                 logOutgoing("conference %s %s", callId, otherCallId);
893                 mServiceInterface.conference(callId, otherCallId);
894             } catch (RemoteException ignored) {
895             }
896         }
897     }
898 
splitFromConference(Call call)899     void splitFromConference(Call call) {
900         final String callId = mCallIdMapper.getCallId(call);
901         if (callId != null && isServiceValid("splitFromConference")) {
902             try {
903                 logOutgoing("splitFromConference %s", callId);
904                 mServiceInterface.splitFromConference(callId);
905             } catch (RemoteException ignored) {
906             }
907         }
908     }
909 
mergeConference(Call call)910     void mergeConference(Call call) {
911         final String callId = mCallIdMapper.getCallId(call);
912         if (callId != null && isServiceValid("mergeConference")) {
913             try {
914                 logOutgoing("mergeConference %s", callId);
915                 mServiceInterface.mergeConference(callId);
916             } catch (RemoteException ignored) {
917             }
918         }
919     }
920 
swapConference(Call call)921     void swapConference(Call call) {
922         final String callId = mCallIdMapper.getCallId(call);
923         if (callId != null && isServiceValid("swapConference")) {
924             try {
925                 logOutgoing("swapConference %s", callId);
926                 mServiceInterface.swapConference(callId);
927             } catch (RemoteException ignored) {
928             }
929         }
930     }
931 
932     /** {@inheritDoc} */
933     @Override
setServiceInterface(IBinder binder)934     protected void setServiceInterface(IBinder binder) {
935         if (binder == null) {
936             // We have lost our service connection. Notify the world that this service is done.
937             // We must notify the adapter before CallsManager. The adapter will force any pending
938             // outgoing calls to try the next service. This needs to happen before CallsManager
939             // tries to clean up any calls still associated with this service.
940             handleConnectionServiceDeath();
941             CallsManager.getInstance().handleConnectionServiceDeath(this);
942             mServiceInterface = null;
943         } else {
944             mServiceInterface = IConnectionService.Stub.asInterface(binder);
945             addConnectionServiceAdapter(mAdapter);
946         }
947     }
948 
handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection)949     private void handleCreateConnectionComplete(
950             String callId,
951             ConnectionRequest request,
952             ParcelableConnection connection) {
953         // TODO: Note we are not using parameter "request", which is a side effect of our tacit
954         // assumption that we have at most one outgoing connection attempt per ConnectionService.
955         // This may not continue to be the case.
956         if (connection.getState() == Connection.STATE_DISCONNECTED) {
957             // A connection that begins in the DISCONNECTED state is an indication of
958             // failure to connect; we handle all failures uniformly
959             removeCall(callId, connection.getDisconnectCause());
960         } else {
961             // Successful connection
962             if (mPendingResponses.containsKey(callId)) {
963                 mPendingResponses.remove(callId)
964                         .handleCreateConnectionSuccess(mCallIdMapper, connection);
965             }
966         }
967     }
968 
969     /**
970      * Called when the associated connection service dies.
971      */
handleConnectionServiceDeath()972     private void handleConnectionServiceDeath() {
973         if (!mPendingResponses.isEmpty()) {
974             CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
975                     new CreateConnectionResponse[mPendingResponses.values().size()]);
976             mPendingResponses.clear();
977             for (int i = 0; i < responses.length; i++) {
978                 responses[i].handleCreateConnectionFailure(
979                         new DisconnectCause(DisconnectCause.ERROR));
980             }
981         }
982         mCallIdMapper.clear();
983     }
984 
logIncoming(String msg, Object... params)985     private void logIncoming(String msg, Object... params) {
986         Log.d(this, "ConnectionService -> Telecom: " + msg, params);
987     }
988 
logOutgoing(String msg, Object... params)989     private void logOutgoing(String msg, Object... params) {
990         Log.d(this, "Telecom -> ConnectionService: " + msg, params);
991     }
992 
queryRemoteConnectionServices(final RemoteServiceCallback callback)993     private void queryRemoteConnectionServices(final RemoteServiceCallback callback) {
994         // Only give remote connection services to this connection service if it is listed as
995         // the connection manager.
996         PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager();
997         Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
998         if (simCallManager == null ||
999                 !simCallManager.getComponentName().equals(getComponentName())) {
1000             noRemoteServices(callback);
1001             return;
1002         }
1003 
1004         // Make a list of ConnectionServices that are listed as being associated with SIM accounts
1005         final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
1006                 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
1007         for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getCallCapablePhoneAccounts()) {
1008             PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(handle);
1009             if ((account.getCapabilities() & PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0) {
1010                 ConnectionServiceWrapper service =
1011                         mConnectionServiceRepository.getService(handle.getComponentName(),
1012                                 handle.getUserHandle());
1013                 if (service != null) {
1014                     simServices.add(service);
1015                 }
1016             }
1017         }
1018 
1019         final List<ComponentName> simServiceComponentNames = new ArrayList<>();
1020         final List<IBinder> simServiceBinders = new ArrayList<>();
1021 
1022         Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices);
1023 
1024         for (ConnectionServiceWrapper simService : simServices) {
1025             if (simService == this) {
1026                 // Only happens in the unlikely case that a SIM service is also a SIM call manager
1027                 continue;
1028             }
1029 
1030             final ConnectionServiceWrapper currentSimService = simService;
1031 
1032             currentSimService.mBinder.bind(new BindCallback() {
1033                 @Override
1034                 public void onSuccess() {
1035                     Log.d(this, "Adding simService %s", currentSimService.getComponentName());
1036                     simServiceComponentNames.add(currentSimService.getComponentName());
1037                     simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
1038                     maybeComplete();
1039                 }
1040 
1041                 @Override
1042                 public void onFailure() {
1043                     Log.d(this, "Failed simService %s", currentSimService.getComponentName());
1044                     // We know maybeComplete() will always be a no-op from now on, so go ahead and
1045                     // signal failure of the entire request
1046                     noRemoteServices(callback);
1047                 }
1048 
1049                 private void maybeComplete() {
1050                     if (simServiceComponentNames.size() == simServices.size()) {
1051                         setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
1052                     }
1053                 }
1054             });
1055         }
1056     }
1057 
setRemoteServices( RemoteServiceCallback callback, List<ComponentName> componentNames, List<IBinder> binders)1058     private void setRemoteServices(
1059             RemoteServiceCallback callback,
1060             List<ComponentName> componentNames,
1061             List<IBinder> binders) {
1062         try {
1063             callback.onResult(componentNames, binders);
1064         } catch (RemoteException e) {
1065             Log.e(this, e, "Contacting ConnectionService %s",
1066                     ConnectionServiceWrapper.this.getComponentName());
1067         }
1068     }
1069 
noRemoteServices(RemoteServiceCallback callback)1070     private void noRemoteServices(RemoteServiceCallback callback) {
1071         try {
1072             callback.onResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
1073         } catch (RemoteException e) {
1074             Log.e(this, e, "Contacting ConnectionService %s", this.getComponentName());
1075         }
1076     }
1077 }
1078