Merge "Add locking to AVRCP Target JNI"
diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index cc33719..508eda6 100644
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -897,48 +897,27 @@
             mProfilesConnected = 0;
             mProfilesConnecting = 0;
             mProfilesDisconnecting = 0;
-            // When BT is being turned on, all adapter properties will be sent in 1
-            // callback. At this stage, set the scan mode.
-            if (getState() == BluetoothAdapter.STATE_TURNING_ON
-                    && mScanMode == BluetoothAdapter.SCAN_MODE_NONE) {
-                    /* mDiscoverableTimeout is part of the
-                       adapterPropertyChangedCallback received before
-                       onBluetoothReady */
-                if (mDiscoverableTimeout != 0) {
-                    setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
-                } else /* if timeout == never (0) at startup */ {
-                    setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
-                }
-                    /* though not always required, this keeps NV up-to date on first-boot after
-                    flash */
-                setDiscoverableTimeout(mDiscoverableTimeout);
-            }
+            // adapterPropertyChangedCallback has already been received.  Set the scan mode.
+            setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
+            // This keeps NV up-to date on first-boot after flash.
+            setDiscoverableTimeout(mDiscoverableTimeout);
         }
     }
 
     void onBleDisable() {
         // Sequence BLE_ON to STATE_OFF - that is _complete_ OFF state.
-        // When BT disable is invoked, set the scan_mode to NONE
-        // so no incoming connections are possible
         debugLog("onBleDisable");
-        if (getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
-            setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
-        }
+        // Set the scan_mode to NONE (no incoming connections).
+        setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
     }
 
     void onBluetoothDisable() {
         // From STATE_ON to BLE_ON
-        // When BT disable is invoked, set the scan_mode to NONE
-        // so no incoming connections are possible
-
-        //Set flag to indicate we are disabling. When property change of scan mode done
-        //continue with disable sequence
         debugLog("onBluetoothDisable()");
-        if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
-            // Turn off any Device Search/Inquiry
-            mService.cancelDiscovery();
-            setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
-        }
+        // Turn off any Device Search/Inquiry
+        mService.cancelDiscovery();
+        // Set the scan_mode to NONE (no incoming connections).
+        setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
     }
 
     void discoveryStateChangeCallback(int state) {
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index 55bde40..4191174 100644
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -84,6 +84,12 @@
         mNativeInterface.initializeNative();
 
         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        if (mAudioManager == null) {
+            Log.e(TAG, "AudioManager service doesn't exist?");
+        } else {
+            // start AudioManager in a known state
+            mAudioManager.setParameters("hfp_enable=false");
+        }
 
         mSmFactory = new HeadsetClientStateMachineFactory();
         mStateMachineMap.clear();
@@ -924,4 +930,8 @@
     protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
         mSmFactory = factory;
     }
+
+    AudioManager getAudioManager() {
+        return mAudioManager;
+    }
 }
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
index 5b65b6b..266358c 100644
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@@ -39,8 +39,9 @@
 import android.bluetooth.BluetoothHeadsetClientCall;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
-import android.content.Context;
 import android.content.Intent;
+import android.media.AudioAttributes;
+import android.media.AudioFocusRequest;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Looper;
@@ -156,7 +157,6 @@
     // indicator
     private Pair<Integer, Object> mPendingAction;
 
-    private static AudioManager sAudioManager;
     private int mAudioState;
     private boolean mAudioWbs;
     private int mVoiceRecognitionActive;
@@ -169,6 +169,11 @@
     private int mPeerFeatures;
     private int mChldFeatures;
 
