Home | History | Annotate | Download | only in hfp
      1 /*
      2  * Copyright 2017 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.bluetooth.hfp;
     18 
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.support.annotation.VisibleForTesting;
     22 import android.util.Log;
     23 
     24 import com.android.bluetooth.Utils;
     25 
     26 /**
     27  * Defines native calls that are used by state machine/service to either send or receive
     28  * messages to/from the native stack. This file is registered for the native methods in
     29  * corresponding CPP file.
     30  */
     31 public class HeadsetNativeInterface {
     32     private static final String TAG = "HeadsetNativeInterface";
     33 
     34     private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
     35 
     36     static {
     37         classInitNative();
     38     }
     39 
     40     private static HeadsetNativeInterface sInterface;
     41     private static final Object INSTANCE_LOCK = new Object();
     42 
     43     private HeadsetNativeInterface() {}
     44 
     45     /**
     46      * This class is a singleton because native library should only be loaded once
     47      *
     48      * @return default instance
     49      */
     50     public static HeadsetNativeInterface getInstance() {
     51         synchronized (INSTANCE_LOCK) {
     52             if (sInterface == null) {
     53                 sInterface = new HeadsetNativeInterface();
     54             }
     55         }
     56         return sInterface;
     57     }
     58 
     59     private void sendMessageToService(HeadsetStackEvent event) {
     60         HeadsetService service = HeadsetService.getHeadsetService();
     61         if (service != null) {
     62             service.messageFromNative(event);
     63         } else {
     64             // Service must call cleanup() when quiting and native stack shouldn't send any event
     65             // after cleanup() -> cleanupNative() is called.
     66             Log.wtfStack(TAG, "FATAL: Stack sent event while service is not available: " + event);
     67         }
     68     }
     69 
     70     private BluetoothDevice getDevice(byte[] address) {
     71         return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
     72     }
     73 
     74     void onConnectionStateChanged(int state, byte[] address) {
     75         HeadsetStackEvent event =
     76                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED, state,
     77                         getDevice(address));
     78         sendMessageToService(event);
     79     }
     80 
     81     // Callbacks for native code
     82 
     83     private void onAudioStateChanged(int state, byte[] address) {
     84         HeadsetStackEvent event =
     85                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, state,
     86                         getDevice(address));
     87         sendMessageToService(event);
     88     }
     89 
     90     private void onVrStateChanged(int state, byte[] address) {
     91         HeadsetStackEvent event =
     92                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED, state,
     93                         getDevice(address));
     94         sendMessageToService(event);
     95     }
     96 
     97     private void onAnswerCall(byte[] address) {
     98         HeadsetStackEvent event =
     99                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL, getDevice(address));
    100         sendMessageToService(event);
    101     }
    102 
    103     private void onHangupCall(byte[] address) {
    104         HeadsetStackEvent event =
    105                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL, getDevice(address));
    106         sendMessageToService(event);
    107     }
    108 
    109     private void onVolumeChanged(int type, int volume, byte[] address) {
    110         HeadsetStackEvent event =
    111                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED, type, volume,
    112                         getDevice(address));
    113         sendMessageToService(event);
    114     }
    115 
    116     private void onDialCall(String number, byte[] address) {
    117         HeadsetStackEvent event =
    118                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, number,
    119                         getDevice(address));
    120         sendMessageToService(event);
    121     }
    122 
    123     private void onSendDtmf(int dtmf, byte[] address) {
    124         HeadsetStackEvent event =
    125                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SEND_DTMF, dtmf,
    126                         getDevice(address));
    127         sendMessageToService(event);
    128     }
    129 
    130     private void onNoiceReductionEnable(boolean enable, byte[] address) {
    131         HeadsetStackEvent event =
    132                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_NOICE_REDUCTION, enable ? 1 : 0,
    133                         getDevice(address));
    134         sendMessageToService(event);
    135     }
    136 
    137     private void onWBS(int codec, byte[] address) {
    138         HeadsetStackEvent event =
    139                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_WBS, codec, getDevice(address));
    140         sendMessageToService(event);
    141     }
    142 
    143     private void onAtChld(int chld, byte[] address) {
    144         HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CHLD, chld,
    145                 getDevice(address));
    146         sendMessageToService(event);
    147     }
    148 
    149     private void onAtCnum(byte[] address) {
    150         HeadsetStackEvent event =
    151                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST,
    152                         getDevice(address));
    153         sendMessageToService(event);
    154     }
    155 
    156     private void onAtCind(byte[] address) {
    157         HeadsetStackEvent event =
    158                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CIND, getDevice(address));
    159         sendMessageToService(event);
    160     }
    161 
    162     private void onAtCops(byte[] address) {
    163         HeadsetStackEvent event =
    164                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_COPS, getDevice(address));
    165         sendMessageToService(event);
    166     }
    167 
    168     private void onAtClcc(byte[] address) {
    169         HeadsetStackEvent event =
    170                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CLCC, getDevice(address));
    171         sendMessageToService(event);
    172     }
    173 
    174     private void onUnknownAt(String atString, byte[] address) {
    175         HeadsetStackEvent event =
    176                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT, atString,
    177                         getDevice(address));
    178         sendMessageToService(event);
    179     }
    180 
    181     private void onKeyPressed(byte[] address) {
    182         HeadsetStackEvent event =
    183                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED, getDevice(address));
    184         sendMessageToService(event);
    185     }
    186 
    187     private void onATBind(String atString, byte[] address) {
    188         HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIND, atString,
    189                 getDevice(address));
    190         sendMessageToService(event);
    191     }
    192 
    193     private void onATBiev(int indId, int indValue, byte[] address) {
    194         HeadsetStackEvent event =
    195                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIEV, indId, indValue,
    196                         getDevice(address));
    197         sendMessageToService(event);
    198     }
    199 
    200     private void onAtBia(boolean service, boolean roam, boolean signal, boolean battery,
    201             byte[] address) {
    202         HeadsetAgIndicatorEnableState agIndicatorEnableState =
    203                 new HeadsetAgIndicatorEnableState(service, roam, signal, battery);
    204         HeadsetStackEvent event =
    205                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIA, agIndicatorEnableState,
    206                         getDevice(address));
    207         sendMessageToService(event);
    208     }
    209 
    210     // Native wrappers to help unit testing
    211 
    212     /**
    213      * Initialize native stack
    214      *
    215      * @param maxHfClients maximum number of headset clients that can be connected simultaneously
    216      * @param inbandRingingEnabled whether in-band ringing is enabled on this AG
    217      */
    218     @VisibleForTesting
    219     public void init(int maxHfClients, boolean inbandRingingEnabled) {
    220         initializeNative(maxHfClients, inbandRingingEnabled);
    221     }
    222 
    223     /**
    224      * Closes the interface
    225      */
    226     @VisibleForTesting
    227     public void cleanup() {
    228         cleanupNative();
    229     }
    230 
    231     /**
    232      * ok/error response
    233      *
    234      * @param device target device
    235      * @param responseCode 0 - ERROR, 1 - OK
    236      * @param errorCode error code in case of ERROR
    237      * @return True on success, False on failure
    238      */
    239     @VisibleForTesting
    240     public boolean atResponseCode(BluetoothDevice device, int responseCode, int errorCode) {
    241         return atResponseCodeNative(responseCode, errorCode, Utils.getByteAddress(device));
    242     }
    243 
    244     /**
    245      * Pre-formatted AT response, typically in response to unknown AT cmd
    246      *
    247      * @param device target device
    248      * @param responseString formatted AT response string
    249      * @return True on success, False on failure
    250      */
    251     @VisibleForTesting
    252     public boolean atResponseString(BluetoothDevice device, String responseString) {
    253         return atResponseStringNative(responseString, Utils.getByteAddress(device));
    254     }
    255 
    256     /**
    257      * Connect to headset
    258      *
    259      * @param device target headset
    260      * @return True on success, False on failure
    261      */
    262     @VisibleForTesting
    263     public boolean connectHfp(BluetoothDevice device) {
    264         return connectHfpNative(Utils.getByteAddress(device));
    265     }
    266 
    267     /**
    268      * Disconnect from headset
    269      *
    270      * @param device target headset
    271      * @return True on success, False on failure
    272      */
    273     @VisibleForTesting
    274     public boolean disconnectHfp(BluetoothDevice device) {
    275         return disconnectHfpNative(Utils.getByteAddress(device));
    276     }
    277 
    278     /**
    279      * Connect HFP audio (SCO) to headset
    280      *
    281      * @param device target headset
    282      * @return True on success, False on failure
    283      */
    284     @VisibleForTesting
    285     public boolean connectAudio(BluetoothDevice device) {
    286         return connectAudioNative(Utils.getByteAddress(device));
    287     }
    288 
    289     /**
    290      * Disconnect HFP audio (SCO) from to headset
    291      *
    292      * @param device target headset
    293      * @return True on success, False on failure
    294      */
    295     @VisibleForTesting
    296     public boolean disconnectAudio(BluetoothDevice device) {
    297         return disconnectAudioNative(Utils.getByteAddress(device));
    298     }
    299 
    300     /**
    301      * Start voice recognition
    302      *
    303      * @param device target headset
    304      * @return True on success, False on failure
    305      */
    306     @VisibleForTesting
    307     public boolean startVoiceRecognition(BluetoothDevice device) {
    308         return startVoiceRecognitionNative(Utils.getByteAddress(device));
    309     }
    310 
    311 
    312     /**
    313      * Stop voice recognition
    314      *
    315      * @param device target headset
    316      * @return True on success, False on failure
    317      */
    318     @VisibleForTesting
    319     public boolean stopVoiceRecognition(BluetoothDevice device) {
    320         return stopVoiceRecognitionNative(Utils.getByteAddress(device));
    321     }
    322 
    323     /**
    324      * Set HFP audio (SCO) volume
    325      *
    326      * @param device target headset
    327      * @param volumeType type of volume
    328      * @param volume value value
    329      * @return True on success, False on failure
    330      */
    331     @VisibleForTesting
    332     public boolean setVolume(BluetoothDevice device, int volumeType, int volume) {
    333         return setVolumeNative(volumeType, volume, Utils.getByteAddress(device));
    334     }
    335 
    336     /**
    337      * Response for CIND command
    338      *
    339      * @param device target device
    340      * @param service service availability, 0 - no service, 1 - presence of service
    341      * @param numActive number of active calls
    342      * @param numHeld number of held calls
    343      * @param callState overall call state [0-6]
    344      * @param signal signal quality [0-5]
    345      * @param roam roaming indicator, 0 - not roaming, 1 - roaming
    346      * @param batteryCharge battery charge level [0-5]
    347      * @return True on success, False on failure
    348      */
    349     @VisibleForTesting
    350     public boolean cindResponse(BluetoothDevice device, int service, int numActive, int numHeld,
    351             int callState, int signal, int roam, int batteryCharge) {
    352         return cindResponseNative(service, numActive, numHeld, callState, signal, roam,
    353                 batteryCharge, Utils.getByteAddress(device));
    354     }
    355 
    356     /**
    357      * Combined device status change notification
    358      *
    359      * @param device target device
    360      * @param deviceState device status object
    361      * @return True on success, False on failure
    362      */
    363     @VisibleForTesting
    364     public boolean notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState) {
    365         return notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam,
    366                 deviceState.mSignal, deviceState.mBatteryCharge, Utils.getByteAddress(device));
    367     }
    368 
    369     /**
    370      * Response for CLCC command. Can be iteratively called for each call index. Call index of 0
    371      * will be treated as NULL termination (Completes response)
    372      *
    373      * @param device target device
    374      * @param index index of the call given by the sequence of setting up or receiving the calls
    375      * as seen by the served subscriber. Calls hold their number until they are released. New
    376      * calls take the lowest available number.
    377      * @param dir direction of the call, 0 (outgoing), 1 (incoming)
    378      * @param status 0 = Active, 1 = Held, 2 = Dialing (outgoing calls only), 3 = Alerting
    379      * (outgoing calls only), 4 = Incoming (incoming calls only), 5 = Waiting (incoming calls
    380      * only), 6 = Call held by Response and Hold
    381      * @param mode 0 (Voice), 1 (Data), 2 (FAX)
    382      * @param mpty 0 - this call is NOT a member of a multi-party (conference) call, 1 - this
    383      * call IS a member of a multi-party (conference) call
    384      * @param number optional
    385      * @param type optional
    386      * @return True on success, False on failure
    387      */
    388     @VisibleForTesting
    389     public boolean clccResponse(BluetoothDevice device, int index, int dir, int status, int mode,
    390             boolean mpty, String number, int type) {
    391         return clccResponseNative(index, dir, status, mode, mpty, number, type,
    392                 Utils.getByteAddress(device));
    393     }
    394 
    395     /**
    396      * Response for COPS command
    397      *
    398      * @param device target device
    399      * @param operatorName operator name
    400      * @return True on success, False on failure
    401      */
    402     @VisibleForTesting
    403     public boolean copsResponse(BluetoothDevice device, String operatorName) {
    404         return copsResponseNative(operatorName, Utils.getByteAddress(device));
    405     }
    406 
    407     /**
    408      *  Notify of a call state change
    409      *  Each update notifies
    410      *    1. Number of active/held/ringing calls
    411      *    2. call_state: This denotes the state change that triggered this msg
    412      *                   This will take one of the values from BtHfCallState
    413      *    3. number & type: valid only for incoming & waiting call
    414      *
    415      * @param device target device for this update
    416      * @param callState callstate structure
    417      * @return True on success, False on failure
    418      */
    419     @VisibleForTesting
    420     public boolean phoneStateChange(BluetoothDevice device, HeadsetCallState callState) {
    421         return phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
    422                 callState.mCallState, callState.mNumber, callState.mType,
    423                 Utils.getByteAddress(device));
    424     }
    425 
    426     /**
    427      * Set whether we will initiate SCO or not
    428      *
    429      * @param value True to enable, False to disable
    430      * @return True on success, False on failure
    431      */
    432     @VisibleForTesting
    433     public boolean setScoAllowed(boolean value) {
    434         return setScoAllowedNative(value);
    435     }
    436 
    437     /**
    438      * Enable or disable in-band ringing for the current service level connection through sending
    439      * +BSIR AT command
    440      *
    441      * @param value True to enable, False to disable
    442      * @return True on success, False on failure
    443      */
    444     @VisibleForTesting
    445     public boolean sendBsir(BluetoothDevice device, boolean value) {
    446         return sendBsirNative(value, Utils.getByteAddress(device));
    447     }
    448 
    449     /**
    450      * Set the current active headset device for SCO audio
    451      * @param device current active SCO device
    452      * @return true on success
    453      */
    454     @VisibleForTesting
    455     public boolean setActiveDevice(BluetoothDevice device) {
    456         return setActiveDeviceNative(Utils.getByteAddress(device));
    457     }
    458 
    459     /* Native methods */
    460     private static native void classInitNative();
    461 
    462     private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address);
    463 
    464     private native boolean atResponseStringNative(String responseString, byte[] address);
    465 
    466     private native void initializeNative(int maxHfClients, boolean inbandRingingEnabled);
    467 
    468     private native void cleanupNative();
    469 
    470     private native boolean connectHfpNative(byte[] address);
    471 
    472     private native boolean disconnectHfpNative(byte[] address);
    473 
    474     private native boolean connectAudioNative(byte[] address);
    475 
    476     private native boolean disconnectAudioNative(byte[] address);
    477 
    478     private native boolean startVoiceRecognitionNative(byte[] address);
    479 
    480     private native boolean stopVoiceRecognitionNative(byte[] address);
    481 
    482     private native boolean setVolumeNative(int volumeType, int volume, byte[] address);
    483 
    484     private native boolean cindResponseNative(int service, int numActive, int numHeld,
    485             int callState, int signal, int roam, int batteryCharge, byte[] address);
    486 
    487     private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
    488             int batteryCharge, byte[] address);
    489 
    490     private native boolean clccResponseNative(int index, int dir, int status, int mode,
    491             boolean mpty, String number, int type, byte[] address);
    492 
    493     private native boolean copsResponseNative(String operatorName, byte[] address);
    494 
    495     private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
    496             String number, int type, byte[] address);
    497 
    498     private native boolean setScoAllowedNative(boolean value);
    499 
    500     private native boolean sendBsirNative(boolean value, byte[] address);
    501 
    502     private native boolean setActiveDeviceNative(byte[] address);
    503 }
    504