Home | History | Annotate | Download | only in imsphone
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.internal.telephony.imsphone;
     18 
     19 import com.android.internal.R;
     20 import com.android.internal.telephony.Call;
     21 import com.android.internal.telephony.CallStateException;
     22 import com.android.internal.telephony.Connection;
     23 import com.android.internal.telephony.Phone;
     24 import com.android.internal.telephony.PhoneConstants;
     25 import com.android.internal.telephony.UUSInfo;
     26 
     27 import android.content.Context;
     28 import android.net.Uri;
     29 import android.telecom.PhoneAccount;
     30 import android.telephony.PhoneNumberUtils;
     31 import android.telephony.Rlog;
     32 import android.util.Log;
     33 
     34 import java.util.Collections;
     35 import java.util.List;
     36 import java.util.Set;
     37 import java.util.concurrent.ConcurrentHashMap;
     38 
     39 /**
     40  * Represents an IMS call external to the device.  This class is used to represent a call which
     41  * takes places on a secondary device associated with this one.  Originates from a Dialog Event
     42  * Package.
     43  *
     44  * Dialog event package information is received from the IMS framework via
     45  * {@link com.android.ims.ImsExternalCallState} instances.
     46  *
     47  * @hide
     48  */
     49 public class ImsExternalConnection extends Connection {
     50 
     51     private static final String CONFERENCE_PREFIX = "conf";
     52     private final Context mContext;
     53 
     54     public interface Listener {
     55         void onPullExternalCall(ImsExternalConnection connection);
     56     }
     57 
     58     /**
     59      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
     60      * load factor before resizing, 1 means we only expect a single thread to
     61      * access the map so make only a single shard
     62      */
     63     private final Set<Listener> mListeners = Collections.newSetFromMap(
     64             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
     65 
     66     /**
     67      * The unqiue dialog event package specified ID associated with this external connection.
     68      */
     69     private int mCallId;
     70 
     71     /**
     72      * A backing call associated with this external connection.
     73      */
     74     private ImsExternalCall mCall;
     75 
     76     /**
     77      * The original address as contained in the dialog event package.
     78      */
     79     private Uri mOriginalAddress;
     80 
     81     /**
     82      * Determines if the call is pullable.
     83      */
     84     private boolean mIsPullable;
     85 
     86     protected ImsExternalConnection(Phone phone, int callId, Uri address, boolean isPullable) {
     87         super(phone.getPhoneType());
     88         mContext = phone.getContext();
     89         mCall = new ImsExternalCall(phone, this);
     90         mCallId = callId;
     91         setExternalConnectionAddress(address);
     92         mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
     93         mIsPullable = isPullable;
     94 
     95         rebuildCapabilities();
     96         setActive();
     97     }
     98 
     99     /**
    100      * @return the unique ID of this connection from the dialog event package data.
    101      */
    102     public int getCallId() {
    103         return mCallId;
    104     }
    105 
    106     @Override
    107     public Call getCall() {
    108         return mCall;
    109     }
    110 
    111     @Override
    112     public long getDisconnectTime() {
    113         return 0;
    114     }
    115 
    116     @Override
    117     public long getHoldDurationMillis() {
    118         return 0;
    119     }
    120 
    121     @Override
    122     public String getVendorDisconnectCause() {
    123         return null;
    124     }
    125 
    126     @Override
    127     public void hangup() throws CallStateException {
    128         // No-op - Hangup is not supported for external calls.
    129     }
    130 
    131     @Override
    132     public void separate() throws CallStateException {
    133         // No-op - Separate is not supported for external calls.
    134     }
    135 
    136     @Override
    137     public void proceedAfterWaitChar() {
    138         // No-op - not supported for external calls.
    139     }
    140 
    141     @Override
    142     public void proceedAfterWildChar(String str) {
    143         // No-op - not supported for external calls.
    144     }
    145 
    146     @Override
    147     public void cancelPostDial() {
    148         // No-op - not supported for external calls.
    149     }
    150 
    151     @Override
    152     public int getNumberPresentation() {
    153         return mNumberPresentation;
    154     }
    155 
    156     @Override
    157     public UUSInfo getUUSInfo() {
    158         return null;
    159     }
    160 
    161     @Override
    162     public int getPreciseDisconnectCause() {
    163         return 0;
    164     }
    165 
    166     @Override
    167     public boolean isMultiparty() {
    168         return false;
    169     }
    170 
    171     /**
    172      * Called by a {@link android.telecom.Connection} to indicate that this call should be pulled
    173      * to the local device.
    174      *
    175      * Informs all listeners, in this case {@link ImsExternalCallTracker}, of the request to pull
    176      * the call.
    177      */
    178     @Override
    179     public void pullExternalCall() {
    180         for (Listener listener : mListeners) {
    181             listener.onPullExternalCall(this);
    182         }
    183     }
    184 
    185     /**
    186      * Sets this external call as active.
    187      */
    188     public void setActive() {
    189         if (mCall == null) {
    190             return;
    191         }
    192         mCall.setActive();
    193     }
    194 
    195     /**
    196      * Sets this external call as terminated.
    197      */
    198     public void setTerminated() {
    199         if (mCall == null) {
    200             return;
    201         }
    202 
    203         mCall.setTerminated();
    204     }
    205 
    206     /**
    207      * Changes whether the call can be pulled or not.
    208      *
    209      * @param isPullable {@code true} if the call can be pulled, {@code false} otherwise.
    210      */
    211     public void setIsPullable(boolean isPullable) {
    212         mIsPullable = isPullable;
    213         rebuildCapabilities();
    214     }
    215 
    216     /**
    217      * Sets the address of this external connection.  Ensures that dialog event package SIP
    218      * {@link Uri}s are converted to a regular telephone number.
    219      *
    220      * @param address The address from the dialog event package.
    221      */
    222     public void setExternalConnectionAddress(Uri address) {
    223         mOriginalAddress = address;
    224 
    225         if (PhoneAccount.SCHEME_SIP.equals(address.getScheme())) {
    226             if (address.getSchemeSpecificPart().startsWith(CONFERENCE_PREFIX)) {
    227                 mCnapName = mContext.getString(com.android.internal.R.string.conference_call);
    228                 mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
    229                 mAddress = "";
    230                 mNumberPresentation = PhoneConstants.PRESENTATION_RESTRICTED;
    231                 return;
    232             }
    233         }
    234         Uri telUri = PhoneNumberUtils.convertSipUriToTelUri(address);
    235         mAddress = telUri.getSchemeSpecificPart();
    236     }
    237 
    238     public void addListener(Listener listener) {
    239         mListeners.add(listener);
    240     }
    241 
    242     public void removeListener(Listener listener) {
    243         mListeners.remove(listener);
    244     }
    245 
    246     /**
    247      * Build a human representation of a connection instance, suitable for debugging.
    248      * Don't log personal stuff unless in debug mode.
    249      * @return a string representing the internal state of this connection.
    250      */
    251     public String toString() {
    252         StringBuilder str = new StringBuilder(128);
    253         str.append("[ImsExternalConnection dialogCallId:");
    254         str.append(mCallId);
    255         str.append(" state:");
    256         if (mCall.getState() == Call.State.ACTIVE) {
    257             str.append("Active");
    258         } else if (mCall.getState() == Call.State.DISCONNECTED) {
    259             str.append("Disconnected");
    260         }
    261         str.append("]");
    262         return str.toString();
    263     }
    264 
    265     /**
    266      * Rebuilds the connection capabilities.
    267      */
    268     private void rebuildCapabilities() {
    269         int capabilities = Capability.IS_EXTERNAL_CONNECTION;
    270         if (mIsPullable) {
    271             capabilities |= Capability.IS_PULLABLE;
    272         }
    273 
    274         setConnectionCapabilities(capabilities);
    275     }
    276 }
    277