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