Allow HFP HF implementations to specify a client controller.

The client controller service is used by the HeadsetClientService to
determine conditions such as whether audio routing to device is permitted.

Bug: 25332357
Change-Id: I8971960f959084ed3969db7c8cdef11673a58994
diff --git a/res/values/config.xml b/res/values/config.xml
index 79fe9ce..877352b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -41,4 +41,7 @@
     <integer name="gatt_high_priority_max_interval">12</integer>
     <integer name="gatt_low_power_min_interval">80</integer>
     <integer name="gatt_low_power_max_interval">100</integer>
+
+    <!-- Specifies the component name of the service to be bound to for controlling HF -->
+    <string name="headset_client_controller_service"></string>
 </resources>
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index d7eb12d..eeb7dd1 100644
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -22,20 +22,27 @@
 import android.bluetooth.BluetoothHeadsetClient;
 import android.bluetooth.BluetoothHeadsetClientCall;
 import android.bluetooth.IBluetoothHeadsetClient;
+import android.bluetooth.IBluetoothHeadsetClientController;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.media.AudioManager;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Message;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.Utils;
 import java.util.ArrayList;
 import java.util.List;
 
+import com.android.bluetooth.R;
+
 /**
  * Provides Bluetooth Headset Client (HF Role) profile, as a service in the
  * Bluetooth application.
@@ -47,8 +54,22 @@
     private static final String TAG = "HeadsetClientService";
 
     private HeadsetClientStateMachine mStateMachine;
+
     private static HeadsetClientService sHeadsetClientService;
 
+    private ServiceConnection mControllerConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            mStateMachine.setClientController(
+                    IBluetoothHeadsetClientController.Stub.asInterface(service));
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mStateMachine.setClientController(null);
+        }
+    };
+
     @Override
     protected String getName() {
         return TAG;
@@ -69,7 +90,18 @@
         } catch (Exception e) {
             Log.w(TAG, "Unable to register broadcat receiver", e);
         }
+
+        final String controllerComponent =
+                getResources().getString(R.string.headset_client_controller_service);
+
+        if (!TextUtils.isEmpty(controllerComponent)) {
+            Intent intent = new Intent();
+            intent.setComponent(ComponentName.unflattenFromString(controllerComponent));
+            bindService(intent, mControllerConnection, Context.BIND_AUTO_CREATE);
+        }
+
         setHeadsetClientService(this);
+
         return true;
     }
 
@@ -551,6 +583,7 @@
         if (mStateMachine.isAudioOn()) {
             return false;
         }
+
         mStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
         return true;
     }
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
index f9cadab..ab0af99 100644
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@@ -40,9 +40,11 @@
 import android.bluetooth.BluetoothHeadsetClientCall;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
+import android.bluetooth.IBluetoothHeadsetClientController;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.ParcelUuid;
+import android.os.RemoteException;
 import android.util.Log;
 import android.util.Pair;
 import android.content.Context;
@@ -140,6 +142,7 @@
     // indicator
     private Pair<Integer, Object> mPendingAction;
 
+    private IBluetoothHeadsetClientController mClientController;
     private final AudioManager mAudioManager;
     private int mAudioState;
     private boolean mAudioWbs;
@@ -157,6 +160,10 @@
         classInitNative();
     }
 
+    public void setClientController(IBluetoothHeadsetClientController clientController) {
+        mClientController = clientController;
+    }
+
     public void dump(StringBuilder sb) {
         ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
         ProfileService.println(sb, "mAudioOn: " + mAudioOn);
@@ -2016,6 +2023,15 @@
                     mAudioWbs = true;
                     // fall through
                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED:
+                    try {
+                        if (mClientController != null && !mClientController.allowAudioConnect()) {
+                            sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
+                            break;
+                        }
+                    } catch (RemoteException remoteException) {
+                        Log.e(TAG, "ERROR: couldn't communicate with client controller");
+                    }
+
                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED;
                     // request audio focus for call
                     int newAudioMode = AudioManager.MODE_IN_CALL;