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.annotation.SystemApi;
     20 import android.net.Uri;
     21 import android.os.Bundle;
     22 import android.os.Handler;
     23 
     24 import java.lang.String;
     25 import java.util.ArrayList;
     26 import java.util.Collections;
     27 import java.util.List;
     28 import java.util.Map;
     29 import java.util.Objects;
     30 import java.util.concurrent.CopyOnWriteArrayList;
     31 
     32 /**
     33  * Represents an ongoing phone call that the in-call app should present to the user.
     34  */
     35 public final class Call {
     36     /**
     37      * The state of a {@code Call} when newly created.
     38      */
     39     public static final int STATE_NEW = 0;
     40 
     41     /**
     42      * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
     43      */
     44     public static final int STATE_DIALING = 1;
     45 
     46     /**
     47      * The state of an incoming {@code Call} when ringing locally, but not yet connected.
     48      */
     49     public static final int STATE_RINGING = 2;
     50 
     51     /**
     52      * The state of a {@code Call} when in a holding state.
     53      */
     54     public static final int STATE_HOLDING = 3;
     55 
     56     /**
     57      * The state of a {@code Call} when actively supporting conversation.
     58      */
     59     public static final int STATE_ACTIVE = 4;
     60 
     61     /**
     62      * The state of a {@code Call} when no further voice or other communication is being
     63      * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
     64      * is no longer active, and the local data transport has or inevitably will release resources
     65      * associated with this {@code Call}.
     66      */
     67     public static final int STATE_DISCONNECTED = 7;
     68 
     69     /**
     70      * The state of an outgoing {@code Call} when waiting on user to select a
     71      * {@link PhoneAccount} through which to place the call.
     72      */
     73     public static final int STATE_SELECT_PHONE_ACCOUNT = 8;
     74 
     75     /**
     76      * @hide
     77      * @deprecated use STATE_SELECT_PHONE_ACCOUNT.
     78      */
     79     @Deprecated
     80     @SystemApi
     81     public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT;
     82 
     83     /**
     84      * The initial state of an outgoing {@code Call}.
     85      * Common transitions are to {@link #STATE_DIALING} state for a successful call or
     86      * {@link #STATE_DISCONNECTED} if it failed.
     87      */
     88     public static final int STATE_CONNECTING = 9;
     89 
     90     /**
     91      * The state of a {@code Call} when the user has initiated a disconnection of the call, but the
     92      * call has not yet been disconnected by the underlying {@code ConnectionService}.  The next
     93      * state of the call is (potentially) {@link #STATE_DISCONNECTED}.
     94      */
     95     public static final int STATE_DISCONNECTING = 10;
     96 
     97     /**
     98      * The state of an external call which is in the process of being pulled from a remote device to
     99      * the local device.
    100      * <p>
    101      * A call can only be in this state if the {@link Details#PROPERTY_IS_EXTERNAL_CALL} property
    102      * and {@link Details#CAPABILITY_CAN_PULL_CALL} capability are set on the call.
    103      * <p>
    104      * An {@link InCallService} will only see this state if it has the
    105      * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
    106      * manifest.
    107      * @hide
    108      */
    109     public static final int STATE_PULLING_CALL = 11;
    110 
    111     /**
    112      * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
    113      * extras. Used to pass the phone accounts to display on the front end to the user in order to
    114      * select phone accounts to (for example) place a call.
    115      */
    116     public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
    117 
    118     public static class Details {
    119 
    120         /** Call can currently be put on hold or unheld. */
    121         public static final int CAPABILITY_HOLD = 0x00000001;
    122 
    123         /** Call supports the hold feature. */
    124         public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
    125 
    126         /**
    127          * Calls within a conference can be merged. A {@link ConnectionService} has the option to
    128          * add a {@link Conference} call before the child {@link Connection}s are merged. This is how
    129          * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
    130          * capability allows a merge button to be shown while the conference call is in the foreground
    131          * of the in-call UI.
    132          * <p>
    133          * This is only intended for use by a {@link Conference}.
    134          */
    135         public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
    136 
    137         /**
    138          * Calls within a conference can be swapped between foreground and background.
    139          * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
    140          * <p>
    141          * This is only intended for use by a {@link Conference}.
    142          */
    143         public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
    144 
    145         /**
    146          * @hide
    147          */
    148         public static final int CAPABILITY_UNUSED_1 = 0x00000010;
    149 
    150         /** Call supports responding via text option. */
    151         public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
    152 
    153         /** Call can be muted. */
    154         public static final int CAPABILITY_MUTE = 0x00000040;
    155 
    156         /**
    157          * Call supports conference call management. This capability only applies to {@link Conference}
    158          * calls which can have {@link Connection}s as children.
    159          */
    160         public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
    161 
    162         /**
    163          * Local device supports receiving video.
    164          */
    165         public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
    166 
    167         /**
    168          * Local device supports transmitting video.
    169          */
    170         public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
    171 
    172         /**
    173          * Local device supports bidirectional video calling.
    174          */
    175         public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
    176                 CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
    177 
    178         /**
    179          * Remote device supports receiving video.
    180          */
    181         public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
    182 
    183         /**
    184          * Remote device supports transmitting video.
    185          */
    186         public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
    187 
    188         /**
    189          * Remote device supports bidirectional video calling.
    190          */
    191         public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
    192                 CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
    193 
    194         /**
    195          * Call is able to be separated from its parent {@code Conference}, if any.
    196          */
    197         public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
    198 
    199         /**
    200          * Call is able to be individually disconnected when in a {@code Conference}.
    201          */
    202         public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
    203 
    204         /**
    205          * Speed up audio setup for MT call.
    206          * @hide
    207          */
    208         public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
    209 
    210         /**
    211          * Call can be upgraded to a video call.
    212          * @hide
    213          */
    214         public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
    215 
    216         /**
    217          * For video calls, indicates whether the outgoing video for the call can be paused using
    218          * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
    219          */
    220         public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
    221 
    222         /**
    223          * Call sends responses through connection.
    224          * @hide
    225          */
    226         public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000;
    227 
    228         /**
    229          * When set, prevents a video {@code Call} from being downgraded to an audio-only call.
    230          * <p>
    231          * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
    232          * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
    233          * downgraded from a video call back to a VideoState of
    234          * {@link VideoProfile#STATE_AUDIO_ONLY}.
    235          * <p>
    236          * Intuitively, a call which can be downgraded to audio should also have local and remote
    237          * video
    238          * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
    239          * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
    240          */
    241         public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
    242 
    243         /**
    244          * When set for an external call, indicates that this {@code Call} can be pulled from a
    245          * remote device to the current device.
    246          * <p>
    247          * Should only be set on a {@code Call} where {@link #PROPERTY_IS_EXTERNAL_CALL} is set.
    248          * <p>
    249          * An {@link InCallService} will only see calls with this capability if it has the
    250          * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
    251          * in its manifest.
    252          * <p>
    253          * See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
    254          * {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
    255          * @hide
    256          */
    257         public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
    258 
    259         //******************************************************************************************
    260         // Next CAPABILITY value: 0x01000000
    261         //******************************************************************************************
    262 
    263         /**
    264          * Whether the call is currently a conference.
    265          */
    266         public static final int PROPERTY_CONFERENCE = 0x00000001;
    267 
    268         /**
    269          * Whether the call is a generic conference, where we do not know the precise state of
    270          * participants in the conference (eg. on CDMA).
    271          */
    272         public static final int PROPERTY_GENERIC_CONFERENCE = 0x00000002;
    273 
    274         /**
    275          * Whether the call is made while the device is in emergency callback mode.
    276          */
    277         public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 0x00000004;
    278 
    279         /**
    280          * Connection is using WIFI.
    281          */
    282         public static final int PROPERTY_WIFI = 0x00000008;
    283 
    284         /**
    285          * Call is using high definition audio.
    286          */
    287         public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010;
    288 
    289         /**
    290          * Whether the call is associated with the work profile.
    291          */
    292         public static final int PROPERTY_ENTERPRISE_CALL = 0x00000020;
    293 
    294         /**
    295          * When set, indicates that this {@code Call} does not actually exist locally for the
    296          * {@link ConnectionService}.
    297          * <p>
    298          * Consider, for example, a scenario where a user has two phones with the same phone number.
    299          * When a user places a call on one device, the telephony stack can represent that call on
    300          * the other device by adding it to the {@link ConnectionService} with the
    301          * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
    302          * <p>
    303          * An {@link InCallService} will only see calls with this property if it has the
    304          * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
    305          * in its manifest.
    306          * <p>
    307          * See {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
    308          * @hide
    309          */
    310         public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
    311 
    312         //******************************************************************************************
    313         // Next PROPERTY value: 0x00000100
    314         //******************************************************************************************
    315 
    316         private final String mTelecomCallId;
    317         private final Uri mHandle;
    318         private final int mHandlePresentation;
    319         private final String mCallerDisplayName;
    320         private final int mCallerDisplayNamePresentation;
    321         private final PhoneAccountHandle mAccountHandle;
    322         private final int mCallCapabilities;
    323         private final int mCallProperties;
    324         private final DisconnectCause mDisconnectCause;
    325         private final long mConnectTimeMillis;
    326         private final GatewayInfo mGatewayInfo;
    327         private final int mVideoState;
    328         private final StatusHints mStatusHints;
    329         private final Bundle mExtras;
    330         private final Bundle mIntentExtras;
    331 
    332         /**
    333          * Whether the supplied capabilities  supports the specified capability.
    334          *
    335          * @param capabilities A bit field of capabilities.
    336          * @param capability The capability to check capabilities for.
    337          * @return Whether the specified capability is supported.
    338          */
    339         public static boolean can(int capabilities, int capability) {
    340             return (capabilities & capability) == capability;
    341         }
    342 
    343         /**
    344          * Whether the capabilities of this {@code Details} supports the specified capability.
    345          *
    346          * @param capability The capability to check capabilities for.
    347          * @return Whether the specified capability is supported.
    348          */
    349         public boolean can(int capability) {
    350             return can(mCallCapabilities, capability);
    351         }
    352 
    353         /**
    354          * Render a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
    355          *
    356          * @param capabilities A capability bit field.
    357          * @return A human readable string representation.
    358          */
    359         public static String capabilitiesToString(int capabilities) {
    360             StringBuilder builder = new StringBuilder();
    361             builder.append("[Capabilities:");
    362             if (can(capabilities, CAPABILITY_HOLD)) {
    363                 builder.append(" CAPABILITY_HOLD");
    364             }
    365             if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
    366                 builder.append(" CAPABILITY_SUPPORT_HOLD");
    367             }
    368             if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
    369                 builder.append(" CAPABILITY_MERGE_CONFERENCE");
    370             }
    371             if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
    372                 builder.append(" CAPABILITY_SWAP_CONFERENCE");
    373             }
    374             if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
    375                 builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
    376             }
    377             if (can(capabilities, CAPABILITY_MUTE)) {
    378                 builder.append(" CAPABILITY_MUTE");
    379             }
    380             if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
    381                 builder.append(" CAPABILITY_MANAGE_CONFERENCE");
    382             }
    383             if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
    384                 builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
    385             }
    386             if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
    387                 builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
    388             }
    389             if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
    390                 builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
    391             }
    392             if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
    393                 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
    394             }
    395             if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
    396                 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
    397             }
    398             if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
    399                 builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
    400             }
    401             if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
    402                 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
    403             }
    404             if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
    405                 builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
    406             }
    407             if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
    408                 builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
    409             }
    410             if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
    411                 builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
    412             }
    413             if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
    414                 builder.append(" CAPABILITY_CAN_PULL_CALL");
    415             }
    416             builder.append("]");
    417             return builder.toString();
    418         }
    419 
    420         /**
    421          * Whether the supplied properties includes the specified property.
    422          *
    423          * @param properties A bit field of properties.
    424          * @param property The property to check properties for.
    425          * @return Whether the specified property is supported.
    426          */
    427         public static boolean hasProperty(int properties, int property) {
    428             return (properties & property) == property;
    429         }
    430 
    431         /**
    432          * Whether the properties of this {@code Details} includes the specified property.
    433          *
    434          * @param property The property to check properties for.
    435          * @return Whether the specified property is supported.
    436          */
    437         public boolean hasProperty(int property) {
    438             return hasProperty(mCallProperties, property);
    439         }
    440 
    441         /**
    442          * Render a set of property bits ({@code PROPERTY_*}) as a human readable string.
    443          *
    444          * @param properties A property bit field.
    445          * @return A human readable string representation.
    446          */
    447         public static String propertiesToString(int properties) {
    448             StringBuilder builder = new StringBuilder();
    449             builder.append("[Properties:");
    450             if (hasProperty(properties, PROPERTY_CONFERENCE)) {
    451                 builder.append(" PROPERTY_CONFERENCE");
    452             }
    453             if (hasProperty(properties, PROPERTY_GENERIC_CONFERENCE)) {
    454                 builder.append(" PROPERTY_GENERIC_CONFERENCE");
    455             }
    456             if (hasProperty(properties, PROPERTY_WIFI)) {
    457                 builder.append(" PROPERTY_WIFI");
    458             }
    459             if (hasProperty(properties, PROPERTY_HIGH_DEF_AUDIO)) {
    460                 builder.append(" PROPERTY_HIGH_DEF_AUDIO");
    461             }
    462             if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
    463                 builder.append(" PROPERTY_EMERGENCY_CALLBACK_MODE");
    464             }
    465             if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) {
    466                 builder.append(" PROPERTY_IS_EXTERNAL_CALL");
    467             }
    468             builder.append("]");
    469             return builder.toString();
    470         }
    471 
    472         /** {@hide} */
    473         public String getTelecomCallId() {
    474             return mTelecomCallId;
    475         }
    476 
    477         /**
    478          * @return The handle (e.g., phone number) to which the {@code Call} is currently
    479          * connected.
    480          */
    481         public Uri getHandle() {
    482             return mHandle;
    483         }
    484 
    485         /**
    486          * @return The presentation requirements for the handle. See
    487          * {@link TelecomManager} for valid values.
    488          */
    489         public int getHandlePresentation() {
    490             return mHandlePresentation;
    491         }
    492 
    493         /**
    494          * @return The display name for the caller.
    495          */
    496         public String getCallerDisplayName() {
    497             return mCallerDisplayName;
    498         }
    499 
    500         /**
    501          * @return The presentation requirements for the caller display name. See
    502          * {@link TelecomManager} for valid values.
    503          */
    504         public int getCallerDisplayNamePresentation() {
    505             return mCallerDisplayNamePresentation;
    506         }
    507 
    508         /**
    509          * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
    510          * routed.
    511          */
    512         public PhoneAccountHandle getAccountHandle() {
    513             return mAccountHandle;
    514         }
    515 
    516         /**
    517          * @return A bitmask of the capabilities of the {@code Call}, as defined by the various
    518          *         {@code CAPABILITY_*} constants in this class.
    519          */
    520         public int getCallCapabilities() {
    521             return mCallCapabilities;
    522         }
    523 
    524         /**
    525          * @return A bitmask of the properties of the {@code Call}, as defined by the various
    526          *         {@code PROPERTY_*} constants in this class.
    527          */
    528         public int getCallProperties() {
    529             return mCallProperties;
    530         }
    531 
    532         /**
    533          * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
    534          * by {@link android.telecom.DisconnectCause}.
    535          */
    536         public DisconnectCause getDisconnectCause() {
    537             return mDisconnectCause;
    538         }
    539 
    540         /**
    541          * @return The time the {@code Call} has been connected. This information is updated
    542          * periodically, but user interfaces should not rely on this to display any "call time
    543          * clock".
    544          */
    545         public final long getConnectTimeMillis() {
    546             return mConnectTimeMillis;
    547         }
    548 
    549         /**
    550          * @return Information about any calling gateway the {@code Call} may be using.
    551          */
    552         public GatewayInfo getGatewayInfo() {
    553             return mGatewayInfo;
    554         }
    555 
    556         /**
    557          * @return The video state of the {@code Call}.
    558          */
    559         public int getVideoState() {
    560             return mVideoState;
    561         }
    562 
    563         /**
    564          * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
    565          * have been set.
    566          */
    567         public StatusHints getStatusHints() {
    568             return mStatusHints;
    569         }
    570 
    571         /**
    572          * @return The extras associated with this call.
    573          */
    574         public Bundle getExtras() {
    575             return mExtras;
    576         }
    577 
    578         /**
    579          * @return The extras used with the original intent to place this call.
    580          */
    581         public Bundle getIntentExtras() {
    582             return mIntentExtras;
    583         }
    584 
    585         @Override
    586         public boolean equals(Object o) {
    587             if (o instanceof Details) {
    588                 Details d = (Details) o;
    589                 return
    590                         Objects.equals(mHandle, d.mHandle) &&
    591                         Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
    592                         Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
    593                         Objects.equals(mCallerDisplayNamePresentation,
    594                                 d.mCallerDisplayNamePresentation) &&
    595                         Objects.equals(mAccountHandle, d.mAccountHandle) &&
    596                         Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
    597                         Objects.equals(mCallProperties, d.mCallProperties) &&
    598                         Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
    599                         Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
    600                         Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
    601                         Objects.equals(mVideoState, d.mVideoState) &&
    602                         Objects.equals(mStatusHints, d.mStatusHints) &&
    603                         areBundlesEqual(mExtras, d.mExtras) &&
    604                         areBundlesEqual(mIntentExtras, d.mIntentExtras);
    605             }
    606             return false;
    607         }
    608 
    609         @Override
    610         public int hashCode() {
    611             return
    612                     Objects.hashCode(mHandle) +
    613                     Objects.hashCode(mHandlePresentation) +
    614                     Objects.hashCode(mCallerDisplayName) +
    615                     Objects.hashCode(mCallerDisplayNamePresentation) +
    616                     Objects.hashCode(mAccountHandle) +
    617                     Objects.hashCode(mCallCapabilities) +
    618                     Objects.hashCode(mCallProperties) +
    619                     Objects.hashCode(mDisconnectCause) +
    620                     Objects.hashCode(mConnectTimeMillis) +
    621                     Objects.hashCode(mGatewayInfo) +
    622                     Objects.hashCode(mVideoState) +
    623                     Objects.hashCode(mStatusHints) +
    624                     Objects.hashCode(mExtras) +
    625                     Objects.hashCode(mIntentExtras);
    626         }
    627 
    628         /** {@hide} */
    629         public Details(
    630                 String telecomCallId,
    631                 Uri handle,
    632                 int handlePresentation,
    633                 String callerDisplayName,
    634                 int callerDisplayNamePresentation,
    635                 PhoneAccountHandle accountHandle,
    636                 int capabilities,
    637                 int properties,
    638                 DisconnectCause disconnectCause,
    639                 long connectTimeMillis,
    640                 GatewayInfo gatewayInfo,
    641                 int videoState,
    642                 StatusHints statusHints,
    643                 Bundle extras,
    644                 Bundle intentExtras) {
    645             mTelecomCallId = telecomCallId;
    646             mHandle = handle;
    647             mHandlePresentation = handlePresentation;
    648             mCallerDisplayName = callerDisplayName;
    649             mCallerDisplayNamePresentation = callerDisplayNamePresentation;
    650             mAccountHandle = accountHandle;
    651             mCallCapabilities = capabilities;
    652             mCallProperties = properties;
    653             mDisconnectCause = disconnectCause;
    654             mConnectTimeMillis = connectTimeMillis;
    655             mGatewayInfo = gatewayInfo;
    656             mVideoState = videoState;
    657             mStatusHints = statusHints;
    658             mExtras = extras;
    659             mIntentExtras = intentExtras;
    660         }
    661 
    662         /** {@hide} */
    663         public static Details createFromParcelableCall(ParcelableCall parcelableCall) {
    664             return new Details(
    665                     parcelableCall.getId(),
    666                     parcelableCall.getHandle(),
    667                     parcelableCall.getHandlePresentation(),
    668                     parcelableCall.getCallerDisplayName(),
    669                     parcelableCall.getCallerDisplayNamePresentation(),
    670                     parcelableCall.getAccountHandle(),
    671                     parcelableCall.getCapabilities(),
    672                     parcelableCall.getProperties(),
    673                     parcelableCall.getDisconnectCause(),
    674                     parcelableCall.getConnectTimeMillis(),
    675                     parcelableCall.getGatewayInfo(),
    676                     parcelableCall.getVideoState(),
    677                     parcelableCall.getStatusHints(),
    678                     parcelableCall.getExtras(),
    679                     parcelableCall.getIntentExtras());
    680         }
    681 
    682         @Override
    683         public String toString() {
    684             StringBuilder sb = new StringBuilder();
    685             sb.append("[pa: ");
    686             sb.append(mAccountHandle);
    687             sb.append(", hdl: ");
    688             sb.append(Log.pii(mHandle));
    689             sb.append(", caps: ");
    690             sb.append(capabilitiesToString(mCallCapabilities));
    691             sb.append(", props: ");
    692             sb.append(propertiesToString(mCallProperties));
    693             sb.append("]");
    694             return sb.toString();
    695         }
    696     }
    697 
    698     public static abstract class Callback {
    699         /**
    700          * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
    701          *
    702          * @param call The {@code Call} invoking this method.
    703          * @param state The new state of the {@code Call}.
    704          */
    705         public void onStateChanged(Call call, int state) {}
    706 
    707         /**
    708          * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
    709          *
    710          * @param call The {@code Call} invoking this method.
    711          * @param parent The new parent of the {@code Call}.
    712          */
    713         public void onParentChanged(Call call, Call parent) {}
    714 
    715         /**
    716          * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
    717          *
    718          * @param call The {@code Call} invoking this method.
    719          * @param children The new children of the {@code Call}.
    720          */
    721         public void onChildrenChanged(Call call, List<Call> children) {}
    722 
    723         /**
    724          * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
    725          *
    726          * @param call The {@code Call} invoking this method.
    727          * @param details A {@code Details} object describing the {@code Call}.
    728          */
    729         public void onDetailsChanged(Call call, Details details) {}
    730 
    731         /**
    732          * Invoked when the text messages that can be used as responses to the incoming
    733          * {@code Call} are loaded from the relevant database.
    734          * See {@link #getCannedTextResponses()}.
    735          *
    736          * @param call The {@code Call} invoking this method.
    737          * @param cannedTextResponses The text messages useable as responses.
    738          */
    739         public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
    740 
    741         /**
    742          * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
    743          * character. This causes the post-dial signals to stop pending user confirmation. An
    744          * implementation should present this choice to the user and invoke
    745          * {@link #postDialContinue(boolean)} when the user makes the choice.
    746          *
    747          * @param call The {@code Call} invoking this method.
    748          * @param remainingPostDialSequence The post-dial characters that remain to be sent.
    749          */
    750         public void onPostDialWait(Call call, String remainingPostDialSequence) {}
    751 
    752         /**
    753          * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
    754          *
    755          * @param call The {@code Call} invoking this method.
    756          * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
    757          */
    758         public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
    759 
    760         /**
    761          * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
    762          * up their UI for the {@code Call} in response to state transitions. Specifically,
    763          * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
    764          * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
    765          * clients should wait for this method to be invoked.
    766          *
    767          * @param call The {@code Call} being destroyed.
    768          */
    769         public void onCallDestroyed(Call call) {}
    770 
    771         /**
    772          * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
    773          * conferenced.
    774          *
    775          * @param call The {@code Call} being updated.
    776          * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
    777          *          conferenced.
    778          */
    779         public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
    780 
    781         /**
    782          * Invoked when a call receives an event from its associated {@link Connection}.
    783          * <p>
    784          * See {@link Connection#sendConnectionEvent(String, Bundle)}.
    785          *
    786          * @param call The {@code Call} receiving the event.
    787          * @param event The event.
    788          * @param extras Extras associated with the connection event.
    789          * @hide
    790          */
    791         public void onConnectionEvent(Call call, String event, Bundle extras) {}
    792     }
    793 
    794     /**
    795      * @deprecated Use {@code Call.Callback} instead.
    796      * @hide
    797      */
    798     @Deprecated
    799     @SystemApi
    800     public static abstract class Listener extends Callback { }
    801 
    802     private final Phone mPhone;
    803     private final String mTelecomCallId;
    804     private final InCallAdapter mInCallAdapter;
    805     private final List<String> mChildrenIds = new ArrayList<>();
    806     private final List<Call> mChildren = new ArrayList<>();
    807     private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
    808     private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
    809     private final List<Call> mConferenceableCalls = new ArrayList<>();
    810     private final List<Call> mUnmodifiableConferenceableCalls =
    811             Collections.unmodifiableList(mConferenceableCalls);
    812 
    813     private boolean mChildrenCached;
    814     private String mParentId = null;
    815     private int mState;
    816     private List<String> mCannedTextResponses = null;
    817     private String mRemainingPostDialSequence;
    818     private VideoCallImpl mVideoCallImpl;
    819     private Details mDetails;
    820     private Bundle mExtras;
    821 
    822     /**
    823      * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
    824      *
    825      * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
    826      * remaining or this {@code Call} is not in a post-dial state.
    827      */
    828     public String getRemainingPostDialSequence() {
    829         return mRemainingPostDialSequence;
    830     }
    831 
    832     /**
    833      * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
    834      * @param videoState The video state in which to answer the call.
    835      */
    836     public void answer(int videoState) {
    837         mInCallAdapter.answerCall(mTelecomCallId, videoState);
    838     }
    839 
    840     /**
    841      * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
    842      *
    843      * @param rejectWithMessage Whether to reject with a text message.
    844      * @param textMessage An optional text message with which to respond.
    845      */
    846     public void reject(boolean rejectWithMessage, String textMessage) {
    847         mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
    848     }
    849 
    850     /**
    851      * Instructs this {@code Call} to disconnect.
    852      */
    853     public void disconnect() {
    854         mInCallAdapter.disconnectCall(mTelecomCallId);
    855     }
    856 
    857     /**
    858      * Instructs this {@code Call} to go on hold.
    859      */
    860     public void hold() {
    861         mInCallAdapter.holdCall(mTelecomCallId);
    862     }
    863 
    864     /**
    865      * Instructs this {@link #STATE_HOLDING} call to release from hold.
    866      */
    867     public void unhold() {
    868         mInCallAdapter.unholdCall(mTelecomCallId);
    869     }
    870 
    871     /**
    872      * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
    873      *
    874      * Any other currently playing DTMF tone in the specified call is immediately stopped.
    875      *
    876      * @param digit A character representing the DTMF digit for which to play the tone. This
    877      *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
    878      */
    879     public void playDtmfTone(char digit) {
    880         mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
    881     }
    882 
    883     /**
    884      * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
    885      * currently playing.
    886      *
    887      * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
    888      * currently playing, this method will do nothing.
    889      */
    890     public void stopDtmfTone() {
    891         mInCallAdapter.stopDtmfTone(mTelecomCallId);
    892     }
    893 
    894     /**
    895      * Instructs this {@code Call} to continue playing a post-dial DTMF string.
    896      *
    897      * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
    898      * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
    899      *
    900      * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
    901      * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
    902      *
    903      * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
    904      * {@code Call} will pause playing the tones and notify callbacks via
    905      * {@link Callback#onPostDialWait(Call, String)}. At this point, the in-call app
    906      * should display to the user an indication of this state and an affordance to continue
    907      * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
    908      * app should invoke the {@link #postDialContinue(boolean)} method.
    909      *
    910      * @param proceed Whether or not to continue with the post-dial sequence.
    911      */
    912     public void postDialContinue(boolean proceed) {
    913         mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
    914     }
    915 
    916     /**
    917      * Notifies this {@code Call} that an account has been selected and to proceed with placing
    918      * an outgoing call. Optionally sets this account as the default account.
    919      */
    920     public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
    921         mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault);
    922 
    923     }
    924 
    925     /**
    926      * Instructs this {@code Call} to enter a conference.
    927      *
    928      * @param callToConferenceWith The other call with which to conference.
    929      */
    930     public void conference(Call callToConferenceWith) {
    931         if (callToConferenceWith != null) {
    932             mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
    933         }
    934     }
    935 
    936     /**
    937      * Instructs this {@code Call} to split from any conference call with which it may be
    938      * connected.
    939      */
    940     public void splitFromConference() {
    941         mInCallAdapter.splitFromConference(mTelecomCallId);
    942     }
    943 
    944     /**
    945      * Merges the calls within this conference. See {@link Details#CAPABILITY_MERGE_CONFERENCE}.
    946      */
    947     public void mergeConference() {
    948         mInCallAdapter.mergeConference(mTelecomCallId);
    949     }
    950 
    951     /**
    952      * Swaps the calls within this conference. See {@link Details#CAPABILITY_SWAP_CONFERENCE}.
    953      */
    954     public void swapConference() {
    955         mInCallAdapter.swapConference(mTelecomCallId);
    956     }
    957 
    958     /**
    959      * Initiates a request to the {@link ConnectionService} to pull an external call to the local
    960      * device.
    961      * <p>
    962      * Calls to this method are ignored if the call does not have the
    963      * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} property set.
    964      * <p>
    965      * An {@link InCallService} will only see calls which support this method if it has the
    966      * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
    967      * in its manifest.
    968      * @hide
    969      */
    970     public void pullExternalCall() {
    971         // If this isn't an external call, ignore the request.
    972         if (!mDetails.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)) {
    973             return;
    974         }
    975 
    976         mInCallAdapter.pullExternalCall(mTelecomCallId);
    977     }
    978 
    979     /**
    980      * Sends a {@code Call} event from this {@code Call} to the associated {@link Connection} in
    981      * the {@link ConnectionService}.
    982      * <p>
    983      * Events are exposed to {@link ConnectionService} implementations via
    984      * {@link android.telecom.Connection#onCallEvent(String, Bundle)}.
    985      * <p>
    986      * No assumptions should be made as to how a {@link ConnectionService} will handle these events.
    987      * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts.
    988      *
    989      * @param event The connection event.
    990      * @param extras Bundle containing extra information associated with the event.
    991      * @hide
    992      */
    993     public void sendCallEvent(String event, Bundle extras) {
    994         mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
    995     }
    996 
    997     /**
    998      * Adds some extras to this {@link Call}.  Existing keys are replaced and new ones are
    999      * added.
   1000      * <p>
   1001      * No assumptions should be made as to how an In-Call UI or service will handle these
   1002      * extras.  Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
   1003      *
   1004      * @param extras The extras to add.
   1005      * @hide
   1006      */
   1007     public final void putExtras(Bundle extras) {
   1008         if (extras == null) {
   1009             return;
   1010         }
   1011 
   1012         if (mExtras == null) {
   1013             mExtras = new Bundle();
   1014         }
   1015         mExtras.putAll(extras);
   1016         mInCallAdapter.putExtras(mTelecomCallId, extras);
   1017     }
   1018 
   1019     /**
   1020      * Adds a boolean extra to this {@link Call}.
   1021      *
   1022      * @param key The extra key.
   1023      * @param value The value.
   1024      * @hide
   1025      */
   1026     public final void putExtra(String key, boolean value) {
   1027         if (mExtras == null) {
   1028             mExtras = new Bundle();
   1029         }
   1030         mExtras.putBoolean(key, value);
   1031         mInCallAdapter.putExtra(mTelecomCallId, key, value);
   1032     }
   1033 
   1034     /**
   1035      * Adds an integer extra to this {@code Connection}.
   1036      *
   1037      * @param key The extra key.
   1038      * @param value The value.
   1039      * @hide
   1040      */
   1041     public final void putExtra(String key, int value) {
   1042         if (mExtras == null) {
   1043             mExtras = new Bundle();
   1044         }
   1045         mExtras.putInt(key, value);
   1046         mInCallAdapter.putExtra(mTelecomCallId, key, value);
   1047     }
   1048 
   1049     /**
   1050      * Adds a string extra to this {@code Connection}.
   1051      *
   1052      * @param key The extra key.
   1053      * @param value The value.
   1054      * @hide
   1055      */
   1056     public final void putExtra(String key, String value) {
   1057         if (mExtras == null) {
   1058             mExtras = new Bundle();
   1059         }
   1060         mExtras.putString(key, value);
   1061         mInCallAdapter.putExtra(mTelecomCallId, key, value);
   1062     }
   1063 
   1064     /**
   1065      * Removes extras from this {@code Connection}.
   1066      *
   1067      * @param keys The keys of the extras to remove.
   1068      * @hide
   1069      */
   1070     public final void removeExtras(List<String> keys) {
   1071         if (mExtras != null) {
   1072             for (String key : keys) {
   1073                 mExtras.remove(key);
   1074             }
   1075             if (mExtras.size() == 0) {
   1076                 mExtras = null;
   1077             }
   1078         }
   1079         mInCallAdapter.removeExtras(mTelecomCallId, keys);
   1080     }
   1081 
   1082     /**
   1083      * Obtains the parent of this {@code Call} in a conference, if any.
   1084      *
   1085      * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
   1086      * child of any conference {@code Call}s.
   1087      */
   1088     public Call getParent() {
   1089         if (mParentId != null) {
   1090             return mPhone.internalGetCallByTelecomId(mParentId);
   1091         }
   1092         return null;
   1093     }
   1094 
   1095     /**
   1096      * Obtains the children of this conference {@code Call}, if any.
   1097      *
   1098      * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
   1099      * {@code List} otherwise.
   1100      */
   1101     public List<Call> getChildren() {
   1102         if (!mChildrenCached) {
   1103             mChildrenCached = true;
   1104             mChildren.clear();
   1105 
   1106             for(String id : mChildrenIds) {
   1107                 Call call = mPhone.internalGetCallByTelecomId(id);
   1108                 if (call == null) {
   1109                     // At least one child was still not found, so do not save true for "cached"
   1110                     mChildrenCached = false;
   1111                 } else {
   1112                     mChildren.add(call);
   1113                 }
   1114             }
   1115         }
   1116 
   1117         return mUnmodifiableChildren;
   1118     }
   1119 
   1120     /**
   1121      * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
   1122      *
   1123      * @return The list of conferenceable {@code Call}s.
   1124      */
   1125     public List<Call> getConferenceableCalls() {
   1126         return mUnmodifiableConferenceableCalls;
   1127     }
   1128 
   1129     /**
   1130      * Obtains the state of this {@code Call}.
   1131      *
   1132      * @return A state value, chosen from the {@code STATE_*} constants.
   1133      */
   1134     public int getState() {
   1135         return mState;
   1136     }
   1137 
   1138     /**
   1139      * Obtains a list of canned, pre-configured message responses to present to the user as
   1140      * ways of rejecting this {@code Call} using via a text message.
   1141      *
   1142      * @see #reject(boolean, String)
   1143      *
   1144      * @return A list of canned text message responses.
   1145      */
   1146     public List<String> getCannedTextResponses() {
   1147         return mCannedTextResponses;
   1148     }
   1149 
   1150     /**
   1151      * Obtains an object that can be used to display video from this {@code Call}.
   1152      *
   1153      * @return An {@code Call.VideoCall}.
   1154      */
   1155     public InCallService.VideoCall getVideoCall() {
   1156         return mVideoCallImpl;
   1157     }
   1158 
   1159     /**
   1160      * Obtains an object containing call details.
   1161      *
   1162      * @return A {@link Details} object. Depending on the state of the {@code Call}, the
   1163      * result may be {@code null}.
   1164      */
   1165     public Details getDetails() {
   1166         return mDetails;
   1167     }
   1168 
   1169     /**
   1170      * Registers a callback to this {@code Call}.
   1171      *
   1172      * @param callback A {@code Callback}.
   1173      */
   1174     public void registerCallback(Callback callback) {
   1175         registerCallback(callback, new Handler());
   1176     }
   1177 
   1178     /**
   1179      * Registers a callback to this {@code Call}.
   1180      *
   1181      * @param callback A {@code Callback}.
   1182      * @param handler A handler which command and status changes will be delivered to.
   1183      */
   1184     public void registerCallback(Callback callback, Handler handler) {
   1185         unregisterCallback(callback);
   1186         // Don't allow new callback registration if the call is already being destroyed.
   1187         if (callback != null && handler != null && mState != STATE_DISCONNECTED) {
   1188             mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler));
   1189         }
   1190     }
   1191 
   1192     /**
   1193      * Unregisters a callback from this {@code Call}.
   1194      *
   1195      * @param callback A {@code Callback}.
   1196      */
   1197     public void unregisterCallback(Callback callback) {
   1198         // Don't allow callback deregistration if the call is already being destroyed.
   1199         if (callback != null && mState != STATE_DISCONNECTED) {
   1200             for (CallbackRecord<Callback> record : mCallbackRecords) {
   1201                 if (record.getCallback() == callback) {
   1202                     mCallbackRecords.remove(record);
   1203                     break;
   1204                 }
   1205             }
   1206         }
   1207     }
   1208 
   1209     @Override
   1210     public String toString() {
   1211         return new StringBuilder().
   1212                 append("Call [id: ").
   1213                 append(mTelecomCallId).
   1214                 append(", state: ").
   1215                 append(stateToString(mState)).
   1216                 append(", details: ").
   1217                 append(mDetails).
   1218                 append("]").toString();
   1219     }
   1220 
   1221     /**
   1222      * @param state An integer value of a {@code STATE_*} constant.
   1223      * @return A string representation of the value.
   1224      */
   1225     private static String stateToString(int state) {
   1226         switch (state) {
   1227             case STATE_NEW:
   1228                 return "NEW";
   1229             case STATE_RINGING:
   1230                 return "RINGING";
   1231             case STATE_DIALING:
   1232                 return "DIALING";
   1233             case STATE_ACTIVE:
   1234                 return "ACTIVE";
   1235             case STATE_HOLDING:
   1236                 return "HOLDING";
   1237             case STATE_DISCONNECTED:
   1238                 return "DISCONNECTED";
   1239             case STATE_CONNECTING:
   1240                 return "CONNECTING";
   1241             case STATE_DISCONNECTING:
   1242                 return "DISCONNECTING";
   1243             case STATE_SELECT_PHONE_ACCOUNT:
   1244                 return "SELECT_PHONE_ACCOUNT";
   1245             default:
   1246                 Log.w(Call.class, "Unknown state %d", state);
   1247                 return "UNKNOWN";
   1248         }
   1249     }
   1250 
   1251     /**
   1252      * Adds a listener to this {@code Call}.
   1253      *
   1254      * @param listener A {@code Listener}.
   1255      * @deprecated Use {@link #registerCallback} instead.
   1256      * @hide
   1257      */
   1258     @Deprecated
   1259     @SystemApi
   1260     public void addListener(Listener listener) {
   1261         registerCallback(listener);
   1262     }
   1263 
   1264     /**
   1265      * Removes a listener from this {@code Call}.
   1266      *
   1267      * @param listener A {@code Listener}.
   1268      * @deprecated Use {@link #unregisterCallback} instead.
   1269      * @hide
   1270      */
   1271     @Deprecated
   1272     @SystemApi
   1273     public void removeListener(Listener listener) {
   1274         unregisterCallback(listener);
   1275     }
   1276 
   1277     /** {@hide} */
   1278     Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
   1279         mPhone = phone;
   1280         mTelecomCallId = telecomCallId;
   1281         mInCallAdapter = inCallAdapter;
   1282         mState = STATE_NEW;
   1283     }
   1284 
   1285     /** {@hide} */
   1286     Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) {
   1287         mPhone = phone;
   1288         mTelecomCallId = telecomCallId;
   1289         mInCallAdapter = inCallAdapter;
   1290         mState = state;
   1291     }
   1292 
   1293     /** {@hide} */
   1294     final String internalGetCallId() {
   1295         return mTelecomCallId;
   1296     }
   1297 
   1298     /** {@hide} */
   1299     final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
   1300         // First, we update the internal state as far as possible before firing any updates.
   1301         Details details = Details.createFromParcelableCall(parcelableCall);
   1302         boolean detailsChanged = !Objects.equals(mDetails, details);
   1303         if (detailsChanged) {
   1304             mDetails = details;
   1305         }
   1306 
   1307         boolean cannedTextResponsesChanged = false;
   1308         if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
   1309                 && !parcelableCall.getCannedSmsResponses().isEmpty()) {
   1310             mCannedTextResponses =
   1311                     Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
   1312             cannedTextResponsesChanged = true;
   1313         }
   1314 
   1315         VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl();
   1316         boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
   1317                 !Objects.equals(mVideoCallImpl, newVideoCallImpl);
   1318         if (videoCallChanged) {
   1319             mVideoCallImpl = newVideoCallImpl;
   1320         }
   1321         if (mVideoCallImpl != null) {
   1322             mVideoCallImpl.setVideoState(getDetails().getVideoState());
   1323         }
   1324 
   1325         int state = parcelableCall.getState();
   1326         boolean stateChanged = mState != state;
   1327         if (stateChanged) {
   1328             mState = state;
   1329         }
   1330 
   1331         String parentId = parcelableCall.getParentCallId();
   1332         boolean parentChanged = !Objects.equals(mParentId, parentId);
   1333         if (parentChanged) {
   1334             mParentId = parentId;
   1335         }
   1336 
   1337         List<String> childCallIds = parcelableCall.getChildCallIds();
   1338         boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
   1339         if (childrenChanged) {
   1340             mChildrenIds.clear();
   1341             mChildrenIds.addAll(parcelableCall.getChildCallIds());
   1342             mChildrenCached = false;
   1343         }
   1344 
   1345         List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
   1346         List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
   1347         for (String otherId : conferenceableCallIds) {
   1348             if (callIdMap.containsKey(otherId)) {
   1349                 conferenceableCalls.add(callIdMap.get(otherId));
   1350             }
   1351         }
   1352 
   1353         if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
   1354             mConferenceableCalls.clear();
   1355             mConferenceableCalls.addAll(conferenceableCalls);
   1356             fireConferenceableCallsChanged();
   1357         }
   1358 
   1359         // Now we fire updates, ensuring that any client who listens to any of these notifications
   1360         // gets the most up-to-date state.
   1361 
   1362         if (stateChanged) {
   1363             fireStateChanged(mState);
   1364         }
   1365         if (detailsChanged) {
   1366             fireDetailsChanged(mDetails);
   1367         }
   1368         if (cannedTextResponsesChanged) {
   1369             fireCannedTextResponsesLoaded(mCannedTextResponses);
   1370         }
   1371         if (videoCallChanged) {
   1372             fireVideoCallChanged(mVideoCallImpl);
   1373         }
   1374         if (parentChanged) {
   1375             fireParentChanged(getParent());
   1376         }
   1377         if (childrenChanged) {
   1378             fireChildrenChanged(getChildren());
   1379         }
   1380 
   1381         // If we have transitioned to DISCONNECTED, that means we need to notify clients and
   1382         // remove ourselves from the Phone. Note that we do this after completing all state updates
   1383         // so a client can cleanly transition all their UI to the state appropriate for a
   1384         // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
   1385         if (mState == STATE_DISCONNECTED) {
   1386             fireCallDestroyed();
   1387         }
   1388     }
   1389 
   1390     /** {@hide} */
   1391     final void internalSetPostDialWait(String remaining) {
   1392         mRemainingPostDialSequence = remaining;
   1393         firePostDialWait(mRemainingPostDialSequence);
   1394     }
   1395 
   1396     /** {@hide} */
   1397     final void internalSetDisconnected() {
   1398         if (mState != Call.STATE_DISCONNECTED) {
   1399             mState = Call.STATE_DISCONNECTED;
   1400             fireStateChanged(mState);
   1401             fireCallDestroyed();
   1402         }
   1403     }
   1404 
   1405     /** {@hide} */
   1406     final void internalOnConnectionEvent(String event, Bundle extras) {
   1407         fireOnConnectionEvent(event, extras);
   1408     }
   1409 
   1410     private void fireStateChanged(final int newState) {
   1411         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1412             final Call call = this;
   1413             final Callback callback = record.getCallback();
   1414             record.getHandler().post(new Runnable() {
   1415                 @Override
   1416                 public void run() {
   1417                     callback.onStateChanged(call, newState);
   1418                 }
   1419             });
   1420         }
   1421     }
   1422 
   1423     private void fireParentChanged(final Call newParent) {
   1424         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1425             final Call call = this;
   1426             final Callback callback = record.getCallback();
   1427             record.getHandler().post(new Runnable() {
   1428                 @Override
   1429                 public void run() {
   1430                     callback.onParentChanged(call, newParent);
   1431                 }
   1432             });
   1433         }
   1434     }
   1435 
   1436     private void fireChildrenChanged(final List<Call> children) {
   1437         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1438             final Call call = this;
   1439             final Callback callback = record.getCallback();
   1440             record.getHandler().post(new Runnable() {
   1441                 @Override
   1442                 public void run() {
   1443                     callback.onChildrenChanged(call, children);
   1444                 }
   1445             });
   1446         }
   1447     }
   1448 
   1449     private void fireDetailsChanged(final Details details) {
   1450         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1451             final Call call = this;
   1452             final Callback callback = record.getCallback();
   1453             record.getHandler().post(new Runnable() {
   1454                 @Override
   1455                 public void run() {
   1456                     callback.onDetailsChanged(call, details);
   1457                 }
   1458             });
   1459         }
   1460     }
   1461 
   1462     private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) {
   1463         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1464             final Call call = this;
   1465             final Callback callback = record.getCallback();
   1466             record.getHandler().post(new Runnable() {
   1467                 @Override
   1468                 public void run() {
   1469                     callback.onCannedTextResponsesLoaded(call, cannedTextResponses);
   1470                 }
   1471             });
   1472         }
   1473     }
   1474 
   1475     private void fireVideoCallChanged(final InCallService.VideoCall videoCall) {
   1476         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1477             final Call call = this;
   1478             final Callback callback = record.getCallback();
   1479             record.getHandler().post(new Runnable() {
   1480                 @Override
   1481                 public void run() {
   1482                     callback.onVideoCallChanged(call, videoCall);
   1483                 }
   1484             });
   1485         }
   1486     }
   1487 
   1488     private void firePostDialWait(final String remainingPostDialSequence) {
   1489         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1490             final Call call = this;
   1491             final Callback callback = record.getCallback();
   1492             record.getHandler().post(new Runnable() {
   1493                 @Override
   1494                 public void run() {
   1495                     callback.onPostDialWait(call, remainingPostDialSequence);
   1496                 }
   1497             });
   1498         }
   1499     }
   1500 
   1501     private void fireCallDestroyed() {
   1502         /**
   1503          * To preserve the ordering of the Call's onCallDestroyed callback and Phone's
   1504          * onCallRemoved callback, we remove this call from the Phone's record
   1505          * only once all of the registered onCallDestroyed callbacks are executed.
   1506          * All the callbacks get removed from our records as a part of this operation
   1507          * since onCallDestroyed is the final callback.
   1508          */
   1509         final Call call = this;
   1510         if (mCallbackRecords.isEmpty()) {
   1511             // No callbacks registered, remove the call from Phone's record.
   1512             mPhone.internalRemoveCall(call);
   1513         }
   1514         for (final CallbackRecord<Callback> record : mCallbackRecords) {
   1515             final Callback callback = record.getCallback();
   1516             record.getHandler().post(new Runnable() {
   1517                 @Override
   1518                 public void run() {
   1519                     boolean isFinalRemoval = false;
   1520                     RuntimeException toThrow = null;
   1521                     try {
   1522                         callback.onCallDestroyed(call);
   1523                     } catch (RuntimeException e) {
   1524                             toThrow = e;
   1525                     }
   1526                     synchronized(Call.this) {
   1527                         mCallbackRecords.remove(record);
   1528                         if (mCallbackRecords.isEmpty()) {
   1529                             isFinalRemoval = true;
   1530                         }
   1531                     }
   1532                     if (isFinalRemoval) {
   1533                         mPhone.internalRemoveCall(call);
   1534                     }
   1535                     if (toThrow != null) {
   1536                         throw toThrow;
   1537                     }
   1538                 }
   1539             });
   1540         }
   1541     }
   1542 
   1543     private void fireConferenceableCallsChanged() {
   1544         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1545             final Call call = this;
   1546             final Callback callback = record.getCallback();
   1547             record.getHandler().post(new Runnable() {
   1548                 @Override
   1549                 public void run() {
   1550                     callback.onConferenceableCallsChanged(call, mUnmodifiableConferenceableCalls);
   1551                 }
   1552             });
   1553         }
   1554     }
   1555 
   1556     /**
   1557      * Notifies listeners of an incoming connection event.
   1558      * <p>
   1559      * Connection events are issued via {@link Connection#sendConnectionEvent(String, Bundle)}.
   1560      *
   1561      * @param event
   1562      * @param extras
   1563      */
   1564     private void fireOnConnectionEvent(final String event, final Bundle extras) {
   1565         for (CallbackRecord<Callback> record : mCallbackRecords) {
   1566             final Call call = this;
   1567             final Callback callback = record.getCallback();
   1568             record.getHandler().post(new Runnable() {
   1569                 @Override
   1570                 public void run() {
   1571                     callback.onConnectionEvent(call, event, extras);
   1572                 }
   1573             });
   1574         }
   1575     }
   1576 
   1577     /**
   1578      * Determines if two bundles are equal.
   1579      *
   1580      * @param bundle The original bundle.
   1581      * @param newBundle The bundle to compare with.
   1582      * @retrun {@code true} if the bundles are equal, {@code false} otherwise.
   1583      */
   1584     private static boolean areBundlesEqual(Bundle bundle, Bundle newBundle) {
   1585         if (bundle == null || newBundle == null) {
   1586             return bundle == newBundle;
   1587         }
   1588 
   1589         if (bundle.size() != newBundle.size()) {
   1590             return false;
   1591         }
   1592 
   1593         for(String key : bundle.keySet()) {
   1594             if (key != null) {
   1595                 final Object value = bundle.get(key);
   1596                 final Object newValue = newBundle.get(key);
   1597                 if (!Objects.equals(value, newValue)) {
   1598                     return false;
   1599                 }
   1600             }
   1601         }
   1602         return true;
   1603     }
   1604 }
   1605