Home | History | Annotate | Download | only in hfp
      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.bluetooth.hfp;
     18 
     19 import static android.Manifest.permission.MODIFY_PHONE_STATE;
     20 
     21 import android.annotation.Nullable;
     22 import android.bluetooth.BluetoothDevice;
     23 import android.bluetooth.BluetoothHeadset;
     24 import android.bluetooth.BluetoothProfile;
     25 import android.bluetooth.BluetoothUuid;
     26 import android.bluetooth.IBluetoothHeadset;
     27 import android.content.BroadcastReceiver;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.media.AudioManager;
     32 import android.net.Uri;
     33 import android.os.BatteryManager;
     34 import android.os.HandlerThread;
     35 import android.os.IDeviceIdleController;
     36 import android.os.Looper;
     37 import android.os.ParcelUuid;
     38 import android.os.RemoteException;
     39 import android.os.ServiceManager;
     40 import android.os.SystemProperties;
     41 import android.os.UserHandle;
     42 import android.provider.Settings;
     43 import android.telecom.PhoneAccount;
     44 import android.util.Log;
     45 
     46 import com.android.bluetooth.BluetoothMetricsProto;
     47 import com.android.bluetooth.Utils;
     48 import com.android.bluetooth.btservice.AdapterService;
     49 import com.android.bluetooth.btservice.MetricsLogger;
     50 import com.android.bluetooth.btservice.ProfileService;
     51 import com.android.internal.annotations.VisibleForTesting;
     52 
     53 import java.util.ArrayList;
     54 import java.util.Arrays;
     55 import java.util.Comparator;
     56 import java.util.HashMap;
     57 import java.util.List;
     58 import java.util.Objects;
     59 
     60 /**
     61  * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application.
     62  *
     63  * Three modes for SCO audio:
     64  * Mode 1: Telecom call through {@link #phoneStateChanged(int, int, int, String, int, boolean)}
     65  * Mode 2: Virtual call through {@link #startScoUsingVirtualVoiceCall()}
     66  * Mode 3: Voice recognition through {@link #startVoiceRecognition(BluetoothDevice)}
     67  *
     68  * When one mode is active, other mode cannot be started. API user has to terminate existing modes
     69  * using the correct API or just {@link #disconnectAudio()} if user is a system service, before
     70  * starting a new mode.
     71  *
     72  * {@link #connectAudio()} will start SCO audio at one of the above modes, but won't change mode
     73  * {@link #disconnectAudio()} can happen in any mode to disconnect SCO
     74  *
     75  * When audio is disconnected, only Mode 1 Telecom call will be persisted, both Mode 2 virtual call
     76  * and Mode 3 voice call will be terminated upon SCO termination and client has to restart the mode.
     77  *
     78  * NOTE: SCO termination can either be initiated on the AG side or the HF side
     79  * TODO(b/79660380): As a workaround, voice recognition will be terminated if virtual call or
     80  * Telecom call is initiated while voice recognition is ongoing, in case calling app did not call
     81  * {@link #stopVoiceRecognition(BluetoothDevice)}
     82  *
     83  * AG - Audio Gateway, device running this {@link HeadsetService}, e.g. Android Phone
     84  * HF - Handsfree device, device running headset client, e.g. Wireless headphones or car kits
     85  */
     86 public class HeadsetService extends ProfileService {
     87     private static final String TAG = "HeadsetService";
     88     private static final boolean DBG = false;
     89     private static final String DISABLE_INBAND_RINGING_PROPERTY =
     90             "persist.bluetooth.disableinbandringing";
     91     private static final ParcelUuid[] HEADSET_UUIDS = {BluetoothUuid.HSP, BluetoothUuid.Handsfree};
     92     private static final int[] CONNECTING_CONNECTED_STATES =
     93             {BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED};
     94     private static final int DIALING_OUT_TIMEOUT_MS = 10000;
     95 
     96     private int mMaxHeadsetConnections = 1;
     97     private BluetoothDevice mActiveDevice;
     98     private AdapterService mAdapterService;
     99     private HandlerThread mStateMachinesThread;
    100     // This is also used as a lock for shared data in HeadsetService
    101     private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>();
    102     private HeadsetNativeInterface mNativeInterface;
    103     private HeadsetSystemInterface mSystemInterface;
    104     private boolean mAudioRouteAllowed = true;
    105     // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions
    106     private boolean mForceScoAudio;
    107     private boolean mInbandRingingRuntimeDisable;
    108     private boolean mVirtualCallStarted;
    109     // Non null value indicates a pending dialing out event is going on
    110     private DialingOutTimeoutEvent mDialingOutTimeoutEvent;
    111     private boolean mVoiceRecognitionStarted;
    112     // Non null value indicates a pending voice recognition request from headset is going on
    113     private VoiceRecognitionTimeoutEvent mVoiceRecognitionTimeoutEvent;
    114     // Timeout when voice recognition is started by remote device
    115     @VisibleForTesting static int sStartVrTimeoutMs = 5000;
    116     private boolean mStarted;
    117     private boolean mCreated;
    118     private static HeadsetService sHeadsetService;
    119 
    120     @Override
    121     public IProfileServiceBinder initBinder() {
    122         return new BluetoothHeadsetBinder(this);
    123     }
    124 
    125     @Override
    126     protected void create() {
    127         Log.i(TAG, "create()");
    128         if (mCreated) {
    129             throw new IllegalStateException("create() called twice");
    130         }
    131         mCreated = true;
    132     }
    133 
    134     @Override
    135     protected boolean start() {
    136         Log.i(TAG, "start()");
    137         if (mStarted) {
    138             throw new IllegalStateException("start() called twice");
    139         }
    140         // Step 1: Get adapter service, should never be null
    141         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
    142                 "AdapterService cannot be null when HeadsetService starts");
    143         // Step 2: Start handler thread for state machines
    144         mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines");
    145         mStateMachinesThread.start();
    146         // Step 3: Initialize system interface
    147         mSystemInterface = HeadsetObjectsFactory.getInstance().makeSystemInterface(this);
    148         mSystemInterface.init();
    149         // Step 4: Initialize native interface
    150         mMaxHeadsetConnections = mAdapterService.getMaxConnectedAudioDevices();
    151         mNativeInterface = HeadsetObjectsFactory.getInstance().getNativeInterface();
    152         // Add 1 to allow a pending device to be connecting or disconnecting
    153         mNativeInterface.init(mMaxHeadsetConnections + 1, isInbandRingingEnabled());
    154         // Step 5: Check if state machine table is empty, crash if not
    155         if (mStateMachines.size() > 0) {
    156             throw new IllegalStateException(
    157                     "start(): mStateMachines is not empty, " + mStateMachines.size()
    158                             + " is already created. Was stop() called properly?");
    159         }
    160         // Step 6: Setup broadcast receivers
    161         IntentFilter filter = new IntentFilter();
    162         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
    163         filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
    164         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
    165         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
    166         registerReceiver(mHeadsetReceiver, filter);
    167         // Step 7: Mark service as started
    168         setHeadsetService(this);
    169         mStarted = true;
    170         return true;
    171     }
    172 
    173     @Override
    174     protected boolean stop() {
    175         Log.i(TAG, "stop()");
    176         if (!mStarted) {
    177             Log.w(TAG, "stop() called before start()");
    178             // Still return true because it is considered "stopped" and doesn't have any functional
    179             // impact on the user
    180             return true;
    181         }
    182         // Step 7: Mark service as stopped
    183         mStarted = false;
    184         setHeadsetService(null);
    185         // Step 6: Tear down broadcast receivers
    186         unregisterReceiver(mHeadsetReceiver);
    187         synchronized (mStateMachines) {
    188             // Reset active device to null
    189             mActiveDevice = null;
    190             mInbandRingingRuntimeDisable = false;
    191             mForceScoAudio = false;
    192             mAudioRouteAllowed = true;
    193             mMaxHeadsetConnections = 1;
    194             mVoiceRecognitionStarted = false;
    195             mVirtualCallStarted = false;
    196             if (mDialingOutTimeoutEvent != null) {
    197                 mStateMachinesThread.getThreadHandler().removeCallbacks(mDialingOutTimeoutEvent);
    198                 mDialingOutTimeoutEvent = null;
    199             }
    200             if (mVoiceRecognitionTimeoutEvent != null) {
    201                 mStateMachinesThread.getThreadHandler()
    202                         .removeCallbacks(mVoiceRecognitionTimeoutEvent);
    203                 mVoiceRecognitionTimeoutEvent = null;
    204                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
    205                     mSystemInterface.getVoiceRecognitionWakeLock().release();
    206                 }
    207             }
    208             // Step 5: Destroy state machines
    209             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
    210                 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine);
    211             }
    212             mStateMachines.clear();
    213         }
    214         // Step 4: Destroy native interface
    215         mNativeInterface.cleanup();
    216         // Step 3: Destroy system interface
    217         mSystemInterface.stop();
    218         // Step 2: Stop handler thread
    219         mStateMachinesThread.quitSafely();
    220         mStateMachinesThread = null;
    221         // Step 1: Clear
    222         mAdapterService = null;
    223         return true;
    224     }
    225 
    226     @Override
    227     protected void cleanup() {
    228         Log.i(TAG, "cleanup");
    229         if (!mCreated) {
    230             Log.w(TAG, "cleanup() called before create()");
    231         }
    232         mCreated = false;
    233     }
    234 
    235     /**
    236      * Checks if this service object is able to accept binder calls
    237      *
    238      * @return True if the object can accept binder calls, False otherwise
    239      */
    240     public boolean isAlive() {
    241         return isAvailable() && mCreated && mStarted;
    242     }
    243 
    244     /**
    245      * Get the {@link Looper} for the state machine thread. This is used in testing and helper
    246      * objects
    247      *
    248      * @return {@link Looper} for the state machine thread
    249      */
    250     @VisibleForTesting
    251     public Looper getStateMachinesThreadLooper() {
    252         return mStateMachinesThread.getLooper();
    253     }
    254 
    255     interface StateMachineTask {
    256         void execute(HeadsetStateMachine stateMachine);
    257     }
    258 
    259     private boolean doForStateMachine(BluetoothDevice device, StateMachineTask task) {
    260         synchronized (mStateMachines) {
    261             HeadsetStateMachine stateMachine = mStateMachines.get(device);
    262             if (stateMachine == null) {
    263                 return false;
    264             }
    265             task.execute(stateMachine);
    266         }
    267         return true;
    268     }
    269 
    270     private void doForEachConnectedStateMachine(StateMachineTask task) {
    271         synchronized (mStateMachines) {
    272             for (BluetoothDevice device : getConnectedDevices()) {
    273                 task.execute(mStateMachines.get(device));
    274             }
    275         }
    276     }
    277 
    278     void onDeviceStateChanged(HeadsetDeviceState deviceState) {
    279         doForEachConnectedStateMachine(
    280                 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED,
    281                         deviceState));
    282     }
    283 
    284     /**
    285      * Handle messages from native (JNI) to Java. This needs to be synchronized to avoid posting
    286      * messages to state machine before start() is done
    287      *
    288      * @param stackEvent event from native stack
    289      */
    290     void messageFromNative(HeadsetStackEvent stackEvent) {
    291         Objects.requireNonNull(stackEvent.device,
    292                 "Device should never be null, event: " + stackEvent);
    293         synchronized (mStateMachines) {
    294             HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device);
    295             if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
    296                 switch (stackEvent.valueInt) {
    297                     case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
    298                     case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: {
    299                         // Create new state machine if none is found
    300                         if (stateMachine == null) {
    301                             stateMachine = HeadsetObjectsFactory.getInstance()
    302                                     .makeStateMachine(stackEvent.device,
    303                                             mStateMachinesThread.getLooper(), this, mAdapterService,
    304                                             mNativeInterface, mSystemInterface);
    305                             mStateMachines.put(stackEvent.device, stateMachine);
    306                         }
    307                         break;
    308                     }
    309                 }
    310             }
    311             if (stateMachine == null) {
    312                 throw new IllegalStateException(
    313                         "State machine not found for stack event: " + stackEvent);
    314             }
    315             stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent);
    316         }
    317     }
    318 
    319     private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() {
    320         @Override
    321         public void onReceive(Context context, Intent intent) {
    322             String action = intent.getAction();
    323             if (action == null) {
    324                 Log.w(TAG, "mHeadsetReceiver, action is null");
    325                 return;
    326             }
    327             switch (action) {
    328                 case Intent.ACTION_BATTERY_CHANGED: {
    329                     int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
    330                     int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
    331                     if (batteryLevel < 0 || scale <= 0) {
    332                         Log.e(TAG, "Bad Battery Changed intent: batteryLevel=" + batteryLevel
    333                                 + ", scale=" + scale);
    334                         return;
    335                     }
    336                     batteryLevel = batteryLevel * 5 / scale;
    337                     mSystemInterface.getHeadsetPhoneState().setCindBatteryCharge(batteryLevel);
    338                     break;
    339                 }
    340                 case AudioManager.VOLUME_CHANGED_ACTION: {
    341                     int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
    342                     if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
    343                         doForEachConnectedStateMachine(stateMachine -> stateMachine.sendMessage(
    344                                 HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, intent));
    345                     }
    346                     break;
    347                 }
    348                 case BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY: {
    349                     int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    350                             BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    351                     BluetoothDevice device =
    352                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    353                     logD("Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, device=" + device
    354                             + ", type=" + requestType);
    355                     if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
    356                         synchronized (mStateMachines) {
    357                             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
    358                             if (stateMachine == null) {
    359                                 Log.wtfStack(TAG, "Cannot find state machine for " + device);
    360                                 return;
    361                             }
    362                             stateMachine.sendMessage(
    363                                     HeadsetStateMachine.INTENT_CONNECTION_ACCESS_REPLY, intent);
    364                         }
    365                     }
    366                     break;
    367                 }
    368                 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
    369                     int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
    370                             BluetoothDevice.ERROR);
    371                     BluetoothDevice device = Objects.requireNonNull(
    372                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE),
    373                             "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
    374                     logD("Bond state changed for device: " + device + " state: " + state);
    375                     if (state != BluetoothDevice.BOND_NONE) {
    376                         break;
    377                     }
    378                     synchronized (mStateMachines) {
    379                         HeadsetStateMachine stateMachine = mStateMachines.get(device);
    380                         if (stateMachine == null) {
    381                             break;
    382                         }
    383                         if (stateMachine.getConnectionState()
    384                                 != BluetoothProfile.STATE_DISCONNECTED) {
    385                             break;
    386                         }
    387                         removeStateMachine(device);
    388                     }
    389                     break;
    390                 }
    391                 default:
    392                     Log.w(TAG, "Unknown action " + action);
    393             }
    394         }
    395     };
    396 
    397     /**
    398      * Handlers for incoming service calls
    399      */
    400     private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub
    401             implements IProfileServiceBinder {
    402         private volatile HeadsetService mService;
    403 
    404         BluetoothHeadsetBinder(HeadsetService svc) {
    405             mService = svc;
    406         }
    407 
    408         @Override
    409         public void cleanup() {
    410             mService = null;
    411         }
    412 
    413         private HeadsetService getService() {
    414             final HeadsetService service = mService;
    415             if (!Utils.checkCallerAllowManagedProfiles(service)) {
    416                 Log.w(TAG, "Headset call not allowed for non-active user");
    417                 return null;
    418             }
    419             if (service == null) {
    420                 Log.w(TAG, "Service is null");
    421                 return null;
    422             }
    423             if (!service.isAlive()) {
    424                 Log.w(TAG, "Service is not alive");
    425                 return null;
    426             }
    427             return service;
    428         }
    429 
    430         @Override
    431         public boolean connect(BluetoothDevice device) {
    432             HeadsetService service = getService();
    433             if (service == null) {
    434                 return false;
    435             }
    436             return service.connect(device);
    437         }
    438 
    439         @Override
    440         public boolean disconnect(BluetoothDevice device) {
    441             HeadsetService service = getService();
    442             if (service == null) {
    443                 return false;
    444             }
    445             return service.disconnect(device);
    446         }
    447 
    448         @Override
    449         public List<BluetoothDevice> getConnectedDevices() {
    450             HeadsetService service = getService();
    451             if (service == null) {
    452                 return new ArrayList<BluetoothDevice>(0);
    453             }
    454             return service.getConnectedDevices();
    455         }
    456 
    457         @Override
    458         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    459             HeadsetService service = getService();
    460             if (service == null) {
    461                 return new ArrayList<BluetoothDevice>(0);
    462             }
    463             return service.getDevicesMatchingConnectionStates(states);
    464         }
    465 
    466         @Override
    467         public int getConnectionState(BluetoothDevice device) {
    468             HeadsetService service = getService();
    469             if (service == null) {
    470                 return BluetoothProfile.STATE_DISCONNECTED;
    471             }
    472             return service.getConnectionState(device);
    473         }
    474 
    475         @Override
    476         public boolean setPriority(BluetoothDevice device, int priority) {
    477             HeadsetService service = getService();
    478             if (service == null) {
    479                 return false;
    480             }
    481             return service.setPriority(device, priority);
    482         }
    483 
    484         @Override
    485         public int getPriority(BluetoothDevice device) {
    486             HeadsetService service = getService();
    487             if (service == null) {
    488                 return BluetoothProfile.PRIORITY_UNDEFINED;
    489             }
    490             return service.getPriority(device);
    491         }
    492 
    493         @Override
    494         public boolean startVoiceRecognition(BluetoothDevice device) {
    495             HeadsetService service = getService();
    496             if (service == null) {
    497                 return false;
    498             }
    499             return service.startVoiceRecognition(device);
    500         }
    501 
    502         @Override
    503         public boolean stopVoiceRecognition(BluetoothDevice device) {
    504             HeadsetService service = getService();
    505             if (service == null) {
    506                 return false;
    507             }
    508             return service.stopVoiceRecognition(device);
    509         }
    510 
    511         @Override
    512         public boolean isAudioOn() {
    513             HeadsetService service = getService();
    514             if (service == null) {
    515                 return false;
    516             }
    517             return service.isAudioOn();
    518         }
    519 
    520         @Override
    521         public boolean isAudioConnected(BluetoothDevice device) {
    522             HeadsetService service = getService();
    523             if (service == null) {
    524                 return false;
    525             }
    526             return service.isAudioConnected(device);
    527         }
    528 
    529         @Override
    530         public int getAudioState(BluetoothDevice device) {
    531             HeadsetService service = getService();
    532             if (service == null) {
    533                 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
    534             }
    535             return service.getAudioState(device);
    536         }
    537 
    538         @Override
    539         public boolean connectAudio() {
    540             HeadsetService service = getService();
    541             if (service == null) {
    542                 return false;
    543             }
    544             return service.connectAudio();
    545         }
    546 
    547         @Override
    548         public boolean disconnectAudio() {
    549             HeadsetService service = getService();
    550             if (service == null) {
    551                 return false;
    552             }
    553             return service.disconnectAudio();
    554         }
    555 
    556         @Override
    557         public void setAudioRouteAllowed(boolean allowed) {
    558             HeadsetService service = getService();
    559             if (service == null) {
    560                 return;
    561             }
    562             service.setAudioRouteAllowed(allowed);
    563         }
    564 
    565         @Override
    566         public boolean getAudioRouteAllowed() {
    567             HeadsetService service = getService();
    568             if (service != null) {
    569                 return service.getAudioRouteAllowed();
    570             }
    571             return false;
    572         }
    573 
    574         @Override
    575         public void setForceScoAudio(boolean forced) {
    576             HeadsetService service = getService();
    577             if (service == null) {
    578                 return;
    579             }
    580             service.setForceScoAudio(forced);
    581         }
    582 
    583         @Override
    584         public boolean startScoUsingVirtualVoiceCall() {
    585             HeadsetService service = getService();
    586             if (service == null) {
    587                 return false;
    588             }
    589             return service.startScoUsingVirtualVoiceCall();
    590         }
    591 
    592         @Override
    593         public boolean stopScoUsingVirtualVoiceCall() {
    594             HeadsetService service = getService();
    595             if (service == null) {
    596                 return false;
    597             }
    598             return service.stopScoUsingVirtualVoiceCall();
    599         }
    600 
    601         @Override
    602         public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
    603                 int type) {
    604             HeadsetService service = getService();
    605             if (service == null) {
    606                 return;
    607             }
    608             service.phoneStateChanged(numActive, numHeld, callState, number, type, false);
    609         }
    610 
    611         @Override
    612         public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
    613                 String number, int type) {
    614             HeadsetService service = getService();
    615             if (service == null) {
    616                 return;
    617             }
    618             service.clccResponse(index, direction, status, mode, mpty, number, type);
    619         }
    620 
    621         @Override
    622         public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
    623                 String arg) {
    624             HeadsetService service = getService();
    625             if (service == null) {
    626                 return false;
    627             }
    628             return service.sendVendorSpecificResultCode(device, command, arg);
    629         }
    630 
    631         @Override
    632         public boolean setActiveDevice(BluetoothDevice device) {
    633             HeadsetService service = getService();
    634             if (service == null) {
    635                 return false;
    636             }
    637             return service.setActiveDevice(device);
    638         }
    639 
    640         @Override
    641         public BluetoothDevice getActiveDevice() {
    642             HeadsetService service = getService();
    643             if (service == null) {
    644                 return null;
    645             }
    646             return service.getActiveDevice();
    647         }
    648 
    649         @Override
    650         public boolean isInbandRingingEnabled() {
    651             HeadsetService service = getService();
    652             if (service == null) {
    653                 return false;
    654             }
    655             return service.isInbandRingingEnabled();
    656         }
    657     }
    658 
    659     // API methods
    660     public static synchronized HeadsetService getHeadsetService() {
    661         if (sHeadsetService == null) {
    662             Log.w(TAG, "getHeadsetService(): service is NULL");
    663             return null;
    664         }
    665         if (!sHeadsetService.isAvailable()) {
    666             Log.w(TAG, "getHeadsetService(): service is not available");
    667             return null;
    668         }
    669         logD("getHeadsetService(): returning " + sHeadsetService);
    670         return sHeadsetService;
    671     }
    672 
    673     private static synchronized void setHeadsetService(HeadsetService instance) {
    674         logD("setHeadsetService(): set to: " + instance);
    675         sHeadsetService = instance;
    676     }
    677 
    678     public boolean connect(BluetoothDevice device) {
    679         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    680         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
    681             Log.w(TAG, "connect: PRIORITY_OFF, device=" + device + ", " + Utils.getUidPidString());
    682             return false;
    683         }
    684         ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
    685         if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
    686             Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, "
    687                     + Utils.getUidPidString());
    688             return false;
    689         }
    690         synchronized (mStateMachines) {
    691             Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString());
    692             HeadsetStateMachine stateMachine = mStateMachines.get(device);
    693             if (stateMachine == null) {
    694                 stateMachine = HeadsetObjectsFactory.getInstance()
    695                         .makeStateMachine(device, mStateMachinesThread.getLooper(), this,
    696                                 mAdapterService, mNativeInterface, mSystemInterface);
    697                 mStateMachines.put(device, stateMachine);
    698             }
    699             int connectionState = stateMachine.getConnectionState();
    700             if (connectionState == BluetoothProfile.STATE_CONNECTED
    701                     || connectionState == BluetoothProfile.STATE_CONNECTING) {
    702                 Log.w(TAG, "connect: device " + device
    703                         + " is already connected/connecting, connectionState=" + connectionState);
    704                 return false;
    705             }
    706             List<BluetoothDevice> connectingConnectedDevices =
    707                     getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
    708             boolean disconnectExisting = false;
    709             if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
    710                 // When there is maximum one device, we automatically disconnect the current one
    711                 if (mMaxHeadsetConnections == 1) {
    712                     disconnectExisting = true;
    713                 } else {
    714                     Log.w(TAG, "Max connection has reached, rejecting connection to " + device);
    715                     return false;
    716                 }
    717             }
    718             if (disconnectExisting) {
    719                 for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) {
    720                     disconnect(connectingConnectedDevice);
    721                 }
    722                 setActiveDevice(null);
    723             }
    724             stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
    725         }
    726         return true;
    727     }
    728 
    729     boolean disconnect(BluetoothDevice device) {
    730         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    731         Log.i(TAG, "disconnect: device=" + device + ", " + Utils.getUidPidString());
    732         synchronized (mStateMachines) {
    733             HeadsetStateMachine stateMachine = mStateMachines.get(device);
    734             if (stateMachine == null) {
    735                 Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting");
    736                 return false;
    737             }
    738             int connectionState = stateMachine.getConnectionState();
    739             if (connectionState != BluetoothProfile.STATE_CONNECTED
    740                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
    741                 Log.w(TAG, "disconnect: device " + device
    742                         + " not connected/connecting, connectionState=" + connectionState);
    743                 return false;
    744             }
    745             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
    746         }
    747         return true;
    748     }
    749 
    750     public List<BluetoothDevice> getConnectedDevices() {
    751         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    752         ArrayList<BluetoothDevice> devices = new ArrayList<>();
    753         synchronized (mStateMachines) {
    754             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
    755                 if (stateMachine.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
    756                     devices.add(stateMachine.getDevice());
    757                 }
    758             }
    759         }
    760         return devices;
    761     }
    762 
    763     /**
    764      * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])}
    765      *
    766      * @param states an array of states from {@link BluetoothProfile}
    767      * @return a list of devices matching the array of connection states
    768      */
    769     @VisibleForTesting
    770     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    771         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    772         ArrayList<BluetoothDevice> devices = new ArrayList<>();
    773         if (states == null) {
    774             return devices;
    775         }
    776         final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
    777         if (bondedDevices == null) {
    778             return devices;
    779         }
    780         synchronized (mStateMachines) {
    781             for (BluetoothDevice device : bondedDevices) {
    782                 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
    783                 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
    784                     continue;
    785                 }
    786                 int connectionState = getConnectionState(device);
    787                 for (int state : states) {
    788                     if (connectionState == state) {
    789                         devices.add(device);
    790                         break;
    791                     }
    792                 }
    793             }
    794         }
    795         return devices;
    796     }
    797 
    798     public int getConnectionState(BluetoothDevice device) {
    799         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    800         synchronized (mStateMachines) {
    801             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
    802             if (stateMachine == null) {
    803                 return BluetoothProfile.STATE_DISCONNECTED;
    804             }
    805             return stateMachine.getConnectionState();
    806         }
    807     }
    808 
    809     public boolean setPriority(BluetoothDevice device, int priority) {
    810         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    811         Settings.Global.putInt(getContentResolver(),
    812                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority);
    813         Log.i(TAG, "setPriority: device=" + device + ", priority=" + priority + ", "
    814                 + Utils.getUidPidString());
    815         return true;
    816     }
    817 
    818     public int getPriority(BluetoothDevice device) {
    819         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    820         return Settings.Global.getInt(getContentResolver(),
    821                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
    822                 BluetoothProfile.PRIORITY_UNDEFINED);
    823     }
    824 
    825     boolean startVoiceRecognition(BluetoothDevice device) {
    826         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    827         Log.i(TAG, "startVoiceRecognition: device=" + device + ", " + Utils.getUidPidString());
    828         synchronized (mStateMachines) {
    829             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
    830             if (mVoiceRecognitionStarted) {
    831                 boolean status = stopVoiceRecognition(mActiveDevice);
    832                 Log.w(TAG, "startVoiceRecognition: voice recognition is still active, just called "
    833                         + "stopVoiceRecognition, returned " + status + " on " + mActiveDevice
    834                         + ", please try again");
    835                 mVoiceRecognitionStarted = false;
    836                 return false;
    837             }
    838             if (!isAudioModeIdle()) {
    839                 Log.w(TAG, "startVoiceRecognition: audio mode not idle, active device is "
    840                         + mActiveDevice);
    841                 return false;
    842             }
    843             // Audio should not be on when no audio mode is active
    844             if (isAudioOn()) {
    845                 // Disconnect audio so that API user can try later
    846                 boolean status = disconnectAudio();
    847                 Log.w(TAG, "startVoiceRecognition: audio is still active, please wait for audio to"
    848                         + " be disconnected, disconnectAudio() returned " + status
    849                         + ", active device is " + mActiveDevice);
    850                 return false;
    851             }
    852             if (device == null) {
    853                 Log.i(TAG, "device is null, use active device " + mActiveDevice + " instead");
    854                 device = mActiveDevice;
    855             }
    856             boolean pendingRequestByHeadset = false;
    857             if (mVoiceRecognitionTimeoutEvent != null) {
    858                 if (!mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice.equals(device)) {
    859                     // TODO(b/79660380): Workaround when target device != requesting device
    860                     Log.w(TAG, "startVoiceRecognition: device " + device
    861                             + " is not the same as requesting device "
    862                             + mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice
    863                             + ", fall back to requesting device");
    864                     device = mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice;
    865                 }
    866                 mStateMachinesThread.getThreadHandler()
    867                         .removeCallbacks(mVoiceRecognitionTimeoutEvent);
    868                 mVoiceRecognitionTimeoutEvent = null;
    869                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
    870                     mSystemInterface.getVoiceRecognitionWakeLock().release();
    871                 }
    872                 pendingRequestByHeadset = true;
    873             }
    874             if (!Objects.equals(device, mActiveDevice) && !setActiveDevice(device)) {
    875                 Log.w(TAG, "startVoiceRecognition: failed to set " + device + " as active");
    876                 return false;
    877             }
    878             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
    879             if (stateMachine == null) {
    880                 Log.w(TAG, "startVoiceRecognition: " + device + " is never connected");
    881                 return false;
    882             }
    883             int connectionState = stateMachine.getConnectionState();
    884             if (connectionState != BluetoothProfile.STATE_CONNECTED
    885                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
    886                 Log.w(TAG, "startVoiceRecognition: " + device + " is not connected or connecting");
    887                 return false;
    888             }
    889             mVoiceRecognitionStarted = true;
    890             if (pendingRequestByHeadset) {
    891                 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_RESULT,
    892                         1 /* success */, 0, device);
    893             } else {
    894                 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device);
    895             }
    896             stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
    897         }
    898         return true;
    899     }
    900 
    901     boolean stopVoiceRecognition(BluetoothDevice device) {
    902         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    903         Log.i(TAG, "stopVoiceRecognition: device=" + device + ", " + Utils.getUidPidString());
    904         synchronized (mStateMachines) {
    905             if (!Objects.equals(mActiveDevice, device)) {
    906                 Log.w(TAG, "startVoiceRecognition: requested device " + device
    907                         + " is not active, use active device " + mActiveDevice + " instead");
    908                 device = mActiveDevice;
    909             }
    910             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
    911             if (stateMachine == null) {
    912                 Log.w(TAG, "stopVoiceRecognition: " + device + " is never connected");
    913                 return false;
    914             }
    915             int connectionState = stateMachine.getConnectionState();
    916             if (connectionState != BluetoothProfile.STATE_CONNECTED
    917                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
    918                 Log.w(TAG, "stopVoiceRecognition: " + device + " is not connected or connecting");
    919                 return false;
    920             }
    921             if (!mVoiceRecognitionStarted) {
    922                 Log.w(TAG, "stopVoiceRecognition: voice recognition was not started");
    923                 return false;
    924             }
    925             mVoiceRecognitionStarted = false;
    926             stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device);
    927             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
    928         }
    929         return true;
    930     }
    931 
    932     boolean isAudioOn() {
    933         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    934         return getNonIdleAudioDevices().size() > 0;
    935     }
    936 
    937     boolean isAudioConnected(BluetoothDevice device) {
    938         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    939         synchronized (mStateMachines) {
    940             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
    941             if (stateMachine == null) {
    942                 return false;
    943             }
    944             return stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED;
    945         }
    946     }
    947 
    948     int getAudioState(BluetoothDevice device) {
    949         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    950         synchronized (mStateMachines) {
    951             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
    952             if (stateMachine == null) {
    953                 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
    954             }
    955             return stateMachine.getAudioState();
    956         }
    957     }
    958 
    959     public void setAudioRouteAllowed(boolean allowed) {
    960         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    961         Log.i(TAG, "setAudioRouteAllowed: allowed=" + allowed + ", " + Utils.getUidPidString());
    962         mAudioRouteAllowed = allowed;
    963         mNativeInterface.setScoAllowed(allowed);
    964     }
    965 
    966     public boolean getAudioRouteAllowed() {
    967         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    968         return mAudioRouteAllowed;
    969     }
    970 
    971     public void setForceScoAudio(boolean forced) {
    972         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    973         Log.i(TAG, "setForceScoAudio: forced=" + forced + ", " + Utils.getUidPidString());
    974         mForceScoAudio = forced;
    975     }
    976 
    977     @VisibleForTesting
    978     public boolean getForceScoAudio() {
    979         return mForceScoAudio;
    980     }
    981 
    982     /**
    983      * Get first available device for SCO audio
    984      *
    985      * @return first connected headset device
    986      */
    987     @VisibleForTesting
    988     @Nullable
    989     public BluetoothDevice getFirstConnectedAudioDevice() {
    990         ArrayList<HeadsetStateMachine> stateMachines = new ArrayList<>();
    991         synchronized (mStateMachines) {
    992             List<BluetoothDevice> availableDevices =
    993                     getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
    994             for (BluetoothDevice device : availableDevices) {
    995                 final HeadsetStateMachine stateMachine = mStateMachines.get(device);
    996                 if (stateMachine == null) {
    997                     continue;
    998                 }
    999                 stateMachines.add(stateMachine);
   1000             }
   1001         }
   1002         stateMachines.sort(Comparator.comparingLong(HeadsetStateMachine::getConnectingTimestampMs));
   1003         if (stateMachines.size() > 0) {
   1004             return stateMachines.get(0).getDevice();
   1005         }
   1006         return null;
   1007     }
   1008 
   1009     /**
   1010      * Set the active device.
   1011      *
   1012      * @param device the active device
   1013      * @return true on success, otherwise false
   1014      */
   1015     public boolean setActiveDevice(BluetoothDevice device) {
   1016         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
   1017         Log.i(TAG, "setActiveDevice: device=" + device + ", " + Utils.getUidPidString());
   1018         synchronized (mStateMachines) {
   1019             if (device == null) {
   1020                 // Clear the active device
   1021                 if (mVoiceRecognitionStarted) {
   1022                     if (!stopVoiceRecognition(mActiveDevice)) {
   1023                         Log.w(TAG, "setActiveDevice: fail to stopVoiceRecognition from "
   1024                                 + mActiveDevice);
   1025                     }
   1026                 }
   1027                 if (mVirtualCallStarted) {
   1028                     if (!stopScoUsingVirtualVoiceCall()) {
   1029                         Log.w(TAG, "setActiveDevice: fail to stopScoUsingVirtualVoiceCall from "
   1030                                 + mActiveDevice);
   1031                     }
   1032                 }
   1033                 if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1034                     if (!disconnectAudio(mActiveDevice)) {
   1035                         Log.w(TAG, "setActiveDevice: disconnectAudio failed on " + mActiveDevice);
   1036                     }
   1037                 }
   1038                 mActiveDevice = null;
   1039                 broadcastActiveDevice(null);
   1040                 return true;
   1041             }
   1042             if (device.equals(mActiveDevice)) {
   1043                 Log.i(TAG, "setActiveDevice: device " + device + " is already active");
   1044                 return true;
   1045             }
   1046             if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
   1047                 Log.e(TAG, "setActiveDevice: Cannot set " + device
   1048                         + " as active, device is not connected");
   1049                 return false;
   1050             }
   1051             if (!mNativeInterface.setActiveDevice(device)) {
   1052                 Log.e(TAG, "setActiveDevice: Cannot set " + device + " as active in native layer");
   1053                 return false;
   1054             }
   1055             BluetoothDevice previousActiveDevice = mActiveDevice;
   1056             mActiveDevice = device;
   1057             if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1058                 if (!disconnectAudio(previousActiveDevice)) {
   1059                     Log.e(TAG, "setActiveDevice: fail to disconnectAudio from "
   1060                             + previousActiveDevice);
   1061                     mActiveDevice = previousActiveDevice;
   1062                     mNativeInterface.setActiveDevice(previousActiveDevice);
   1063                     return false;
   1064                 }
   1065                 broadcastActiveDevice(mActiveDevice);
   1066             } else if (shouldPersistAudio()) {
   1067                 broadcastActiveDevice(mActiveDevice);
   1068                 if (!connectAudio(mActiveDevice)) {
   1069                     Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice);
   1070                     mActiveDevice = previousActiveDevice;
   1071                     mNativeInterface.setActiveDevice(previousActiveDevice);
   1072                     return false;
   1073                 }
   1074             } else {
   1075                 broadcastActiveDevice(mActiveDevice);
   1076             }
   1077         }
   1078         return true;
   1079     }
   1080 
   1081     /**
   1082      * Get the active device.
   1083      *
   1084      * @return the active device or null if no device is active
   1085      */
   1086     public BluetoothDevice getActiveDevice() {
   1087         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1088         synchronized (mStateMachines) {
   1089             return mActiveDevice;
   1090         }
   1091     }
   1092 
   1093     boolean connectAudio() {
   1094         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
   1095         synchronized (mStateMachines) {
   1096             BluetoothDevice device = mActiveDevice;
   1097             if (device == null) {
   1098                 Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString());
   1099                 return false;
   1100             }
   1101             return connectAudio(device);
   1102         }
   1103     }
   1104 
   1105     boolean connectAudio(BluetoothDevice device) {
   1106         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
   1107         Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString());
   1108         synchronized (mStateMachines) {
   1109             if (!isScoAcceptable(device)) {
   1110                 Log.w(TAG, "connectAudio, rejected SCO request to " + device);
   1111                 return false;
   1112             }
   1113             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
   1114             if (stateMachine == null) {
   1115                 Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting");
   1116                 return false;
   1117             }
   1118             if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
   1119                 Log.w(TAG, "connectAudio: profile not connected");
   1120                 return false;
   1121             }
   1122             if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1123                 logD("connectAudio: audio is not idle for device " + device);
   1124                 return true;
   1125             }
   1126             if (isAudioOn()) {
   1127                 Log.w(TAG, "connectAudio: audio is not idle, current audio devices are "
   1128                         + Arrays.toString(getNonIdleAudioDevices().toArray()));
   1129                 return false;
   1130             }
   1131             stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
   1132         }
   1133         return true;
   1134     }
   1135 
   1136     private List<BluetoothDevice> getNonIdleAudioDevices() {
   1137         ArrayList<BluetoothDevice> devices = new ArrayList<>();
   1138         synchronized (mStateMachines) {
   1139             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
   1140                 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1141                     devices.add(stateMachine.getDevice());
   1142                 }
   1143             }
   1144         }
   1145         return devices;
   1146     }
   1147 
   1148     boolean disconnectAudio() {
   1149         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
   1150         boolean result = false;
   1151         synchronized (mStateMachines) {
   1152             for (BluetoothDevice device : getNonIdleAudioDevices()) {
   1153                 if (disconnectAudio(device)) {
   1154                     result = true;
   1155                 } else {
   1156                     Log.e(TAG, "disconnectAudio() from " + device + " failed");
   1157                 }
   1158             }
   1159         }
   1160         if (!result) {
   1161             logD("disconnectAudio() no active audio connection");
   1162         }
   1163         return result;
   1164     }
   1165 
   1166     boolean disconnectAudio(BluetoothDevice device) {
   1167         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
   1168         synchronized (mStateMachines) {
   1169             Log.i(TAG, "disconnectAudio: device=" + device + ", " + Utils.getUidPidString());
   1170             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
   1171             if (stateMachine == null) {
   1172                 Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting");
   1173                 return false;
   1174             }
   1175             if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1176                 Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device);
   1177                 return false;
   1178             }
   1179             stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
   1180         }
   1181         return true;
   1182     }
   1183 
   1184     boolean isVirtualCallStarted() {
   1185         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1186         synchronized (mStateMachines) {
   1187             return mVirtualCallStarted;
   1188         }
   1189     }
   1190 
   1191     private boolean startScoUsingVirtualVoiceCall() {
   1192         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
   1193         Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
   1194         synchronized (mStateMachines) {
   1195             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
   1196             if (mVoiceRecognitionStarted) {
   1197                 boolean status = stopVoiceRecognition(mActiveDevice);
   1198                 Log.w(TAG, "startScoUsingVirtualVoiceCall: voice recognition is still active, "
   1199                         + "just called stopVoiceRecognition, returned " + status + " on "
   1200                         + mActiveDevice + ", please try again");
   1201                 mVoiceRecognitionStarted = false;
   1202                 return false;
   1203             }
   1204             if (!isAudioModeIdle()) {
   1205                 Log.w(TAG, "startScoUsingVirtualVoiceCall: audio mode not idle, active device is "
   1206                         + mActiveDevice);
   1207                 return false;
   1208             }
   1209             // Audio should not be on when no audio mode is active
   1210             if (isAudioOn()) {
   1211                 // Disconnect audio so that API user can try later
   1212                 boolean status = disconnectAudio();
   1213                 Log.w(TAG, "startScoUsingVirtualVoiceCall: audio is still active, please wait for "
   1214                         + "audio to be disconnected, disconnectAudio() returned " + status
   1215                         + ", active device is " + mActiveDevice);
   1216                 return false;
   1217             }
   1218             if (mActiveDevice == null) {
   1219                 Log.w(TAG, "startScoUsingVirtualVoiceCall: no active device");
   1220                 return false;
   1221             }
   1222             mVirtualCallStarted = true;
   1223             // Send virtual phone state changed to initialize SCO
   1224             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0, true);
   1225             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0, true);
   1226             phoneStateChanged(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, true);
   1227             return true;
   1228         }
   1229     }
   1230 
   1231     boolean stopScoUsingVirtualVoiceCall() {
   1232         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
   1233         Log.i(TAG, "stopScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
   1234         synchronized (mStateMachines) {
   1235             // 1. Check if virtual call has already started
   1236             if (!mVirtualCallStarted) {
   1237                 Log.w(TAG, "stopScoUsingVirtualVoiceCall: virtual call not started");
   1238                 return false;
   1239             }
   1240             mVirtualCallStarted = false;
   1241             // 2. Send virtual phone state changed to close SCO
   1242             phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, true);
   1243         }
   1244         return true;
   1245     }
   1246 
   1247     class DialingOutTimeoutEvent implements Runnable {
   1248         BluetoothDevice mDialingOutDevice;
   1249 
   1250         DialingOutTimeoutEvent(BluetoothDevice fromDevice) {
   1251             mDialingOutDevice = fromDevice;
   1252         }
   1253 
   1254         @Override
   1255         public void run() {
   1256             synchronized (mStateMachines) {
   1257                 mDialingOutTimeoutEvent = null;
   1258                 doForStateMachine(mDialingOutDevice, stateMachine -> stateMachine.sendMessage(
   1259                         HeadsetStateMachine.DIALING_OUT_RESULT, 0 /* fail */, 0,
   1260                         mDialingOutDevice));
   1261             }
   1262         }
   1263 
   1264         @Override
   1265         public String toString() {
   1266             return "DialingOutTimeoutEvent[" + mDialingOutDevice + "]";
   1267         }
   1268     }
   1269 
   1270     /**
   1271      * Dial an outgoing call as requested by the remote device
   1272      *
   1273      * @param fromDevice remote device that initiated this dial out action
   1274      * @param dialNumber number to dial
   1275      * @return true on successful dial out
   1276      */
   1277     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
   1278     public boolean dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber) {
   1279         synchronized (mStateMachines) {
   1280             Log.i(TAG, "dialOutgoingCall: from " + fromDevice);
   1281             if (!isOnStateMachineThread()) {
   1282                 Log.e(TAG, "dialOutgoingCall must be called from state machine thread");
   1283                 return false;
   1284             }
   1285             if (mDialingOutTimeoutEvent != null) {
   1286                 Log.e(TAG, "dialOutgoingCall, already dialing by " + mDialingOutTimeoutEvent);
   1287                 return false;
   1288             }
   1289             if (isVirtualCallStarted()) {
   1290                 if (!stopScoUsingVirtualVoiceCall()) {
   1291                     Log.e(TAG, "dialOutgoingCall failed to stop current virtual call");
   1292                     return false;
   1293                 }
   1294             }
   1295             if (!setActiveDevice(fromDevice)) {
   1296                 Log.e(TAG, "dialOutgoingCall failed to set active device to " + fromDevice);
   1297                 return false;
   1298             }
   1299             Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
   1300                     Uri.fromParts(PhoneAccount.SCHEME_TEL, dialNumber, null));
   1301             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1302             startActivity(intent);
   1303             mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(fromDevice);
   1304             mStateMachinesThread.getThreadHandler()
   1305                     .postDelayed(mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS);
   1306             return true;
   1307         }
   1308     }
   1309 
   1310     /**
   1311      * Check if any connected headset has started dialing calls
   1312      *
   1313      * @return true if some device has started dialing calls
   1314      */
   1315     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
   1316     public boolean hasDeviceInitiatedDialingOut() {
   1317         synchronized (mStateMachines) {
   1318             return mDialingOutTimeoutEvent != null;
   1319         }
   1320     }
   1321 
   1322     class VoiceRecognitionTimeoutEvent implements Runnable {
   1323         BluetoothDevice mVoiceRecognitionDevice;
   1324 
   1325         VoiceRecognitionTimeoutEvent(BluetoothDevice device) {
   1326             mVoiceRecognitionDevice = device;
   1327         }
   1328 
   1329         @Override
   1330         public void run() {
   1331             synchronized (mStateMachines) {
   1332                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
   1333                     mSystemInterface.getVoiceRecognitionWakeLock().release();
   1334                 }
   1335                 mVoiceRecognitionTimeoutEvent = null;
   1336                 doForStateMachine(mVoiceRecognitionDevice, stateMachine -> stateMachine.sendMessage(
   1337                         HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 0 /* fail */, 0,
   1338                         mVoiceRecognitionDevice));
   1339             }
   1340         }
   1341 
   1342         @Override
   1343         public String toString() {
   1344             return "VoiceRecognitionTimeoutEvent[" + mVoiceRecognitionDevice + "]";
   1345         }
   1346     }
   1347 
   1348     boolean startVoiceRecognitionByHeadset(BluetoothDevice fromDevice) {
   1349         synchronized (mStateMachines) {
   1350             Log.i(TAG, "startVoiceRecognitionByHeadset: from " + fromDevice);
   1351             // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
   1352             if (mVoiceRecognitionStarted) {
   1353                 boolean status = stopVoiceRecognition(mActiveDevice);
   1354                 Log.w(TAG, "startVoiceRecognitionByHeadset: voice recognition is still active, "
   1355                         + "just called stopVoiceRecognition, returned " + status + " on "
   1356                         + mActiveDevice + ", please try again");
   1357                 mVoiceRecognitionStarted = false;
   1358                 return false;
   1359             }
   1360             if (fromDevice == null) {
   1361                 Log.e(TAG, "startVoiceRecognitionByHeadset: fromDevice is null");
   1362                 return false;
   1363             }
   1364             if (!isAudioModeIdle()) {
   1365                 Log.w(TAG, "startVoiceRecognitionByHeadset: audio mode not idle, active device is "
   1366                         + mActiveDevice);
   1367                 return false;
   1368             }
   1369             // Audio should not be on when no audio mode is active
   1370             if (isAudioOn()) {
   1371                 // Disconnect audio so that user can try later
   1372                 boolean status = disconnectAudio();
   1373                 Log.w(TAG, "startVoiceRecognitionByHeadset: audio is still active, please wait for"
   1374                         + " audio to be disconnected, disconnectAudio() returned " + status
   1375                         + ", active device is " + mActiveDevice);
   1376                 return false;
   1377             }
   1378             // Do not start new request until the current one is finished or timeout
   1379             if (mVoiceRecognitionTimeoutEvent != null) {
   1380                 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice
   1381                         + ", already pending by " + mVoiceRecognitionTimeoutEvent);
   1382                 return false;
   1383             }
   1384             if (!setActiveDevice(fromDevice)) {
   1385                 Log.w(TAG, "startVoiceRecognitionByHeadset: failed to set " + fromDevice
   1386                         + " as active");
   1387                 return false;
   1388             }
   1389             IDeviceIdleController deviceIdleController = IDeviceIdleController.Stub.asInterface(
   1390                     ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
   1391             if (deviceIdleController == null) {
   1392                 Log.w(TAG, "startVoiceRecognitionByHeadset: deviceIdleController is null, device="
   1393                         + fromDevice);
   1394                 return false;
   1395             }
   1396             try {
   1397                 deviceIdleController.exitIdle("voice-command");
   1398             } catch (RemoteException e) {
   1399                 Log.w(TAG,
   1400                         "startVoiceRecognitionByHeadset: failed to exit idle, device=" + fromDevice
   1401                                 + ", error=" + e.getMessage());
   1402                 return false;
   1403             }
   1404             if (!mSystemInterface.activateVoiceRecognition()) {
   1405                 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice);
   1406                 return false;
   1407             }
   1408             mVoiceRecognitionTimeoutEvent = new VoiceRecognitionTimeoutEvent(fromDevice);
   1409             mStateMachinesThread.getThreadHandler()
   1410                     .postDelayed(mVoiceRecognitionTimeoutEvent, sStartVrTimeoutMs);
   1411             if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
   1412                 mSystemInterface.getVoiceRecognitionWakeLock().acquire(sStartVrTimeoutMs);
   1413             }
   1414             return true;
   1415         }
   1416     }
   1417 
   1418     boolean stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice) {
   1419         synchronized (mStateMachines) {
   1420             Log.i(TAG, "stopVoiceRecognitionByHeadset: from " + fromDevice);
   1421             if (!Objects.equals(fromDevice, mActiveDevice)) {
   1422                 Log.w(TAG, "stopVoiceRecognitionByHeadset: " + fromDevice
   1423                         + " is not active, active device is " + mActiveDevice);
   1424                 return false;
   1425             }
   1426             if (!mVoiceRecognitionStarted && mVoiceRecognitionTimeoutEvent == null) {
   1427                 Log.w(TAG, "stopVoiceRecognitionByHeadset: voice recognition not started, device="
   1428                         + fromDevice);
   1429                 return false;
   1430             }
   1431             if (mVoiceRecognitionTimeoutEvent != null) {
   1432                 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
   1433                     mSystemInterface.getVoiceRecognitionWakeLock().release();
   1434                 }
   1435                 mStateMachinesThread.getThreadHandler()
   1436                         .removeCallbacks(mVoiceRecognitionTimeoutEvent);
   1437                 mVoiceRecognitionTimeoutEvent = null;
   1438             }
   1439             if (mVoiceRecognitionStarted) {
   1440                 if (!disconnectAudio()) {
   1441                     Log.w(TAG, "stopVoiceRecognitionByHeadset: failed to disconnect audio from "
   1442                             + fromDevice);
   1443                 }
   1444                 mVoiceRecognitionStarted = false;
   1445             }
   1446             if (!mSystemInterface.deactivateVoiceRecognition()) {
   1447                 Log.w(TAG, "stopVoiceRecognitionByHeadset: failed request from " + fromDevice);
   1448                 return false;
   1449             }
   1450             return true;
   1451         }
   1452     }
   1453 
   1454     private void phoneStateChanged(int numActive, int numHeld, int callState, String number,
   1455             int type, boolean isVirtualCall) {
   1456         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission");
   1457         synchronized (mStateMachines) {
   1458             // Should stop all other audio mode in this case
   1459             if ((numActive + numHeld) > 0 || callState != HeadsetHalConstants.CALL_STATE_IDLE) {
   1460                 if (!isVirtualCall && mVirtualCallStarted) {
   1461                     // stop virtual voice call if there is an incoming Telecom call update
   1462                     stopScoUsingVirtualVoiceCall();
   1463                 }
   1464                 if (mVoiceRecognitionStarted) {
   1465                     // stop voice recognition if there is any incoming call
   1466                     stopVoiceRecognition(mActiveDevice);
   1467                 }
   1468             }
   1469             if (mDialingOutTimeoutEvent != null) {
   1470                 // Send result to state machine when dialing starts
   1471                 if (callState == HeadsetHalConstants.CALL_STATE_DIALING) {
   1472                     mStateMachinesThread.getThreadHandler()
   1473                             .removeCallbacks(mDialingOutTimeoutEvent);
   1474                     doForStateMachine(mDialingOutTimeoutEvent.mDialingOutDevice,
   1475                             stateMachine -> stateMachine.sendMessage(
   1476                                     HeadsetStateMachine.DIALING_OUT_RESULT, 1 /* success */, 0,
   1477                                     mDialingOutTimeoutEvent.mDialingOutDevice));
   1478                 } else if (callState == HeadsetHalConstants.CALL_STATE_ACTIVE
   1479                         || callState == HeadsetHalConstants.CALL_STATE_IDLE) {
   1480                     // Clear the timeout event when the call is connected or disconnected
   1481                     if (!mStateMachinesThread.getThreadHandler()
   1482                             .hasCallbacks(mDialingOutTimeoutEvent)) {
   1483                         mDialingOutTimeoutEvent = null;
   1484                     }
   1485                 }
   1486             }
   1487         }
   1488         mStateMachinesThread.getThreadHandler().post(() -> {
   1489             boolean shouldCallAudioBeActiveBefore = shouldCallAudioBeActive();
   1490             mSystemInterface.getHeadsetPhoneState().setNumActiveCall(numActive);
   1491             mSystemInterface.getHeadsetPhoneState().setNumHeldCall(numHeld);
   1492             mSystemInterface.getHeadsetPhoneState().setCallState(callState);
   1493             // Suspend A2DP when call about is about to become active
   1494             if (callState != HeadsetHalConstants.CALL_STATE_DISCONNECTED
   1495                     && shouldCallAudioBeActive() && !shouldCallAudioBeActiveBefore) {
   1496                 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true");
   1497             }
   1498         });
   1499         doForEachConnectedStateMachine(
   1500                 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED,
   1501                         new HeadsetCallState(numActive, numHeld, callState, number, type)));
   1502         mStateMachinesThread.getThreadHandler().post(() -> {
   1503             if (callState == HeadsetHalConstants.CALL_STATE_IDLE
   1504                     && !shouldCallAudioBeActive() && !isAudioOn()) {
   1505                 // Resume A2DP when call ended and SCO is not connected
   1506                 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false");
   1507             }
   1508         });
   1509 
   1510     }
   1511 
   1512     private void clccResponse(int index, int direction, int status, int mode, boolean mpty,
   1513             String number, int type) {
   1514         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission");
   1515         doForEachConnectedStateMachine(
   1516                 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE,
   1517                         new HeadsetClccResponse(index, direction, status, mode, mpty, number,
   1518                                 type)));
   1519     }
   1520 
   1521     private boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
   1522             String arg) {
   1523         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1524         synchronized (mStateMachines) {
   1525             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
   1526             if (stateMachine == null) {
   1527                 Log.w(TAG, "sendVendorSpecificResultCode: device " + device
   1528                         + " was never connected/connecting");
   1529                 return false;
   1530             }
   1531             int connectionState = stateMachine.getConnectionState();
   1532             if (connectionState != BluetoothProfile.STATE_CONNECTED) {
   1533                 return false;
   1534             }
   1535             // Currently we support only "+ANDROID".
   1536             if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) {
   1537                 Log.w(TAG, "Disallowed unsolicited result code command: " + command);
   1538                 return false;
   1539             }
   1540             stateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE,
   1541                     new HeadsetVendorSpecificResultCode(device, command, arg));
   1542         }
   1543         return true;
   1544     }
   1545 
   1546     boolean isInbandRingingEnabled() {
   1547         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1548         return BluetoothHeadset.isInbandRingingSupported(this) && !SystemProperties.getBoolean(
   1549                 DISABLE_INBAND_RINGING_PROPERTY, false) && !mInbandRingingRuntimeDisable;
   1550     }
   1551 
   1552     /**
   1553      * Called from {@link HeadsetStateMachine} in state machine thread when there is a connection
   1554      * state change
   1555      *
   1556      * @param device remote device
   1557      * @param fromState from which connection state is the change
   1558      * @param toState to which connection state is the change
   1559      */
   1560     @VisibleForTesting
   1561     public void onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState,
   1562             int toState) {
   1563         synchronized (mStateMachines) {
   1564             List<BluetoothDevice> audioConnectableDevices =
   1565                     getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
   1566             if (fromState != BluetoothProfile.STATE_CONNECTED
   1567                     && toState == BluetoothProfile.STATE_CONNECTED) {
   1568                 if (audioConnectableDevices.size() > 1) {
   1569                     mInbandRingingRuntimeDisable = true;
   1570                     doForEachConnectedStateMachine(
   1571                             stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR,
   1572                                     0));
   1573                 }
   1574                 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET);
   1575             }
   1576             if (fromState != BluetoothProfile.STATE_DISCONNECTED
   1577                     && toState == BluetoothProfile.STATE_DISCONNECTED) {
   1578                 if (audioConnectableDevices.size() <= 1) {
   1579                     mInbandRingingRuntimeDisable = false;
   1580                     doForEachConnectedStateMachine(
   1581                             stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR,
   1582                                     1));
   1583                 }
   1584                 if (device.equals(mActiveDevice)) {
   1585                     setActiveDevice(null);
   1586                 }
   1587             }
   1588         }
   1589     }
   1590 
   1591     /**
   1592      * Check if no audio mode is active
   1593      *
   1594      * @return false if virtual call, voice recognition, or Telecom call is active, true if all idle
   1595      */
   1596     private boolean isAudioModeIdle() {
   1597         synchronized (mStateMachines) {
   1598             if (mVoiceRecognitionStarted || mVirtualCallStarted || !mSystemInterface.isCallIdle()) {
   1599                 Log.i(TAG, "isAudioModeIdle: not idle, mVoiceRecognitionStarted="
   1600                         + mVoiceRecognitionStarted + ", mVirtualCallStarted=" + mVirtualCallStarted
   1601                         + ", isCallIdle=" + mSystemInterface.isCallIdle());
   1602                 return false;
   1603             }
   1604             return true;
   1605         }
   1606     }
   1607 
   1608     private boolean shouldCallAudioBeActive() {
   1609         return mSystemInterface.isInCall() || (mSystemInterface.isRinging()
   1610                 && isInbandRingingEnabled());
   1611     }
   1612 
   1613     /**
   1614      * Only persist audio during active device switch when call audio is supposed to be active and
   1615      * virtual call has not been started. Virtual call is ignored because AudioService and
   1616      * applications should reconnect SCO during active device switch and forcing SCO connection
   1617      * here will make AudioService think SCO is started externally instead of by one of its SCO
   1618      * clients.
   1619      *
   1620      * @return true if call audio should be active and no virtual call is going on
   1621      */
   1622     private boolean shouldPersistAudio() {
   1623         return !mVirtualCallStarted && shouldCallAudioBeActive();
   1624     }
   1625 
   1626     /**
   1627      * Called from {@link HeadsetStateMachine} in state machine thread when there is a audio
   1628      * connection state change
   1629      *
   1630      * @param device remote device
   1631      * @param fromState from which audio connection state is the change
   1632      * @param toState to which audio connection state is the change
   1633      */
   1634     @VisibleForTesting
   1635     public void onAudioStateChangedFromStateMachine(BluetoothDevice device, int fromState,
   1636             int toState) {
   1637         synchronized (mStateMachines) {
   1638             if (toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1639                 if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1640                     if (mActiveDevice != null && !mActiveDevice.equals(device)
   1641                             && shouldPersistAudio()) {
   1642                         if (!connectAudio(mActiveDevice)) {
   1643                             Log.w(TAG, "onAudioStateChangedFromStateMachine, failed to connect"
   1644                                     + " audio to new " + "active device " + mActiveDevice
   1645                                     + ", after " + device + " is disconnected from SCO");
   1646                         }
   1647                     }
   1648                 }
   1649                 if (mVoiceRecognitionStarted) {
   1650                     if (!stopVoiceRecognitionByHeadset(device)) {
   1651                         Log.w(TAG, "onAudioStateChangedFromStateMachine: failed to stop voice "
   1652                                 + "recognition");
   1653                     }
   1654                 }
   1655                 if (mVirtualCallStarted) {
   1656                     if (!stopScoUsingVirtualVoiceCall()) {
   1657                         Log.w(TAG, "onAudioStateChangedFromStateMachine: failed to stop virtual "
   1658                                 + "voice call");
   1659                     }
   1660                 }
   1661                 // Unsuspend A2DP when SCO connection is gone and call state is idle
   1662                 if (mSystemInterface.isCallIdle()) {
   1663                     mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false");
   1664                 }
   1665             }
   1666         }
   1667     }
   1668 
   1669     private void broadcastActiveDevice(BluetoothDevice device) {
   1670         logD("broadcastActiveDevice: " + device);
   1671         Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
   1672         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
   1673         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   1674                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
   1675         sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
   1676     }
   1677 
   1678     /**
   1679      * Check whether it is OK to accept a headset connection from a remote device
   1680      *
   1681      * @param device remote device that initiates the connection
   1682      * @return true if the connection is acceptable
   1683      */
   1684     public boolean okToAcceptConnection(BluetoothDevice device) {
   1685         // Check if this is an incoming connection in Quiet mode.
   1686         if (mAdapterService.isQuietModeEnabled()) {
   1687             Log.w(TAG, "okToAcceptConnection: return false as quiet mode enabled");
   1688             return false;
   1689         }
   1690         // Check priority and accept or reject the connection.
   1691         // Note: Logic can be simplified, but keeping it this way for readability
   1692         int priority = getPriority(device);
   1693         int bondState = mAdapterService.getBondState(device);
   1694         // If priority is undefined, it is likely that service discovery has not completed and peer
   1695         // initiated the connection. Allow this connection only if the device is bonded or bonding
   1696         boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED) && (
   1697                 bondState == BluetoothDevice.BOND_BONDING
   1698                         || bondState == BluetoothDevice.BOND_BONDED);
   1699         // Also allow connection when device is bonded/bonding and priority is ON/AUTO_CONNECT.
   1700         boolean isEnabled = (priority == BluetoothProfile.PRIORITY_ON
   1701                 || priority == BluetoothProfile.PRIORITY_AUTO_CONNECT) && (
   1702                 bondState == BluetoothDevice.BOND_BONDED
   1703                         || bondState == BluetoothDevice.BOND_BONDING);
   1704         if (!serviceDiscoveryPending && !isEnabled) {
   1705             // Otherwise, reject the connection if no service discovery is pending and priority is
   1706             // neither PRIORITY_ON nor PRIORITY_AUTO_CONNECT
   1707             Log.w(TAG,
   1708                     "okToConnect: return false, priority=" + priority + ", bondState=" + bondState);
   1709             return false;
   1710         }
   1711         List<BluetoothDevice> connectingConnectedDevices =
   1712                 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
   1713         if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
   1714             Log.w(TAG, "Maximum number of connections " + mMaxHeadsetConnections
   1715                     + " was reached, rejecting connection from " + device);
   1716             return false;
   1717         }
   1718         return true;
   1719     }
   1720 
   1721     /**
   1722      * Checks if SCO should be connected at current system state
   1723      *
   1724      * @param device device for SCO to be connected
   1725      * @return true if SCO is allowed to be connected
   1726      */
   1727     public boolean isScoAcceptable(BluetoothDevice device) {
   1728         synchronized (mStateMachines) {
   1729             if (device == null || !device.equals(mActiveDevice)) {
   1730                 Log.w(TAG, "isScoAcceptable: rejected SCO since " + device
   1731                         + " is not the current active device " + mActiveDevice);
   1732                 return false;
   1733             }
   1734             if (mForceScoAudio) {
   1735                 return true;
   1736             }
   1737             if (!mAudioRouteAllowed) {
   1738                 Log.w(TAG, "isScoAcceptable: rejected SCO since audio route is not allowed");
   1739                 return false;
   1740             }
   1741             if (mVoiceRecognitionStarted || mVirtualCallStarted) {
   1742                 return true;
   1743             }
   1744             if (shouldCallAudioBeActive()) {
   1745                 return true;
   1746             }
   1747             Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall()
   1748                     + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing="
   1749                     + mSystemInterface.isRinging() + ", inbandRinging=" + isInbandRingingEnabled()
   1750                     + ", isVirtualCallStarted=" + mVirtualCallStarted);
   1751             return false;
   1752         }
   1753     }
   1754 
   1755     /**
   1756      * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice}
   1757      *
   1758      * @param device device whose state machine is to be removed.
   1759      */
   1760     void removeStateMachine(BluetoothDevice device) {
   1761         synchronized (mStateMachines) {
   1762             HeadsetStateMachine stateMachine = mStateMachines.get(device);
   1763             if (stateMachine == null) {
   1764                 Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine");
   1765                 return;
   1766             }
   1767             Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device);
   1768             HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine);
   1769             mStateMachines.remove(device);
   1770         }
   1771     }
   1772 
   1773     private boolean isOnStateMachineThread() {
   1774         final Looper myLooper = Looper.myLooper();
   1775         return myLooper != null && (mStateMachinesThread != null) && (myLooper.getThread().getId()
   1776                 == mStateMachinesThread.getId());
   1777     }
   1778 
   1779     @Override
   1780     public void dump(StringBuilder sb) {
   1781         synchronized (mStateMachines) {
   1782             super.dump(sb);
   1783             ProfileService.println(sb, "mMaxHeadsetConnections: " + mMaxHeadsetConnections);
   1784             ProfileService.println(sb, "DefaultMaxHeadsetConnections: "
   1785                     + mAdapterService.getMaxConnectedAudioDevices());
   1786             ProfileService.println(sb, "mActiveDevice: " + mActiveDevice);
   1787             ProfileService.println(sb, "isInbandRingingEnabled: " + isInbandRingingEnabled());
   1788             ProfileService.println(sb,
   1789                     "isInbandRingingSupported: " + BluetoothHeadset.isInbandRingingSupported(this));
   1790             ProfileService.println(sb,
   1791                     "mInbandRingingRuntimeDisable: " + mInbandRingingRuntimeDisable);
   1792             ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed);
   1793             ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
   1794             ProfileService.println(sb,
   1795                     "mVoiceRecognitionTimeoutEvent: " + mVoiceRecognitionTimeoutEvent);
   1796             ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted);
   1797             ProfileService.println(sb, "mDialingOutTimeoutEvent: " + mDialingOutTimeoutEvent);
   1798             ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio);
   1799             ProfileService.println(sb, "mCreated: " + mCreated);
   1800             ProfileService.println(sb, "mStarted: " + mStarted);
   1801             ProfileService.println(sb,
   1802                     "AudioManager.isBluetoothScoOn(): " + mSystemInterface.getAudioManager()
   1803                             .isBluetoothScoOn());
   1804             ProfileService.println(sb, "Telecom.isInCall(): " + mSystemInterface.isInCall());
   1805             ProfileService.println(sb, "Telecom.isRinging(): " + mSystemInterface.isRinging());
   1806             for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
   1807                 ProfileService.println(sb,
   1808                         "==== StateMachine for " + stateMachine.getDevice() + " ====");
   1809                 stateMachine.dump(sb);
   1810             }
   1811         }
   1812     }
   1813 
   1814     private static void logD(String message) {
   1815         if (DBG) {
   1816             Log.d(TAG, message);
   1817         }
   1818     }
   1819 }
   1820