Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2012 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.phone;
     18 
     19 import android.app.Service;
     20 import android.bluetooth.BluetoothAdapter;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.bluetooth.BluetoothHeadset;
     23 import android.bluetooth.BluetoothProfile;
     24 import android.bluetooth.IBluetoothHeadsetPhone;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.os.AsyncResult;
     28 import android.os.Handler;
     29 import android.os.IBinder;
     30 import android.os.Message;
     31 import android.os.PowerManager;
     32 import android.os.PowerManager.WakeLock;
     33 import android.os.SystemProperties;
     34 import android.telephony.PhoneNumberUtils;
     35 import android.telephony.ServiceState;
     36 import android.util.Log;
     37 
     38 import com.android.internal.telephony.Call;
     39 import com.android.internal.telephony.Connection;
     40 import com.android.internal.telephony.Phone;
     41 import com.android.internal.telephony.PhoneConstants;
     42 import com.android.internal.telephony.TelephonyIntents;
     43 import com.android.internal.telephony.CallManager;
     44 
     45 import java.io.IOException;
     46 import java.util.LinkedList;
     47 import java.util.List;
     48 
     49 /**
     50  * Bluetooth headset manager for the Phone app.
     51  * @hide
     52  */
     53 public class BluetoothPhoneService extends Service {
     54     private static final String TAG = "BluetoothPhoneService";
     55     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 1)
     56             && (SystemProperties.getInt("ro.debuggable", 0) == 1);
     57     private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);  // even more logging
     58 
     59     private static final String MODIFY_PHONE_STATE = android.Manifest.permission.MODIFY_PHONE_STATE;
     60 
     61     private BluetoothAdapter mAdapter;
     62     private CallManager mCM;
     63 
     64     private BluetoothHeadset mBluetoothHeadset;
     65 
     66     private PowerManager mPowerManager;
     67 
     68     private WakeLock mStartCallWakeLock;  // held while waiting for the intent to start call
     69 
     70     private PhoneConstants.State mPhoneState = PhoneConstants.State.IDLE;
     71     CdmaPhoneCallState.PhoneCallState mCdmaThreeWayCallState =
     72                                             CdmaPhoneCallState.PhoneCallState.IDLE;
     73 
     74     private Call.State mForegroundCallState;
     75     private Call.State mRingingCallState;
     76     private CallNumber mRingNumber;
     77     // number of active calls
     78     int mNumActive;
     79     // number of background (held) calls
     80     int mNumHeld;
     81 
     82     long mBgndEarliestConnectionTime = 0;
     83 
     84     private boolean mRoam = false;
     85 
     86     // CDMA specific flag used in context with BT devices having display capabilities
     87     // to show which Caller is active. This state might not be always true as in CDMA
     88     // networks if a caller drops off no update is provided to the Phone.
     89     // This flag is just used as a toggle to provide a update to the BT device to specify
     90     // which caller is active.
     91     private boolean mCdmaIsSecondCallActive = false;
     92     private boolean mCdmaCallsSwapped = false;
     93 
     94     private long[] mClccTimestamps; // Timestamps associated with each clcc index
     95     private boolean[] mClccUsed;     // Is this clcc index in use
     96 
     97     private static final int GSM_MAX_CONNECTIONS = 6;  // Max connections allowed by GSM
     98     private static final int CDMA_MAX_CONNECTIONS = 2;  // Max connections allowed by CDMA
     99 
    100     @Override
    101     public void onCreate() {
    102         super.onCreate();
    103         mCM = CallManager.getInstance();
    104         mAdapter = BluetoothAdapter.getDefaultAdapter();
    105         if (mAdapter == null) {
    106             if (VDBG) Log.d(TAG, "mAdapter null");
    107             return;
    108         }
    109 
    110         mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    111         mStartCallWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    112                                                        TAG + ":StartCall");
    113         mStartCallWakeLock.setReferenceCounted(false);
    114 
    115         mAdapter.getProfileProxy(this, mProfileListener, BluetoothProfile.HEADSET);
    116 
    117         mForegroundCallState = Call.State.IDLE;
    118         mRingingCallState = Call.State.IDLE;
    119         mNumActive = 0;
    120         mNumHeld = 0;
    121         mRingNumber = new CallNumber("", 0);;
    122         mRoam = false;
    123 
    124         updateServiceState(mCM.getDefaultPhone().getServiceState());
    125         handlePreciseCallStateChange(null);
    126 
    127         if(VDBG) Log.d(TAG, "registerForServiceStateChanged");
    128         // register for updates
    129         // Use the service state of default phone as BT service state to
    130         // avoid situation such as no cell or wifi connection but still
    131         // reporting in service (since SipPhone always reports in service).
    132         mCM.getDefaultPhone().registerForServiceStateChanged(mHandler,
    133                                                              SERVICE_STATE_CHANGED, null);
    134         mCM.registerForPreciseCallStateChanged(mHandler,
    135                                                PRECISE_CALL_STATE_CHANGED, null);
    136         mCM.registerForCallWaiting(mHandler,
    137                                    PHONE_CDMA_CALL_WAITING, null);
    138         // TODO(BT) registerForIncomingRing?
    139         // TODO(BT) registerdisconnection?
    140         mClccTimestamps = new long[GSM_MAX_CONNECTIONS];
    141         mClccUsed = new boolean[GSM_MAX_CONNECTIONS];
    142         for (int i = 0; i < GSM_MAX_CONNECTIONS; i++) {
    143             mClccUsed[i] = false;
    144         }
    145     }
    146 
    147     @Override
    148     public void onStart(Intent intent, int startId) {
    149         if (mAdapter == null) {
    150             Log.w(TAG, "Stopping Bluetooth BluetoothPhoneService Service: device does not have BT");
    151             stopSelf();
    152         }
    153         if (VDBG) Log.d(TAG, "BluetoothPhoneService started");
    154     }
    155 
    156     @Override
    157     public void onDestroy() {
    158         super.onDestroy();
    159         if (DBG) log("Stopping Bluetooth BluetoothPhoneService Service");
    160     }
    161 
    162     @Override
    163     public IBinder onBind(Intent intent) {
    164         return mBinder;
    165     }
    166 
    167     private static final int SERVICE_STATE_CHANGED = 1;
    168     private static final int PRECISE_CALL_STATE_CHANGED = 2;
    169     private static final int PHONE_CDMA_CALL_WAITING = 3;
    170     private static final int LIST_CURRENT_CALLS = 4;
    171     private static final int QUERY_PHONE_STATE = 5;
    172     private static final int CDMA_SWAP_SECOND_CALL_STATE = 6;
    173     private static final int CDMA_SET_SECOND_CALL_STATE = 7;
    174 
    175     private Handler mHandler = new Handler() {
    176         @Override
    177         public void handleMessage(Message msg) {
    178             if (VDBG) Log.d(TAG, "handleMessage: " + msg.what);
    179             switch(msg.what) {
    180                 case SERVICE_STATE_CHANGED:
    181                     ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
    182                     updateServiceState(state);
    183                     break;
    184                 case PRECISE_CALL_STATE_CHANGED:
    185                 case PHONE_CDMA_CALL_WAITING:
    186                     Connection connection = null;
    187                     if (((AsyncResult) msg.obj).result instanceof Connection) {
    188                         connection = (Connection) ((AsyncResult) msg.obj).result;
    189                     }
    190                     handlePreciseCallStateChange(connection);
    191                     break;
    192                 case LIST_CURRENT_CALLS:
    193                     handleListCurrentCalls();
    194                     break;
    195                 case QUERY_PHONE_STATE:
    196                     handleQueryPhoneState();
    197                     break;
    198                 case CDMA_SWAP_SECOND_CALL_STATE:
    199                     handleCdmaSwapSecondCallState();
    200                     break;
    201                 case CDMA_SET_SECOND_CALL_STATE:
    202                     handleCdmaSetSecondCallState((Boolean) msg.obj);
    203                     break;
    204             }
    205         }
    206     };
    207 
    208     private void updateBtPhoneStateAfterRadioTechnologyChange() {
    209         if(VDBG) Log.d(TAG, "updateBtPhoneStateAfterRadioTechnologyChange...");
    210 
    211         //Unregister all events from the old obsolete phone
    212         mCM.getDefaultPhone().unregisterForServiceStateChanged(mHandler);
    213         mCM.unregisterForPreciseCallStateChanged(mHandler);
    214         mCM.unregisterForCallWaiting(mHandler);
    215 
    216         //Register all events new to the new active phone
    217         mCM.getDefaultPhone().registerForServiceStateChanged(mHandler,
    218                                                              SERVICE_STATE_CHANGED, null);
    219         mCM.registerForPreciseCallStateChanged(mHandler,
    220                                                PRECISE_CALL_STATE_CHANGED, null);
    221         mCM.registerForCallWaiting(mHandler,
    222                                    PHONE_CDMA_CALL_WAITING, null);
    223     }
    224 
    225     private void updateServiceState(ServiceState state) {
    226         boolean roam = state.getRoaming();
    227 
    228         if (roam != mRoam) {
    229             mRoam = roam;
    230             if (mBluetoothHeadset != null) {
    231                 mBluetoothHeadset.roamChanged(roam);
    232             }
    233         }
    234     }
    235 
    236     private void handlePreciseCallStateChange(Connection connection) {
    237         // get foreground call state
    238         int oldNumActive = mNumActive;
    239         int oldNumHeld = mNumHeld;
    240         Call.State oldRingingCallState = mRingingCallState;
    241         Call.State oldForegroundCallState = mForegroundCallState;
    242         CallNumber oldRingNumber = mRingNumber;
    243 
    244         Call foregroundCall = mCM.getActiveFgCall();
    245 
    246         if (VDBG)
    247             Log.d(TAG, " handlePreciseCallStateChange: foreground: " + foregroundCall +
    248                 " background: " + mCM.getFirstActiveBgCall() + " ringing: " +
    249                 mCM.getFirstActiveRingingCall());
    250 
    251         mForegroundCallState = foregroundCall.getState();
    252         /* if in transition, do not update */
    253         if (mForegroundCallState == Call.State.DISCONNECTING)
    254         {
    255             Log.d(TAG, "handlePreciseCallStateChange. Call disconnecting, wait before update");
    256             return;
    257         }
    258         else
    259             mNumActive = (mForegroundCallState == Call.State.ACTIVE) ? 1 : 0;
    260 
    261         Call ringingCall = mCM.getFirstActiveRingingCall();
    262         mRingingCallState = ringingCall.getState();
    263         mRingNumber = getCallNumber(connection, ringingCall);
    264 
    265         if (mCM.getDefaultPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
    266             mNumHeld = getNumHeldCdma();
    267             PhoneGlobals app = PhoneGlobals.getInstance();
    268             if (app.cdmaPhoneCallState != null) {
    269                 CdmaPhoneCallState.PhoneCallState currCdmaThreeWayCallState =
    270                         app.cdmaPhoneCallState.getCurrentCallState();
    271                 CdmaPhoneCallState.PhoneCallState prevCdmaThreeWayCallState =
    272                     app.cdmaPhoneCallState.getPreviousCallState();
    273 
    274                 log("CDMA call state: " + currCdmaThreeWayCallState + " prev state:" +
    275                     prevCdmaThreeWayCallState);
    276 
    277                 if ((mBluetoothHeadset != null) &&
    278                     (mCdmaThreeWayCallState != currCdmaThreeWayCallState)) {
    279                     // In CDMA, the network does not provide any feedback
    280                     // to the phone when the 2nd MO call goes through the
    281                     // stages of DIALING > ALERTING -> ACTIVE we fake the
    282                     // sequence
    283                     log("CDMA 3way call state change. mNumActive: " + mNumActive +
    284                         " mNumHeld: " + mNumHeld + " IsThreeWayCallOrigStateDialing: " +
    285                         app.cdmaPhoneCallState.IsThreeWayCallOrigStateDialing());
    286                     if ((currCdmaThreeWayCallState ==
    287                             CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE)
    288                                 && app.cdmaPhoneCallState.IsThreeWayCallOrigStateDialing()) {
    289                         // Mimic dialing, put the call on hold, alerting
    290                         mBluetoothHeadset.phoneStateChanged(0, mNumHeld,
    291                             convertCallState(Call.State.IDLE, Call.State.DIALING),
    292                             mRingNumber.mNumber, mRingNumber.mType);
    293 
    294                         mBluetoothHeadset.phoneStateChanged(0, mNumHeld,
    295                             convertCallState(Call.State.IDLE, Call.State.ALERTING),
    296                             mRingNumber.mNumber, mRingNumber.mType);
    297 
    298                     }
    299 
    300                     // In CDMA, the network does not provide any feedback to
    301                     // the phone when a user merges a 3way call or swaps
    302                     // between two calls we need to send a CIEV response
    303                     // indicating that a call state got changed which should
    304                     // trigger a CLCC update request from the BT client.
    305                     if (currCdmaThreeWayCallState ==
    306                             CdmaPhoneCallState.PhoneCallState.CONF_CALL &&
    307                             prevCdmaThreeWayCallState ==
    308                               CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
    309                         log("CDMA 3way conf call. mNumActive: " + mNumActive +
    310                             " mNumHeld: " + mNumHeld);
    311                         mBluetoothHeadset.phoneStateChanged(mNumActive, mNumHeld,
    312                             convertCallState(Call.State.IDLE, mForegroundCallState),
    313                             mRingNumber.mNumber, mRingNumber.mType);
    314                     }
    315                 }
    316                 mCdmaThreeWayCallState = currCdmaThreeWayCallState;
    317             }
    318         } else {
    319             mNumHeld = getNumHeldUmts();
    320         }
    321 
    322         boolean callsSwitched = false;
    323         if (mCM.getDefaultPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA &&
    324             mCdmaThreeWayCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
    325             callsSwitched = mCdmaCallsSwapped;
    326         } else {
    327             Call backgroundCall = mCM.getFirstActiveBgCall();
    328             callsSwitched =
    329                 (mNumHeld == 1 && ! (backgroundCall.getEarliestConnectTime() ==
    330                     mBgndEarliestConnectionTime));
    331             mBgndEarliestConnectionTime = backgroundCall.getEarliestConnectTime();
    332         }
    333 
    334         if (mNumActive != oldNumActive || mNumHeld != oldNumHeld ||
    335             mRingingCallState != oldRingingCallState ||
    336             mForegroundCallState != oldForegroundCallState ||
    337             !mRingNumber.equalTo(oldRingNumber) ||
    338             callsSwitched) {
    339             if (mBluetoothHeadset != null) {
    340                 mBluetoothHeadset.phoneStateChanged(mNumActive, mNumHeld,
    341                     convertCallState(mRingingCallState, mForegroundCallState),
    342                     mRingNumber.mNumber, mRingNumber.mType);
    343             }
    344         }
    345     }
    346 
    347     private void handleListCurrentCalls() {
    348         Phone phone = mCM.getDefaultPhone();
    349         int phoneType = phone.getPhoneType();
    350 
    351         // TODO(BT) handle virtual call
    352 
    353         if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
    354             listCurrentCallsCdma();
    355         } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
    356             listCurrentCallsGsm();
    357         } else {
    358             Log.e(TAG, "Unexpected phone type: " + phoneType);
    359         }
    360         // end the result
    361         // when index is 0, other parameter does not matter
    362         mBluetoothHeadset.clccResponse(0, 0, 0, 0, false, "", 0);
    363     }
    364 
    365     private void handleQueryPhoneState() {
    366         if (mBluetoothHeadset != null) {
    367             mBluetoothHeadset.phoneStateChanged(mNumActive, mNumHeld,
    368                 convertCallState(mRingingCallState, mForegroundCallState),
    369                 mRingNumber.mNumber, mRingNumber.mType);
    370         }
    371     }
    372 
    373     private int getNumHeldUmts() {
    374         int countHeld = 0;
    375         List<Call> heldCalls = mCM.getBackgroundCalls();
    376 
    377         for (Call call : heldCalls) {
    378             if (call.getState() == Call.State.HOLDING) {
    379                 countHeld++;
    380             }
    381         }
    382         return countHeld;
    383     }
    384 
    385     private int getNumHeldCdma() {
    386         int numHeld = 0;
    387         PhoneGlobals app = PhoneGlobals.getInstance();
    388         if (app.cdmaPhoneCallState != null) {
    389             CdmaPhoneCallState.PhoneCallState curr3WayCallState =
    390                 app.cdmaPhoneCallState.getCurrentCallState();
    391             CdmaPhoneCallState.PhoneCallState prev3WayCallState =
    392                 app.cdmaPhoneCallState.getPreviousCallState();
    393 
    394             log("CDMA call state: " + curr3WayCallState + " prev state:" +
    395                 prev3WayCallState);
    396             if (curr3WayCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
    397                 if (prev3WayCallState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
    398                     numHeld = 0; //0: no calls held, as now *both* the caller are active
    399                 } else {
    400                     numHeld = 1; //1: held call and active call, as on answering a
    401                     // Call Waiting, one of the caller *is* put on hold
    402                 }
    403             } else if (curr3WayCallState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
    404                 numHeld = 1; //1: held call and active call, as on make a 3 Way Call
    405                 // the first caller *is* put on hold
    406             } else {
    407                 numHeld = 0; //0: no calls held as this is a SINGLE_ACTIVE call
    408             }
    409         }
    410         return numHeld;
    411     }
    412 
    413     private CallNumber getCallNumber(Connection connection, Call call) {
    414         String number = null;
    415         int type = 128;
    416         // find phone number and type
    417         if (connection == null) {
    418             connection = call.getEarliestConnection();
    419             if (connection == null) {
    420                 Log.e(TAG, "Could not get a handle on Connection object for the call");
    421             }
    422         }
    423         if (connection != null) {
    424             number = connection.getAddress();
    425             if (number != null) {
    426                 type = PhoneNumberUtils.toaFromString(number);
    427             }
    428         }
    429         if (number == null) {
    430             number = "";
    431         }
    432         return new CallNumber(number, type);
    433     }
    434 
    435     private class CallNumber
    436     {
    437         private String mNumber = null;
    438         private int mType = 0;
    439 
    440         private CallNumber(String number, int type) {
    441             mNumber = number;
    442             mType = type;
    443         }
    444 
    445         private boolean equalTo(CallNumber callNumber)
    446         {
    447             if (mType != callNumber.mType) return false;
    448 
    449             if (mNumber != null && mNumber.compareTo(callNumber.mNumber) == 0) {
    450                 return true;
    451             }
    452             return false;
    453         }
    454     }
    455 
    456     private BluetoothProfile.ServiceListener mProfileListener =
    457             new BluetoothProfile.ServiceListener() {
    458         public void onServiceConnected(int profile, BluetoothProfile proxy) {
    459             mBluetoothHeadset = (BluetoothHeadset) proxy;
    460         }
    461         public void onServiceDisconnected(int profile) {
    462             mBluetoothHeadset = null;
    463         }
    464     };
    465 
    466     private void listCurrentCallsGsm() {
    467         // Collect all known connections
    468         // clccConnections isindexed by CLCC index
    469         Connection[] clccConnections = new Connection[GSM_MAX_CONNECTIONS];
    470         LinkedList<Connection> newConnections = new LinkedList<Connection>();
    471         LinkedList<Connection> connections = new LinkedList<Connection>();
    472 
    473         Call foregroundCall = mCM.getActiveFgCall();
    474         Call backgroundCall = mCM.getFirstActiveBgCall();
    475         Call ringingCall = mCM.getFirstActiveRingingCall();
    476 
    477         if (ringingCall.getState().isAlive()) {
    478             connections.addAll(ringingCall.getConnections());
    479         }
    480         if (foregroundCall.getState().isAlive()) {
    481             connections.addAll(foregroundCall.getConnections());
    482         }
    483         if (backgroundCall.getState().isAlive()) {
    484             connections.addAll(backgroundCall.getConnections());
    485         }
    486 
    487         // Mark connections that we already known about
    488         boolean clccUsed[] = new boolean[GSM_MAX_CONNECTIONS];
    489         for (int i = 0; i < GSM_MAX_CONNECTIONS; i++) {
    490             clccUsed[i] = mClccUsed[i];
    491             mClccUsed[i] = false;
    492         }
    493         for (Connection c : connections) {
    494             boolean found = false;
    495             long timestamp = c.getCreateTime();
    496             for (int i = 0; i < GSM_MAX_CONNECTIONS; i++) {
    497                 if (clccUsed[i] && timestamp == mClccTimestamps[i]) {
    498                     mClccUsed[i] = true;
    499                     found = true;
    500                     clccConnections[i] = c;
    501                     break;
    502                 }
    503             }
    504             if (!found) {
    505                 newConnections.add(c);
    506             }
    507         }
    508 
    509         // Find a CLCC index for new connections
    510         while (!newConnections.isEmpty()) {
    511             // Find lowest empty index
    512             int i = 0;
    513             while (mClccUsed[i]) i++;
    514             // Find earliest connection
    515             long earliestTimestamp = newConnections.get(0).getCreateTime();
    516             Connection earliestConnection = newConnections.get(0);
    517             for (int j = 0; j < newConnections.size(); j++) {
    518                 long timestamp = newConnections.get(j).getCreateTime();
    519                 if (timestamp < earliestTimestamp) {
    520                     earliestTimestamp = timestamp;
    521                     earliestConnection = newConnections.get(j);
    522                 }
    523             }
    524 
    525             // update
    526             mClccUsed[i] = true;
    527             mClccTimestamps[i] = earliestTimestamp;
    528             clccConnections[i] = earliestConnection;
    529             newConnections.remove(earliestConnection);
    530         }
    531 
    532         // Send CLCC response to Bluetooth headset service
    533         for (int i = 0; i < clccConnections.length; i++) {
    534             if (mClccUsed[i]) {
    535                 sendClccResponseGsm(i, clccConnections[i]);
    536             }
    537         }
    538     }
    539 
    540     /** Convert a Connection object into a single +CLCC result */
    541     private void sendClccResponseGsm(int index, Connection connection) {
    542         int state = convertCallState(connection.getState());
    543         boolean mpty = false;
    544         Call call = connection.getCall();
    545         if (call != null) {
    546             mpty = call.isMultiparty();
    547         }
    548 
    549         int direction = connection.isIncoming() ? 1 : 0;
    550 
    551         String number = connection.getAddress();
    552         int type = -1;
    553         if (number != null) {
    554             type = PhoneNumberUtils.toaFromString(number);
    555         }
    556 
    557         mBluetoothHeadset.clccResponse(index + 1, direction, state, 0, mpty, number, type);
    558     }
    559 
    560     /** Build the +CLCC result for CDMA
    561      *  The complexity arises from the fact that we need to maintain the same
    562      *  CLCC index even as a call moves between states. */
    563     private synchronized void listCurrentCallsCdma() {
    564         // In CDMA at one time a user can have only two live/active connections
    565         Connection[] clccConnections = new Connection[CDMA_MAX_CONNECTIONS];// indexed by CLCC index
    566         Call foregroundCall = mCM.getActiveFgCall();
    567         Call ringingCall = mCM.getFirstActiveRingingCall();
    568 
    569         Call.State ringingCallState = ringingCall.getState();
    570         // If the Ringing Call state is INCOMING, that means this is the very first call
    571         // hence there should not be any Foreground Call
    572         if (ringingCallState == Call.State.INCOMING) {
    573             if (VDBG) log("Filling clccConnections[0] for INCOMING state");
    574             clccConnections[0] = ringingCall.getLatestConnection();
    575         } else if (foregroundCall.getState().isAlive()) {
    576             // Getting Foreground Call connection based on Call state
    577             if (ringingCall.isRinging()) {
    578                 if (VDBG) log("Filling clccConnections[0] & [1] for CALL WAITING state");
    579                 clccConnections[0] = foregroundCall.getEarliestConnection();
    580                 clccConnections[1] = ringingCall.getLatestConnection();
    581             } else {
    582                 if (foregroundCall.getConnections().size() <= 1) {
    583                     // Single call scenario
    584                     if (VDBG) {
    585                         log("Filling clccConnections[0] with ForgroundCall latest connection");
    586                     }
    587                     clccConnections[0] = foregroundCall.getLatestConnection();
    588                 } else {
    589                     // Multiple Call scenario. This would be true for both
    590                     // CONF_CALL and THRWAY_ACTIVE state
    591                     if (VDBG) {
    592                         log("Filling clccConnections[0] & [1] with ForgroundCall connections");
    593                     }
    594                     clccConnections[0] = foregroundCall.getEarliestConnection();
    595                     clccConnections[1] = foregroundCall.getLatestConnection();
    596                 }
    597             }
    598         }
    599 
    600         // Update the mCdmaIsSecondCallActive flag based on the Phone call state
    601         if (PhoneGlobals.getInstance().cdmaPhoneCallState.getCurrentCallState()
    602                 == CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE) {
    603             Message msg = mHandler.obtainMessage(CDMA_SET_SECOND_CALL_STATE, false);
    604             mHandler.sendMessage(msg);
    605         } else if (PhoneGlobals.getInstance().cdmaPhoneCallState.getCurrentCallState()
    606                 == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
    607             Message msg = mHandler.obtainMessage(CDMA_SET_SECOND_CALL_STATE, true);
    608             mHandler.sendMessage(msg);
    609         }
    610 
    611         // send CLCC result
    612         for (int i = 0; (i < clccConnections.length) && (clccConnections[i] != null); i++) {
    613             sendClccResponseCdma(i, clccConnections[i]);
    614         }
    615     }
    616 
    617     /** Send ClCC results for a Connection object for CDMA phone */
    618     private void sendClccResponseCdma(int index, Connection connection) {
    619         int state;
    620         PhoneGlobals app = PhoneGlobals.getInstance();
    621         CdmaPhoneCallState.PhoneCallState currCdmaCallState =
    622                 app.cdmaPhoneCallState.getCurrentCallState();
    623         CdmaPhoneCallState.PhoneCallState prevCdmaCallState =
    624                 app.cdmaPhoneCallState.getPreviousCallState();
    625 
    626         if ((prevCdmaCallState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE)
    627                 && (currCdmaCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL)) {
    628             // If the current state is reached after merging two calls
    629             // we set the state of all the connections as ACTIVE
    630             state = CALL_STATE_ACTIVE;
    631         } else {
    632             Call.State callState = connection.getState();
    633             switch (callState) {
    634             case ACTIVE:
    635                 // For CDMA since both the connections are set as active by FW after accepting
    636                 // a Call waiting or making a 3 way call, we need to set the state specifically
    637                 // to ACTIVE/HOLDING based on the mCdmaIsSecondCallActive flag. This way the
    638                 // CLCC result will allow BT devices to enable the swap or merge options
    639                 if (index == 0) { // For the 1st active connection
    640                     state = mCdmaIsSecondCallActive ? CALL_STATE_HELD : CALL_STATE_ACTIVE;
    641                 } else { // for the 2nd active connection
    642                     state = mCdmaIsSecondCallActive ? CALL_STATE_ACTIVE : CALL_STATE_HELD;
    643                 }
    644                 break;
    645             case HOLDING:
    646                 state = CALL_STATE_HELD;
    647                 break;
    648             case DIALING:
    649                 state = CALL_STATE_DIALING;
    650                 break;
    651             case ALERTING:
    652                 state = CALL_STATE_ALERTING;
    653                 break;
    654             case INCOMING:
    655                 state = CALL_STATE_INCOMING;
    656                 break;
    657             case WAITING:
    658                 state = CALL_STATE_WAITING;
    659                 break;
    660             default:
    661                 Log.e(TAG, "bad call state: " + callState);
    662                 return;
    663             }
    664         }
    665 
    666         boolean mpty = false;
    667         if (currCdmaCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
    668             if (prevCdmaCallState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
    669                 // If the current state is reached after merging two calls
    670                 // we set the multiparty call true.
    671                 mpty = true;
    672             } // else
    673                 // CALL_CONF state is not from merging two calls, but from
    674                 // accepting the second call. In this case first will be on
    675                 // hold in most cases but in some cases its already merged.
    676                 // However, we will follow the common case and the test case
    677                 // as per Bluetooth SIG PTS
    678         }
    679 
    680         int direction = connection.isIncoming() ? 1 : 0;
    681 
    682         String number = connection.getAddress();
    683         int type = -1;
    684         if (number != null) {
    685             type = PhoneNumberUtils.toaFromString(number);
    686         } else {
    687             number = "";
    688         }
    689 
    690         mBluetoothHeadset.clccResponse(index + 1, direction, state, 0, mpty, number, type);
    691     }
    692 
    693     private void handleCdmaSwapSecondCallState() {
    694         if (VDBG) log("cdmaSwapSecondCallState: Toggling mCdmaIsSecondCallActive");
    695         mCdmaIsSecondCallActive = !mCdmaIsSecondCallActive;
    696         mCdmaCallsSwapped = true;
    697     }
    698 
    699     private void handleCdmaSetSecondCallState(boolean state) {
    700         if (VDBG) log("cdmaSetSecondCallState: Setting mCdmaIsSecondCallActive to " + state);
    701         mCdmaIsSecondCallActive = state;
    702 
    703         if (!mCdmaIsSecondCallActive) {
    704             mCdmaCallsSwapped = false;
    705         }
    706     }
    707 
    708     private final IBluetoothHeadsetPhone.Stub mBinder = new IBluetoothHeadsetPhone.Stub() {
    709         public boolean answerCall() {
    710             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    711             return PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
    712         }
    713 
    714         public boolean hangupCall() {
    715             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    716             if (mCM.hasActiveFgCall()) {
    717                 return PhoneUtils.hangupActiveCall(mCM.getActiveFgCall());
    718             } else if (mCM.hasActiveRingingCall()) {
    719                 return PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall());
    720             } else if (mCM.hasActiveBgCall()) {
    721                 return PhoneUtils.hangupHoldingCall(mCM.getFirstActiveBgCall());
    722             }
    723             // TODO(BT) handle virtual voice call
    724             return false;
    725         }
    726 
    727         public boolean sendDtmf(int dtmf) {
    728             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    729             return mCM.sendDtmf((char) dtmf);
    730         }
    731 
    732         public boolean processChld(int chld) {
    733             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    734             Phone phone = mCM.getDefaultPhone();
    735             int phoneType = phone.getPhoneType();
    736             Call ringingCall = mCM.getFirstActiveRingingCall();
    737             Call backgroundCall = mCM.getFirstActiveBgCall();
    738 
    739             if (chld == CHLD_TYPE_RELEASEHELD) {
    740                 if (ringingCall.isRinging()) {
    741                     return PhoneUtils.hangupRingingCall(ringingCall);
    742                 } else {
    743                     return PhoneUtils.hangupHoldingCall(backgroundCall);
    744                 }
    745             } else if (chld == CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD) {
    746                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
    747                     if (ringingCall.isRinging()) {
    748                         // Hangup the active call and then answer call waiting call.
    749                         if (VDBG) log("CHLD:1 Callwaiting Answer call");
    750                         PhoneUtils.hangupRingingAndActive(phone);
    751                     } else {
    752                         // If there is no Call waiting then just hangup
    753                         // the active call. In CDMA this mean that the complete
    754                         // call session would be ended
    755                         if (VDBG) log("CHLD:1 Hangup Call");
    756                         PhoneUtils.hangup(PhoneGlobals.getInstance().mCM);
    757                     }
    758                     return true;
    759                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
    760                     // Hangup active call, answer held call
    761                     return PhoneUtils.answerAndEndActive(PhoneGlobals.getInstance().mCM, ringingCall);
    762                 } else {
    763                     Log.e(TAG, "bad phone type: " + phoneType);
    764                     return false;
    765                 }
    766             } else if (chld == CHLD_TYPE_HOLDACTIVE_ACCEPTHELD) {
    767                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
    768                     // For CDMA, the way we switch to a new incoming call is by
    769                     // calling PhoneUtils.answerCall(). switchAndHoldActive() won't
    770                     // properly update the call state within telephony.
    771                     // If the Phone state is already in CONF_CALL then we simply send
    772                     // a flash cmd by calling switchHoldingAndActive()
    773                     if (ringingCall.isRinging()) {
    774                         if (VDBG) log("CHLD:2 Callwaiting Answer call");
    775                         PhoneUtils.answerCall(ringingCall);
    776                         PhoneUtils.setMute(false);
    777                         // Setting the second callers state flag to TRUE (i.e. active)
    778                         cdmaSetSecondCallState(true);
    779                         return true;
    780                     } else if (PhoneGlobals.getInstance().cdmaPhoneCallState
    781                                .getCurrentCallState()
    782                                == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
    783                         if (VDBG) log("CHLD:2 Swap Calls");
    784                         PhoneUtils.switchHoldingAndActive(backgroundCall);
    785                         // Toggle the second callers active state flag
    786                         cdmaSwapSecondCallState();
    787                         return true;
    788                     }
    789                     Log.e(TAG, "CDMA fail to do hold active and accept held");
    790                     return false;
    791                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
    792                     PhoneUtils.switchHoldingAndActive(backgroundCall);
    793                     return true;
    794                 } else {
    795                     Log.e(TAG, "Unexpected phone type: " + phoneType);
    796                     return false;
    797                 }
    798             } else if (chld == CHLD_TYPE_ADDHELDTOCONF) {
    799                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
    800                     CdmaPhoneCallState.PhoneCallState state =
    801                         PhoneGlobals.getInstance().cdmaPhoneCallState.getCurrentCallState();
    802                     // For CDMA, we need to check if the call is in THRWAY_ACTIVE state
    803                     if (state == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
    804                         if (VDBG) log("CHLD:3 Merge Calls");
    805                         PhoneUtils.mergeCalls();
    806                         return true;
    807                     }   else if (state == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
    808                         // State is CONF_CALL already and we are getting a merge call
    809                         // This can happen when CONF_CALL was entered from a Call Waiting
    810                         // TODO(BT)
    811                         return false;
    812                     }
    813                     Log.e(TAG, "GSG no call to add conference");
    814                     return false;
    815                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
    816                     if (mCM.hasActiveFgCall() && mCM.hasActiveBgCall()) {
    817                         PhoneUtils.mergeCalls();
    818                         return true;
    819                     } else {
    820                         Log.e(TAG, "GSG no call to merge");
    821                         return false;
    822                     }
    823                 } else {
    824                     Log.e(TAG, "Unexpected phone type: " + phoneType);
    825                     return false;
    826                 }
    827             } else {
    828                 Log.e(TAG, "bad CHLD value: " + chld);
    829                 return false;
    830             }
    831         }
    832 
    833         public String getNetworkOperator() {
    834             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    835             return mCM.getDefaultPhone().getServiceState().getOperatorAlphaLong();
    836         }
    837 
    838         public String getSubscriberNumber() {
    839             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    840             return mCM.getDefaultPhone().getLine1Number();
    841         }
    842 
    843         public boolean listCurrentCalls() {
    844             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    845             Message msg = Message.obtain(mHandler, LIST_CURRENT_CALLS);
    846             mHandler.sendMessage(msg);
    847             return true;
    848         }
    849 
    850         public boolean queryPhoneState() {
    851             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    852             Message msg = Message.obtain(mHandler, QUERY_PHONE_STATE);
    853             mHandler.sendMessage(msg);
    854             return true;
    855         }
    856 
    857         public void updateBtHandsfreeAfterRadioTechnologyChange() {
    858             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    859             if (VDBG) Log.d(TAG, "updateBtHandsfreeAfterRadioTechnologyChange...");
    860             updateBtPhoneStateAfterRadioTechnologyChange();
    861         }
    862 
    863         public void cdmaSwapSecondCallState() {
    864             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    865             Message msg = Message.obtain(mHandler, CDMA_SWAP_SECOND_CALL_STATE);
    866             mHandler.sendMessage(msg);
    867         }
    868 
    869         public void cdmaSetSecondCallState(boolean state) {
    870             enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
    871             Message msg = mHandler.obtainMessage(CDMA_SET_SECOND_CALL_STATE, state);
    872             mHandler.sendMessage(msg);
    873         }
    874     };
    875 
    876     // match up with bthf_call_state_t of bt_hf.h
    877     final static int CALL_STATE_ACTIVE = 0;
    878     final static int CALL_STATE_HELD = 1;
    879     final static int CALL_STATE_DIALING = 2;
    880     final static int CALL_STATE_ALERTING = 3;
    881     final static int CALL_STATE_INCOMING = 4;
    882     final static int CALL_STATE_WAITING = 5;
    883     final static int CALL_STATE_IDLE = 6;
    884 
    885     // match up with bthf_chld_type_t of bt_hf.h
    886     final static int CHLD_TYPE_RELEASEHELD = 0;
    887     final static int CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD = 1;
    888     final static int CHLD_TYPE_HOLDACTIVE_ACCEPTHELD = 2;
    889     final static int CHLD_TYPE_ADDHELDTOCONF = 3;
    890 
    891      /* Convert telephony phone call state into hf hal call state */
    892     static int convertCallState(Call.State ringingState, Call.State foregroundState) {
    893         if ((ringingState == Call.State.INCOMING) ||
    894             (ringingState == Call.State.WAITING) )
    895             return CALL_STATE_INCOMING;
    896         else if (foregroundState == Call.State.DIALING)
    897             return CALL_STATE_DIALING;
    898         else if (foregroundState == Call.State.ALERTING)
    899             return CALL_STATE_ALERTING;
    900         else
    901             return CALL_STATE_IDLE;
    902     }
    903 
    904     static int convertCallState(Call.State callState) {
    905         switch (callState) {
    906         case IDLE:
    907         case DISCONNECTED:
    908         case DISCONNECTING:
    909             return CALL_STATE_IDLE;
    910         case ACTIVE:
    911             return CALL_STATE_ACTIVE;
    912         case HOLDING:
    913             return CALL_STATE_HELD;
    914         case DIALING:
    915             return CALL_STATE_DIALING;
    916         case ALERTING:
    917             return CALL_STATE_ALERTING;
    918         case INCOMING:
    919             return CALL_STATE_INCOMING;
    920         case WAITING:
    921             return CALL_STATE_WAITING;
    922         default:
    923             Log.e(TAG, "bad call state: " + callState);
    924             return CALL_STATE_IDLE;
    925         }
    926     }
    927 
    928     private static void log(String msg) {
    929         Log.d(TAG, msg);
    930     }
    931 }
    932