Home | History | Annotate | Download | only in telecom
      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.telecom;
     18 
     19 import android.net.Uri;
     20 import android.os.Bundle;
     21 import android.os.IBinder.DeathRecipient;
     22 import android.os.RemoteException;
     23 
     24 import com.android.internal.telecom.IConnectionServiceAdapter;
     25 import com.android.internal.telecom.RemoteServiceCallback;
     26 
     27 import java.util.Collections;
     28 import java.util.Iterator;
     29 import java.util.List;
     30 import java.util.Set;
     31 import java.util.concurrent.ConcurrentHashMap;
     32 
     33 /**
     34  * Provides methods for IConnectionService implementations to interact with the system phone app.
     35  *
     36  * @hide
     37  */
     38 final class ConnectionServiceAdapter implements DeathRecipient {
     39     /**
     40      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
     41      * load factor before resizing, 1 means we only expect a single thread to
     42      * access the map so make only a single shard
     43      */
     44     private final Set<IConnectionServiceAdapter> mAdapters = Collections.newSetFromMap(
     45             new ConcurrentHashMap<IConnectionServiceAdapter, Boolean>(8, 0.9f, 1));
     46 
     47     ConnectionServiceAdapter() {
     48     }
     49 
     50     void addAdapter(IConnectionServiceAdapter adapter) {
     51         for (IConnectionServiceAdapter it : mAdapters) {
     52             if (it.asBinder() == adapter.asBinder()) {
     53                 Log.w(this, "Ignoring duplicate adapter addition.");
     54                 return;
     55             }
     56         }
     57         if (mAdapters.add(adapter)) {
     58             try {
     59                 adapter.asBinder().linkToDeath(this, 0);
     60             } catch (RemoteException e) {
     61                 mAdapters.remove(adapter);
     62             }
     63         }
     64     }
     65 
     66     void removeAdapter(IConnectionServiceAdapter adapter) {
     67         if (adapter != null) {
     68             for (IConnectionServiceAdapter it : mAdapters) {
     69                 if (it.asBinder() == adapter.asBinder() && mAdapters.remove(it)) {
     70                     adapter.asBinder().unlinkToDeath(this, 0);
     71                     break;
     72                 }
     73             }
     74         }
     75     }
     76 
     77     /** ${inheritDoc} */
     78     @Override
     79     public void binderDied() {
     80         Iterator<IConnectionServiceAdapter> it = mAdapters.iterator();
     81         while (it.hasNext()) {
     82             IConnectionServiceAdapter adapter = it.next();
     83             if (!adapter.asBinder().isBinderAlive()) {
     84                 it.remove();
     85                 adapter.asBinder().unlinkToDeath(this, 0);
     86             }
     87         }
     88     }
     89 
     90     void handleCreateConnectionComplete(
     91             String id,
     92             ConnectionRequest request,
     93             ParcelableConnection connection) {
     94         for (IConnectionServiceAdapter adapter : mAdapters) {
     95             try {
     96                 adapter.handleCreateConnectionComplete(id, request, connection,
     97                         Log.getExternalSession());
     98             } catch (RemoteException e) {
     99             }
    100         }
    101     }
    102 
    103     /**
    104      * Sets a call's state to active (e.g., an ongoing call where two parties can actively
    105      * communicate).
    106      *
    107      * @param callId The unique ID of the call whose state is changing to active.
    108      */
    109     void setActive(String callId) {
    110         for (IConnectionServiceAdapter adapter : mAdapters) {
    111             try {
    112                 adapter.setActive(callId, Log.getExternalSession());
    113             } catch (RemoteException e) {
    114             }
    115         }
    116     }
    117 
    118     /**
    119      * Sets a call's state to ringing (e.g., an inbound ringing call).
    120      *
    121      * @param callId The unique ID of the call whose state is changing to ringing.
    122      */
    123     void setRinging(String callId) {
    124         for (IConnectionServiceAdapter adapter : mAdapters) {
    125             try {
    126                 adapter.setRinging(callId, Log.getExternalSession());
    127             } catch (RemoteException e) {
    128             }
    129         }
    130     }
    131 
    132     /**
    133      * Sets a call's state to dialing (e.g., dialing an outbound call).
    134      *
    135      * @param callId The unique ID of the call whose state is changing to dialing.
    136      */
    137     void setDialing(String callId) {
    138         for (IConnectionServiceAdapter adapter : mAdapters) {
    139             try {
    140                 adapter.setDialing(callId, Log.getExternalSession());
    141             } catch (RemoteException e) {
    142             }
    143         }
    144     }
    145 
    146     /**
    147      * Sets a call's state to pulling (e.g. a call with {@link Connection#PROPERTY_IS_EXTERNAL_CALL}
    148      * is being pulled to the local device.
    149      *
    150      * @param callId The unique ID of the call whose state is changing to dialing.
    151      */
    152     void setPulling(String callId) {
    153         for (IConnectionServiceAdapter adapter : mAdapters) {
    154             try {
    155                 adapter.setPulling(callId, Log.getExternalSession());
    156             } catch (RemoteException e) {
    157             }
    158         }
    159     }
    160 
    161     /**
    162      * Sets a call's state to disconnected.
    163      *
    164      * @param callId The unique ID of the call whose state is changing to disconnected.
    165      * @param disconnectCause The reason for the disconnection, as described by
    166      *            {@link android.telecomm.DisconnectCause}.
    167      */
    168     void setDisconnected(String callId, DisconnectCause disconnectCause) {
    169         for (IConnectionServiceAdapter adapter : mAdapters) {
    170             try {
    171                 adapter.setDisconnected(callId, disconnectCause, Log.getExternalSession());
    172             } catch (RemoteException e) {
    173             }
    174         }
    175     }
    176 
    177     /**
    178      * Sets a call's state to be on hold.
    179      *
    180      * @param callId - The unique ID of the call whose state is changing to be on hold.
    181      */
    182     void setOnHold(String callId) {
    183         for (IConnectionServiceAdapter adapter : mAdapters) {
    184             try {
    185                 adapter.setOnHold(callId, Log.getExternalSession());
    186             } catch (RemoteException e) {
    187             }
    188         }
    189     }
    190 
    191     /**
    192      * Asks Telecom to start or stop a ringback tone for a call.
    193      *
    194      * @param callId The unique ID of the call whose ringback is being changed.
    195      * @param ringback Whether Telecom should start playing a ringback tone.
    196      */
    197     void setRingbackRequested(String callId, boolean ringback) {
    198         for (IConnectionServiceAdapter adapter : mAdapters) {
    199             try {
    200                 adapter.setRingbackRequested(callId, ringback, Log.getExternalSession());
    201             } catch (RemoteException e) {
    202             }
    203         }
    204     }
    205 
    206     void setConnectionCapabilities(String callId, int capabilities) {
    207         for (IConnectionServiceAdapter adapter : mAdapters) {
    208             try {
    209                 adapter.setConnectionCapabilities(callId, capabilities, Log.getExternalSession());
    210             } catch (RemoteException ignored) {
    211             }
    212         }
    213     }
    214 
    215     void setConnectionProperties(String callId, int properties) {
    216         for (IConnectionServiceAdapter adapter : mAdapters) {
    217             try {
    218                 adapter.setConnectionProperties(callId, properties, Log.getExternalSession());
    219             } catch (RemoteException ignored) {
    220             }
    221         }
    222     }
    223 
    224     /**
    225      * Indicates whether or not the specified call is currently conferenced into the specified
    226      * conference call.
    227      *
    228      * @param callId The unique ID of the call being conferenced.
    229      * @param conferenceCallId The unique ID of the conference call. Null if call is not
    230      *            conferenced.
    231      */
    232     void setIsConferenced(String callId, String conferenceCallId) {
    233         for (IConnectionServiceAdapter adapter : mAdapters) {
    234             try {
    235                 Log.d(this, "sending connection %s with conference %s", callId, conferenceCallId);
    236                 adapter.setIsConferenced(callId, conferenceCallId, Log.getExternalSession());
    237             } catch (RemoteException ignored) {
    238             }
    239         }
    240     }
    241 
    242     /**
    243      * Indicates that the merge request on this call has failed.
    244      *
    245      * @param callId The unique ID of the call being conferenced.
    246      */
    247     void onConferenceMergeFailed(String callId) {
    248         for (IConnectionServiceAdapter adapter : mAdapters) {
    249             try {
    250                 Log.d(this, "merge failed for call %s", callId);
    251                 adapter.setConferenceMergeFailed(callId, Log.getExternalSession());
    252             } catch (RemoteException ignored) {
    253             }
    254         }
    255     }
    256 
    257     /**
    258         * Resets the cdma connection time.
    259         */
    260     void resetConnectionTime(String callId) {
    261         for (IConnectionServiceAdapter adapter : mAdapters) {
    262             try {
    263                 adapter.resetConnectionTime(callId, Log.getExternalSession());
    264             } catch (RemoteException e) {
    265             }
    266         }
    267     }
    268 
    269     /**
    270      * Indicates that the call no longer exists. Can be used with either a call or a conference
    271      * call.
    272      *
    273      * @param callId The unique ID of the call.
    274      */
    275     void removeCall(String callId) {
    276         for (IConnectionServiceAdapter adapter : mAdapters) {
    277             try {
    278                 adapter.removeCall(callId, Log.getExternalSession());
    279             } catch (RemoteException ignored) {
    280             }
    281         }
    282     }
    283 
    284     void onPostDialWait(String callId, String remaining) {
    285         for (IConnectionServiceAdapter adapter : mAdapters) {
    286             try {
    287                 adapter.onPostDialWait(callId, remaining, Log.getExternalSession());
    288             } catch (RemoteException ignored) {
    289             }
    290         }
    291     }
    292 
    293     void onPostDialChar(String callId, char nextChar) {
    294         for (IConnectionServiceAdapter adapter : mAdapters) {
    295             try {
    296                 adapter.onPostDialChar(callId, nextChar, Log.getExternalSession());
    297             } catch (RemoteException ignored) {
    298             }
    299         }
    300     }
    301 
    302     /**
    303      * Indicates that a new conference call has been created.
    304      *
    305      * @param callId The unique ID of the conference call.
    306      */
    307     void addConferenceCall(String callId, ParcelableConference parcelableConference) {
    308         for (IConnectionServiceAdapter adapter : mAdapters) {
    309             try {
    310                 adapter.addConferenceCall(callId, parcelableConference, Log.getExternalSession());
    311             } catch (RemoteException ignored) {
    312             }
    313         }
    314     }
    315 
    316     /**
    317      * Retrieves a list of remote connection services usable to place calls.
    318      */
    319     void queryRemoteConnectionServices(RemoteServiceCallback callback, String callingPackage) {
    320         // Only supported when there is only one adapter.
    321         if (mAdapters.size() == 1) {
    322             try {
    323                 mAdapters.iterator().next().queryRemoteConnectionServices(callback, callingPackage,
    324                         Log.getExternalSession());
    325             } catch (RemoteException e) {
    326                 Log.e(this, e, "Exception trying to query for remote CSs");
    327             }
    328         } else {
    329             try {
    330                 // This is not an error condition, so just pass back an empty list.
    331                 // This happens when querying from a remote connection service, not the connection
    332                 // manager itself.
    333                 callback.onResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
    334             } catch (RemoteException e) {
    335                 Log.e(this, e, "Exception trying to query for remote CSs");
    336             }
    337         }
    338     }
    339 
    340     /**
    341      * Sets the call video provider for a call.
    342      *
    343      * @param callId The unique ID of the call to set with the given call video provider.
    344      * @param videoProvider The call video provider instance to set on the call.
    345      */
    346     void setVideoProvider(
    347             String callId, Connection.VideoProvider videoProvider) {
    348         for (IConnectionServiceAdapter adapter : mAdapters) {
    349             try {
    350                 adapter.setVideoProvider(
    351                         callId,
    352                         videoProvider == null ? null : videoProvider.getInterface(),
    353                         Log.getExternalSession());
    354             } catch (RemoteException e) {
    355             }
    356         }
    357     }
    358 
    359     /**
    360      * Requests that the framework use VOIP audio mode for this connection.
    361      *
    362      * @param callId The unique ID of the call to set with the given call video provider.
    363      * @param isVoip True if the audio mode is VOIP.
    364      */
    365     void setIsVoipAudioMode(String callId, boolean isVoip) {
    366         for (IConnectionServiceAdapter adapter : mAdapters) {
    367             try {
    368                 adapter.setIsVoipAudioMode(callId, isVoip, Log.getExternalSession());
    369             } catch (RemoteException e) {
    370             }
    371         }
    372     }
    373 
    374     void setStatusHints(String callId, StatusHints statusHints) {
    375         for (IConnectionServiceAdapter adapter : mAdapters) {
    376             try {
    377                 adapter.setStatusHints(callId, statusHints, Log.getExternalSession());
    378             } catch (RemoteException e) {
    379             }
    380         }
    381     }
    382 
    383     void setAddress(String callId, Uri address, int presentation) {
    384         for (IConnectionServiceAdapter adapter : mAdapters) {
    385             try {
    386                 adapter.setAddress(callId, address, presentation, Log.getExternalSession());
    387             } catch (RemoteException e) {
    388             }
    389         }
    390     }
    391 
    392     void setCallerDisplayName(String callId, String callerDisplayName, int presentation) {
    393         for (IConnectionServiceAdapter adapter : mAdapters) {
    394             try {
    395                 adapter.setCallerDisplayName(callId, callerDisplayName, presentation,
    396                         Log.getExternalSession());
    397             } catch (RemoteException e) {
    398             }
    399         }
    400     }
    401 
    402     /**
    403      * Sets the video state associated with a call.
    404      *
    405      * Valid values: {@link VideoProfile#STATE_BIDIRECTIONAL},
    406      * {@link VideoProfile#STATE_AUDIO_ONLY},
    407      * {@link VideoProfile#STATE_TX_ENABLED},
    408      * {@link VideoProfile#STATE_RX_ENABLED}.
    409      *
    410      * @param callId The unique ID of the call to set the video state for.
    411      * @param videoState The video state.
    412      */
    413     void setVideoState(String callId, int videoState) {
    414         Log.v(this, "setVideoState: %d", videoState);
    415         for (IConnectionServiceAdapter adapter : mAdapters) {
    416             try {
    417                 adapter.setVideoState(callId, videoState, Log.getExternalSession());
    418             } catch (RemoteException ignored) {
    419             }
    420         }
    421     }
    422 
    423     void setConferenceableConnections(String callId, List<String> conferenceableCallIds) {
    424         Log.v(this, "setConferenceableConnections: %s, %s", callId, conferenceableCallIds);
    425         for (IConnectionServiceAdapter adapter : mAdapters) {
    426             try {
    427                 adapter.setConferenceableConnections(callId, conferenceableCallIds,
    428                         Log.getExternalSession());
    429             } catch (RemoteException ignored) {
    430             }
    431         }
    432     }
    433 
    434     /**
    435      * Informs telecom of an existing connection which was added by the {@link ConnectionService}.
    436      *
    437      * @param callId The unique ID of the call being added.
    438      * @param connection The connection.
    439      */
    440     void addExistingConnection(String callId, ParcelableConnection connection) {
    441         Log.v(this, "addExistingConnection: %s", callId);
    442         for (IConnectionServiceAdapter adapter : mAdapters) {
    443             try {
    444                 adapter.addExistingConnection(callId, connection, Log.getExternalSession());
    445             } catch (RemoteException ignored) {
    446             }
    447         }
    448     }
    449 
    450     /**
    451      * Adds some extras associated with a {@code Connection}.
    452      *
    453      * @param callId The unique ID of the call.
    454      * @param extras The extras to add.
    455      */
    456     void putExtras(String callId, Bundle extras) {
    457         Log.v(this, "putExtras: %s", callId);
    458         for (IConnectionServiceAdapter adapter : mAdapters) {
    459             try {
    460                 adapter.putExtras(callId, extras, Log.getExternalSession());
    461             } catch (RemoteException ignored) {
    462             }
    463         }
    464     }
    465 
    466     /**
    467      * Adds an extra associated with a {@code Connection}.
    468      *
    469      * @param callId The unique ID of the call.
    470      * @param key The extra key.
    471      * @param value The extra value.
    472      */
    473     void putExtra(String callId, String key, boolean value) {
    474         Log.v(this, "putExtra: %s %s=%b", callId, key, value);
    475         for (IConnectionServiceAdapter adapter : mAdapters) {
    476             try {
    477                 Bundle bundle = new Bundle();
    478                 bundle.putBoolean(key, value);
    479                 adapter.putExtras(callId, bundle, Log.getExternalSession());
    480             } catch (RemoteException ignored) {
    481             }
    482         }
    483     }
    484 
    485     /**
    486      * Adds an extra associated with a {@code Connection}.
    487      *
    488      * @param callId The unique ID of the call.
    489      * @param key The extra key.
    490      * @param value The extra value.
    491      */
    492     void putExtra(String callId, String key, int value) {
    493         Log.v(this, "putExtra: %s %s=%d", callId, key, value);
    494         for (IConnectionServiceAdapter adapter : mAdapters) {
    495             try {
    496                 Bundle bundle = new Bundle();
    497                 bundle.putInt(key, value);
    498                 adapter.putExtras(callId, bundle, Log.getExternalSession());
    499             } catch (RemoteException ignored) {
    500             }
    501         }
    502     }
    503 
    504     /**
    505      * Adds an extra associated with a {@code Connection}.
    506      *
    507      * @param callId The unique ID of the call.
    508      * @param key The extra key.
    509      * @param value The extra value.
    510      */
    511     void putExtra(String callId, String key, String value) {
    512         Log.v(this, "putExtra: %s %s=%s", callId, key, value);
    513         for (IConnectionServiceAdapter adapter : mAdapters) {
    514             try {
    515                 Bundle bundle = new Bundle();
    516                 bundle.putString(key, value);
    517                 adapter.putExtras(callId, bundle, Log.getExternalSession());
    518             } catch (RemoteException ignored) {
    519             }
    520         }
    521     }
    522 
    523     /**
    524      * Removes extras associated with a {@code Connection}.
    525      *  @param callId The unique ID of the call.
    526      * @param keys The extra keys to remove.
    527      */
    528     void removeExtras(String callId, List<String> keys) {
    529         Log.v(this, "removeExtras: %s %s", callId, keys);
    530         for (IConnectionServiceAdapter adapter : mAdapters) {
    531             try {
    532                 adapter.removeExtras(callId, keys, Log.getExternalSession());
    533             } catch (RemoteException ignored) {
    534             }
    535         }
    536     }
    537 
    538     /**
    539      * Sets the audio route associated with a {@link Connection}.
    540      *
    541      * @param callId The unique ID of the call.
    542      * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
    543      */
    544     void setAudioRoute(String callId, int audioRoute, String bluetoothAddress) {
    545         Log.v(this, "setAudioRoute: %s %s %s", callId,
    546                 CallAudioState.audioRouteToString(audioRoute),
    547                 bluetoothAddress);
    548         for (IConnectionServiceAdapter adapter : mAdapters) {
    549             try {
    550                 adapter.setAudioRoute(callId, audioRoute,
    551                         bluetoothAddress, Log.getExternalSession());
    552             } catch (RemoteException ignored) {
    553             }
    554         }
    555     }
    556 
    557 
    558     /**
    559      * Informs Telecom of a connection level event.
    560      *
    561      * @param callId The unique ID of the call.
    562      * @param event The event.
    563      * @param extras Extras associated with the event.
    564      */
    565     void onConnectionEvent(String callId, String event, Bundle extras) {
    566         Log.v(this, "onConnectionEvent: %s", event);
    567         for (IConnectionServiceAdapter adapter : mAdapters) {
    568             try {
    569                 adapter.onConnectionEvent(callId, event, extras, Log.getExternalSession());
    570             } catch (RemoteException ignored) {
    571             }
    572         }
    573     }
    574 
    575     /**
    576      * Notifies Telecom that an RTT session was successfully established.
    577      *
    578      * @param callId The unique ID of the call.
    579      */
    580     void onRttInitiationSuccess(String callId) {
    581         Log.v(this, "onRttInitiationSuccess: %s", callId);
    582         for (IConnectionServiceAdapter adapter : mAdapters) {
    583             try {
    584                 adapter.onRttInitiationSuccess(callId, Log.getExternalSession());
    585             } catch (RemoteException ignored) {
    586             }
    587         }
    588     }
    589 
    590     /**
    591      * Notifies Telecom that a requested RTT session failed to be established.
    592      *
    593      * @param callId The unique ID of the call.
    594      */
    595     void onRttInitiationFailure(String callId, int reason) {
    596         Log.v(this, "onRttInitiationFailure: %s", callId);
    597         for (IConnectionServiceAdapter adapter : mAdapters) {
    598             try {
    599                 adapter.onRttInitiationFailure(callId, reason, Log.getExternalSession());
    600             } catch (RemoteException ignored) {
    601             }
    602         }
    603     }
    604 
    605     /**
    606      * Notifies Telecom that an established RTT session was terminated by the remote user on
    607      * the call.
    608      *
    609      * @param callId The unique ID of the call.
    610      */
    611     void onRttSessionRemotelyTerminated(String callId) {
    612         Log.v(this, "onRttSessionRemotelyTerminated: %s", callId);
    613         for (IConnectionServiceAdapter adapter : mAdapters) {
    614             try {
    615                 adapter.onRttSessionRemotelyTerminated(callId, Log.getExternalSession());
    616             } catch (RemoteException ignored) {
    617             }
    618         }
    619     }
    620 
    621     /**
    622      * Notifies Telecom that the remote user on the call has requested an upgrade to an RTT
    623      * session for this call.
    624      *
    625      * @param callId The unique ID of the call.
    626      */
    627     void onRemoteRttRequest(String callId) {
    628         Log.v(this, "onRemoteRttRequest: %s", callId);
    629         for (IConnectionServiceAdapter adapter : mAdapters) {
    630             try {
    631                 adapter.onRemoteRttRequest(callId, Log.getExternalSession());
    632             } catch (RemoteException ignored) {
    633             }
    634         }
    635     }
    636 
    637     /**
    638      * Notifies Telecom that a call's PhoneAccountHandle has changed.
    639      *
    640      * @param callId The unique ID of the call.
    641      * @param pHandle The new PhoneAccountHandle associated with the call.
    642      */
    643     void onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle) {
    644         for (IConnectionServiceAdapter adapter : mAdapters) {
    645             try {
    646                 Log.d(this, "onPhoneAccountChanged %s", callId);
    647                 adapter.onPhoneAccountChanged(callId, pHandle, Log.getExternalSession());
    648             } catch (RemoteException ignored) {
    649             }
    650         }
    651     }
    652 
    653     /**
    654      * Notifies Telecom that the {@link ConnectionService} has released the call resource.
    655      */
    656     void onConnectionServiceFocusReleased() {
    657         for (IConnectionServiceAdapter adapter : mAdapters) {
    658             try {
    659                 Log.d(this, "onConnectionServiceFocusReleased");
    660                 adapter.onConnectionServiceFocusReleased(Log.getExternalSession());
    661             } catch (RemoteException ignored) {
    662             }
    663         }
    664     }
    665 
    666     /**
    667      * Sets whether a conference is treated as a conference or a single party call.
    668      * See {@link Conference#setConferenceState(boolean)} for more information.
    669      *
    670      * @param callId The ID of the telecom call.
    671      * @param isConference {@code true} if this call should be treated as a conference,
    672      * {@code false} otherwise.
    673      */
    674     void setConferenceState(String callId, boolean isConference) {
    675         Log.v(this, "setConferenceState: %s %b", callId, isConference);
    676         for (IConnectionServiceAdapter adapter : mAdapters) {
    677             try {
    678                 adapter.setConferenceState(callId, isConference, Log.getExternalSession());
    679             } catch (RemoteException ignored) {
    680             }
    681         }
    682     }
    683 }
    684