Home | History | Annotate | Download | only in imsphone
      1 /*
      2  * Copyright (C) 2013 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 android.telephony.Rlog;
     20 import android.telephony.DisconnectCause;
     21 
     22 import com.android.internal.annotations.VisibleForTesting;
     23 import com.android.internal.telephony.Call;
     24 import com.android.internal.telephony.CallStateException;
     25 import com.android.internal.telephony.Connection;
     26 import com.android.internal.telephony.Phone;
     27 import com.android.ims.ImsCall;
     28 import com.android.ims.ImsException;
     29 import com.android.ims.ImsStreamMediaProfile;
     30 
     31 import java.util.List;
     32 
     33 /**
     34  * {@hide}
     35  */
     36 public class ImsPhoneCall extends Call {
     37     /*************************** Instance Variables **************************/
     38 
     39     private static final String LOG_TAG = "ImsPhoneCall";
     40 
     41     /*package*/ ImsPhoneCallTracker mOwner;
     42 
     43     private boolean mRingbackTonePlayed = false;
     44 
     45     /****************************** Constructors *****************************/
     46     /*package*/
     47     ImsPhoneCall() {
     48     }
     49 
     50     /*package*/
     51     ImsPhoneCall(ImsPhoneCallTracker owner) {
     52         mOwner = owner;
     53     }
     54 
     55     public void dispose() {
     56         try {
     57             mOwner.hangup(this);
     58         } catch (CallStateException ex) {
     59             //Rlog.e(LOG_TAG, "dispose: unexpected error on hangup", ex);
     60             //while disposing, ignore the exception and clean the connections
     61         } finally {
     62             for(int i = 0, s = mConnections.size(); i < s; i++) {
     63                 ImsPhoneConnection c = (ImsPhoneConnection) mConnections.get(i);
     64                 c.onDisconnect(DisconnectCause.LOST_SIGNAL);
     65             }
     66         }
     67     }
     68 
     69     /************************** Overridden from Call *************************/
     70 
     71     @Override
     72     public List<Connection>
     73     getConnections() {
     74         return mConnections;
     75     }
     76 
     77     @Override
     78     public Phone
     79     getPhone() {
     80         return mOwner.mPhone;
     81     }
     82 
     83     @Override
     84     public boolean
     85     isMultiparty() {
     86         ImsCall imsCall = getImsCall();
     87         if (imsCall == null) {
     88             return false;
     89         }
     90 
     91         return imsCall.isMultiparty();
     92     }
     93 
     94     /** Please note: if this is the foreground call and a
     95      *  background call exists, the background call will be resumed.
     96      */
     97     @Override
     98     public void
     99     hangup() throws CallStateException {
    100         mOwner.hangup(this);
    101     }
    102 
    103     @Override
    104     public String
    105     toString() {
    106         return mState.toString();
    107     }
    108 
    109     //***** Called from ImsPhoneConnection
    110 
    111     /*package*/ void
    112     attach(Connection conn) {
    113         clearDisconnected();
    114         mConnections.add(conn);
    115     }
    116 
    117     /*package*/ void
    118     attach(Connection conn, State state) {
    119         this.attach(conn);
    120         mState = state;
    121     }
    122 
    123     /*package*/ void
    124     attachFake(Connection conn, State state) {
    125         attach(conn, state);
    126     }
    127 
    128     /**
    129      * Called by ImsPhoneConnection when it has disconnected
    130      */
    131     boolean
    132     connectionDisconnected(ImsPhoneConnection conn) {
    133         if (mState != State.DISCONNECTED) {
    134             /* If only disconnected connections remain, we are disconnected*/
    135 
    136             boolean hasOnlyDisconnectedConnections = true;
    137 
    138             for (int i = 0, s = mConnections.size()  ; i < s; i ++) {
    139                 if (mConnections.get(i).getState() != State.DISCONNECTED) {
    140                     hasOnlyDisconnectedConnections = false;
    141                     break;
    142                 }
    143             }
    144 
    145             if (hasOnlyDisconnectedConnections) {
    146                 mState = State.DISCONNECTED;
    147                 return true;
    148             }
    149         }
    150 
    151         return false;
    152     }
    153 
    154     /*package*/ void
    155     detach(ImsPhoneConnection conn) {
    156         mConnections.remove(conn);
    157         clearDisconnected();
    158     }
    159 
    160     /**
    161      * @return true if there's no space in this call for additional
    162      * connections to be added via "conference"
    163      */
    164     /*package*/ boolean
    165     isFull() {
    166         return mConnections.size() == ImsPhoneCallTracker.MAX_CONNECTIONS_PER_CALL;
    167     }
    168 
    169     //***** Called from ImsPhoneCallTracker
    170     /**
    171      * Called when this Call is being hung up locally (eg, user pressed "end")
    172      */
    173     void
    174     onHangupLocal() {
    175         for (int i = 0, s = mConnections.size(); i < s; i++) {
    176             ImsPhoneConnection cn = (ImsPhoneConnection)mConnections.get(i);
    177             cn.onHangupLocal();
    178         }
    179         mState = State.DISCONNECTING;
    180     }
    181 
    182     /**
    183      * Called when it's time to clean up disconnected Connection objects
    184      */
    185     void
    186     clearDisconnected() {
    187         for (int i = mConnections.size() - 1 ; i >= 0 ; i--) {
    188             ImsPhoneConnection cn = (ImsPhoneConnection)mConnections.get(i);
    189 
    190             if (cn.getState() == State.DISCONNECTED) {
    191                 mConnections.remove(i);
    192             }
    193         }
    194 
    195         if (mConnections.size() == 0) {
    196             mState = State.IDLE;
    197         }
    198     }
    199 
    200     /*package*/ ImsPhoneConnection
    201     getFirstConnection() {
    202         if (mConnections.size() == 0) return null;
    203 
    204         return (ImsPhoneConnection) mConnections.get(0);
    205     }
    206 
    207     /*package*/ void
    208     setMute(boolean mute) {
    209         ImsCall imsCall = getFirstConnection() == null ?
    210                 null : getFirstConnection().getImsCall();
    211         if (imsCall != null) {
    212             try {
    213                 imsCall.setMute(mute);
    214             } catch (ImsException e) {
    215                 Rlog.e(LOG_TAG, "setMute failed : " + e.getMessage());
    216             }
    217         }
    218     }
    219 
    220     /* package */ void
    221     merge(ImsPhoneCall that, State state) {
    222         // This call is the conference host and the "that" call is the one being merged in.
    223         // Set the connect time for the conference; this will have been determined when the
    224         // conference was initially created.
    225         ImsPhoneConnection imsPhoneConnection = getFirstConnection();
    226         if (imsPhoneConnection != null) {
    227             long conferenceConnectTime = imsPhoneConnection.getConferenceConnectTime();
    228             if (conferenceConnectTime > 0) {
    229                 imsPhoneConnection.setConnectTime(conferenceConnectTime);
    230             }
    231         }
    232 
    233         ImsPhoneConnection[] cc = that.mConnections.toArray(
    234                 new ImsPhoneConnection[that.mConnections.size()]);
    235         for (ImsPhoneConnection c : cc) {
    236             c.update(null, state);
    237         }
    238     }
    239 
    240     /**
    241      * Retrieves the {@link ImsCall} for the current {@link ImsPhoneCall}.
    242      * <p>
    243      * Marked as {@code VisibleForTesting} so that the
    244      * {@link com.android.internal.telephony.TelephonyTester} class can inject a test conference
    245      * event package into a regular ongoing IMS call.
    246      *
    247      * @return The {@link ImsCall}.
    248      */
    249     @VisibleForTesting
    250     public ImsCall
    251     getImsCall() {
    252         return (getFirstConnection() == null) ? null : getFirstConnection().getImsCall();
    253     }
    254 
    255     /*package*/ static boolean isLocalTone(ImsCall imsCall) {
    256         if ((imsCall == null) || (imsCall.getCallProfile() == null)
    257                 || (imsCall.getCallProfile().mMediaProfile == null)) {
    258             return false;
    259         }
    260 
    261         ImsStreamMediaProfile mediaProfile = imsCall.getCallProfile().mMediaProfile;
    262 
    263         return (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE)
    264                 ? true : false;
    265     }
    266 
    267     /*package*/ boolean
    268     update (ImsPhoneConnection conn, ImsCall imsCall, State state) {
    269         State newState = state;
    270         boolean changed = false;
    271 
    272         //ImsCall.Listener.onCallProgressing can be invoked several times
    273         //and ringback tone mode can be changed during the call setup procedure
    274         if (state == State.ALERTING) {
    275             if (mRingbackTonePlayed && !isLocalTone(imsCall)) {
    276                 mOwner.mPhone.stopRingbackTone();
    277                 mRingbackTonePlayed = false;
    278             } else if (!mRingbackTonePlayed && isLocalTone(imsCall)) {
    279                 mOwner.mPhone.startRingbackTone();
    280                 mRingbackTonePlayed = true;
    281             }
    282         } else {
    283             if (mRingbackTonePlayed) {
    284                 mOwner.mPhone.stopRingbackTone();
    285                 mRingbackTonePlayed = false;
    286             }
    287         }
    288 
    289         if ((newState != mState) && (state != State.DISCONNECTED)) {
    290             mState = newState;
    291             changed = true;
    292         } else if (state == State.DISCONNECTED) {
    293             changed = true;
    294         }
    295 
    296         return changed;
    297     }
    298 
    299     /* package */ ImsPhoneConnection
    300     getHandoverConnection() {
    301         return (ImsPhoneConnection) getEarliestConnection();
    302     }
    303 
    304     void switchWith(ImsPhoneCall that) {
    305         synchronized (ImsPhoneCall.class) {
    306             ImsPhoneCall tmp = new ImsPhoneCall();
    307             tmp.takeOver(this);
    308             this.takeOver(that);
    309             that.takeOver(tmp);
    310         }
    311     }
    312 
    313     private void takeOver(ImsPhoneCall that) {
    314         mConnections = that.mConnections;
    315         mState = that.mState;
    316         for (Connection c : mConnections) {
    317             ((ImsPhoneConnection) c).changeParent(this);
    318         }
    319     }
    320 }
    321