+    // This is returned when requesting focus from AudioManager
+    private AudioFocusRequest mAudioFocusRequest;
+
+    private AudioManager mAudioManager;
+
     // Accessor for the states, useful for reusing the state machines
     public IState getDisconnectedState() {
         return mDisconnected;
@@ -690,13 +695,9 @@
     HeadsetClientStateMachine(HeadsetClientService context, Looper looper) {
         super(TAG, looper);
         mService = context;
+        mAudioManager = mService.getAudioManager();
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        if (sAudioManager == null) {
-            sAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-            // Initialize hfp_enable into a known state.
-            routeHfpAudio(false);
-        }
         mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
         mAudioWbs = false;
         mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED;
@@ -706,8 +707,8 @@
         mIndicatorNetworkSignal = 0;
         mIndicatorBatteryLevel = 0;
 
-        sMaxAmVcVol = sAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
-        sMinAmVcVol = sAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
+        sMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
+        sMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
 
         mOperatorName = null;
         mSubscriberInfo = null;
@@ -740,26 +741,53 @@
         return hfcsm;
     }
 
-    static synchronized void routeHfpAudio(boolean enable) {
+    synchronized void routeHfpAudio(boolean enable) {
+        if (mAudioManager == null) {
+            Log.e(TAG, "AudioManager is null!");
+            return;
+        }
         if (DBG) {
             Log.d(TAG, "hfp_enable=" + enable);
         }
         if (enable && !sAudioIsRouted) {
-            sAudioManager.setParameters("hfp_enable=true");
+            mAudioManager.setParameters("hfp_enable=true");
         } else if (!enable) {
-            sAudioManager.setParameters("hfp_enable=false");
+            mAudioManager.setParameters("hfp_enable=false");
         }
         sAudioIsRouted = enable;
     }
 
+    private AudioFocusRequest requestAudioFocus() {
+        AudioAttributes streamAttributes =
+                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
+                        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+                        .build();
+        AudioFocusRequest focusRequest =
+                new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
+                        .setAudioAttributes(streamAttributes)
+                        .build();
+        int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest);
+        if (DBG) {
+            String s = (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
+                    ? "AudioFocus granted" : "AudioFocus NOT granted";
+            Log.d(TAG, "AudioManager requestAudioFocus returned: " + s);
+        }
+        return focusRequest;
+    }
+
     public void doQuit() {
         Log.d(TAG, "doQuit");
-        if (sAudioManager != null) {
-            routeHfpAudio(false);
-        }
+        routeHfpAudio(false);
+        returnAudioFocusIfNecessary();
         quitNow();
     }
 
+    private void returnAudioFocusIfNecessary() {
+        if (mAudioFocusRequest == null) return;
+        mAudioManager.abandonAudioFocusRequest(mAudioFocusRequest);
+        mAudioFocusRequest = null;
+    }
+
     static int hfToAmVol(int hfVol) {
         int amRange = sMaxAmVcVol - sMinAmVcVol;
         int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
@@ -1025,13 +1053,13 @@
                         }
                     }
 
-                    int amVol = sAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+                    int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
                     deferMessage(
                             obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0));
                     // Mic is either in ON state (full volume) or OFF state. There is no way in
                     // Android to change the MIC volume.
                     deferMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME,
-                            sAudioManager.isMicrophoneMute() ? 0 : 15, 0));
+                            mAudioManager.isMicrophoneMute() ? 0 : 15, 0));
                     // query subscriber info
                     deferMessage(obtainMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO));
                     transitionTo(mConnected);
@@ -1361,11 +1389,11 @@
                             if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) {
                                 mCommandedSpeakerVolume = hfToAmVol(event.valueInt2);
                                 Log.d(TAG, "AM volume set to " + mCommandedSpeakerVolume);
-                                sAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+                                mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
                                         +mCommandedSpeakerVolume, AudioManager.FLAG_SHOW_UI);
                             } else if (event.valueInt
                                     == HeadsetClientHalConstants.VOLUME_TYPE_MIC) {
-                                sAudioManager.setMicrophoneMute(event.valueInt2 == 0);
+                                mAudioManager.setMicrophoneMute(event.valueInt2 == 0);
                             }
                             break;
                         case StackEvent.EVENT_TYPE_CMD_RESULT:
@@ -1495,7 +1523,7 @@
 
                     // We need to set the volume after switching into HFP mode as some Audio HALs
                     // reset the volume to a known-default on mode switch.
-                    final int amVol = sAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+                    final int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
                     final int hfVol = amToHfVol(amVol);
 
                     if (DBG) {
@@ -1505,18 +1533,19 @@
                         if (DBG) {
                             Log.d(TAG, "Setting sampling rate as 16000");
                         }
-                        sAudioManager.setParameters("hfp_set_sampling_rate=16000");
+                        mAudioManager.setParameters("hfp_set_sampling_rate=16000");
                     } else {
                         if (DBG) {
                             Log.d(TAG, "Setting sampling rate as 8000");
                         }
-                        sAudioManager.setParameters("hfp_set_sampling_rate=8000");
+                        mAudioManager.setParameters("hfp_set_sampling_rate=8000");
                     }
                     if (DBG) {
                         Log.d(TAG, "hf_volume " + hfVol);
                     }
                     routeHfpAudio(true);
-                    sAudioManager.setParameters("hfp_volume=" + hfVol);
+                    mAudioFocusRequest = requestAudioFocus();
+                    mAudioManager.setParameters("hfp_volume=" + hfVol);
                     transitionTo(mAudioOn);
                     break;
 
@@ -1590,6 +1619,7 @@
                      */
                     if (NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) {
                         routeHfpAudio(false);
+                        returnAudioFocusIfNecessary();
                     }
                     break;
 
@@ -1661,6 +1691,7 @@
                     // is not much we can do here since dropping the call without user consent
                     // even if the audio connection snapped may not be a good idea.
                     routeHfpAudio(false);
+                    returnAudioFocusIfNecessary();
                     transitionTo(mConnected);
                     break;