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