Add fragment for BluetoothConnection management

Currently handles only HFP based connection properties

Change-Id: Id75f3e4d6805318a5a025dcfd3eee5b2045dfd81
diff --git a/tests/EmbeddedKitchenSinkApp/Android.mk b/tests/EmbeddedKitchenSinkApp/Android.mk
index 49d578a..ca32045 100644
--- a/tests/EmbeddedKitchenSinkApp/Android.mk
+++ b/tests/EmbeddedKitchenSinkApp/Android.mk
@@ -30,6 +30,8 @@
 
 LOCAL_PRIVILEGED_MODULE := true
 
+LOCAL_CERTIFICATE := platform
+
 LOCAL_PROGUARD_ENABLED := disabled
 
 LOCAL_DEX_PREOPT := false
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index f83a923..95eb5f8 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-        package="com.google.android.car.kitchensink" >
+        package="com.google.android.car.kitchensink"
+        android:sharedUserId="android.uid.system">
     <uses-sdk
         android:minSdkVersion="22"
         android:targetSdkVersion='23'/>
@@ -38,6 +39,7 @@
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.READ_SMS"/>
     <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     <application android:label="@string/app_title"
         android:icon="@drawable/ic_launcher">
 
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml b/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
new file mode 100644
index 0000000..3ef75e2
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1" >
+        <TextView
+            android:id="@+id/bluetooth_device"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"/>
+        <Button
+            android:id="@+id/bluetooth_pick_device"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/bluetooth_pick_device"/>
+    </LinearLayout>
+    <Button
+        android:id="@+id/bluetooth_sco_connect"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/bluetooth_sco_connect"/>
+    <Button
+        android:id="@+id/bluetooth_sco_disconnect"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/bluetooth_sco_disconnect"/>
+    <Button
+        android:id="@+id/bluetooth_quiet_mode_enable"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/bluetooth_quiet_mode_enable"/>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index a6a9e69..a3f45a0 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -195,6 +195,12 @@
     <string name="sms_reply_busy" translatable="false">Reply "I'm Busy"</string>
     <string name="sms_reply_sent" translatable="false">Reply Sent</string>
     <string name="sms_reply_delivered" translatable="false">Reply Delivered</string>
+    <string name="bluetooth_profile_connect">Profile connect</string>
+    <string name="bluetooth_profile_disconnect">Profile disconnect</string>
+    <string name="bluetooth_sco_connect">SCO connect</string>
+    <string name="bluetooth_sco_disconnect">SCO disconnect</string>
+    <string name="bluetooth_pick_device">Pick Device</string>
+    <string name="bluetooth_quiet_mode_enable">Quiet Mode</string>
 
     <!--Car Service Settings-->
     <string name="garage_title">Garage Mode</string>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index f31cd26..527b09b 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -50,7 +50,8 @@
 import com.google.android.car.kitchensink.setting.CarServiceSettingsActivity;
 import com.google.android.car.kitchensink.touch.TouchTestFragment;
 import com.google.android.car.kitchensink.volume.VolumeTestFragment;
-//import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
+import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
+import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -73,6 +74,7 @@
     private static final String MENU_CUBES_TEST = "cubes test";
     private static final String MENU_CAR_SETTINGS = "car service settings";
     private static final String MENU_ORIENTATION = "orientation test";
+    private static final String MENU_BLUETOOTH_HEADSET = "bluetooth headset";
     private static final String MENU_MAP_MESSAGING = "bluetooth messaging test";
 
     private Car mCarApi;
@@ -94,7 +96,8 @@
     private TouchTestFragment mTouchTestFragment;
     private CubesTestFragment mCubesTestFragment;
     private OrientationTestFragment mOrientationFragment;
-    //private MapMceTestFragment mMapMceTestFragment;
+    private MapMceTestFragment mMapMceTestFragment;
+    private BluetoothHeadsetFragment mBluetoothHeadsetFragement;
 
     private final CarSensorManager.OnSensorChangedListener mListener =
             new CarSensorManager.OnSensorChangedListener() {
@@ -224,7 +227,7 @@
                         MENU_AUDIO, MENU_RADIO, MENU_CAMERA, MENU_HVAC, MENU_JOB, MENU_KEYBOARD,
                         MENU_CLUSTER, MENU_INPUT_TEST, MENU_SENSORS, MENU_VOLUME_TEST,
                         MENU_TOUCH_TEST, MENU_CUBES_TEST, MENU_CAR_SETTINGS, MENU_ORIENTATION,
-                        MENU_MAP_MESSAGING, MENU_QUIT
+                        MENU_BLUETOOTH_HEADSET, MENU_MAP_MESSAGING, MENU_QUIT
                 };
                 for (String menu : allMenus) {
                     items.add(new CarMenu.Builder(menu).setText(menu).build());
@@ -312,12 +315,17 @@
                     mOrientationFragment = new OrientationTestFragment();
                 }
                 showFragment(mOrientationFragment);
-            //} else if (id.equals(MENU_MAP_MESSAGING)) {
-            //    if (mMapMceTestFragment == null) {
-            //        mMapMceTestFragment = new MapMceTestFragment();
-            //    }
-            //    showFragment(mMapMceTestFragment);
-            } else if (id.equals(MENU_QUIT)) {
+            } else if (id.equals(MENU_BLUETOOTH_HEADSET)) {
+                if (mBluetoothHeadsetFragement == null) {
+                    mBluetoothHeadsetFragement = new BluetoothHeadsetFragment();
+                }
+                showFragment(mBluetoothHeadsetFragement);
+            } else if (id.equals(MENU_MAP_MESSAGING)) {
+                if (mMapMceTestFragment == null) {
+                    mMapMceTestFragment = new MapMceTestFragment();
+                }
+                showFragment(mMapMceTestFragment);
+            }else if (id.equals(MENU_QUIT)) {
                 finish();
             }
         }
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
new file mode 100644
index 0000000..4965113
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.bluetooth;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDevicePicker;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.List;
+
+public class BluetoothHeadsetFragment extends Fragment {
+    private static final String TAG = "CAR.BLUETOOTH.KS";
+    BluetoothAdapter mBluetoothAdapter;
+    BluetoothDevice mPickedDevice;
+
+    TextView mPickedDeviceText;
+    Button mDevicePicker;
+    Button mScoConnect;
+    Button mScoDisconnect;
+    Button mEnableQuietMode;
+
+    BluetoothHeadsetClient mHfpClientProfile;
+
+    // Intent for picking a Bluetooth device
+    public static final String DEVICE_PICKER_ACTION = "android.bluetooth.devicepicker.action.LAUNCH";
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View v = inflater.inflate(R.layout.bluetooth_headset, container, false);
+
+        mPickedDeviceText = (TextView) v.findViewById(R.id.bluetooth_device);
+        mDevicePicker = (Button) v.findViewById(R.id.bluetooth_pick_device);
+        mScoConnect = (Button) v.findViewById(R.id.bluetooth_sco_connect);
+        mScoDisconnect= (Button) v.findViewById(R.id.bluetooth_sco_disconnect);
+        mEnableQuietMode = (Button) v.findViewById(R.id.bluetooth_quiet_mode_enable);
+
+        // Pick a bluetooth device
+        mDevicePicker.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                launchDevicePicker();
+            }
+        });
+
+        // Connect SCO
+        mScoConnect.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                connectSco();
+            }
+        });
+
+        // Disconnect SCO
+        mScoDisconnect.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                disconnectSco();
+            }
+        });
+
+        // Enable quiet mode
+        mEnableQuietMode.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mBluetoothAdapter.enableNoAutoConnect();
+            }
+        });
+        return v;
+    }
+
+    void launchDevicePicker() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
+        getContext().registerReceiver(mPickerReceiver, filter);
+
+        Intent intent = new Intent(DEVICE_PICKER_ACTION);
+        intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        getContext().startActivity(intent);
+    }
+
+    void connectSco() {
+        if (mPickedDevice == null) {
+            Log.w(TAG, "Device null when trying to connect sco!");
+            return;
+        }
+
+        // Check if we have the proxy and connect the device.
+        if (mHfpClientProfile == null) {
+            Log.w(TAG, "HFP Profile proxy not available, cannot connect sco to " + mPickedDevice);
+            return;
+        }
+        mHfpClientProfile.connectAudio(mPickedDevice);
+    }
+
+    void disconnectSco() {
+        if (mPickedDevice == null) {
+            Log.w(TAG, "Device null when trying to disconnect sco!");
+            return;
+        }
+
+        if (mHfpClientProfile == null) {
+            Log.w(TAG, "HFP Profile proxy not available, cannot disconnect sco to " +
+                mPickedDevice);
+            return;
+        }
+        mHfpClientProfile.disconnectAudio(mPickedDevice);
+    }
+
+    private final BroadcastReceiver mPickerReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            Log.v(TAG, "mPickerReceiver got " + action);
+
+            if (BluetoothDevicePicker.ACTION_DEVICE_SELECTED.equals(action)) {
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                mPickedDevice = device;
+                String text = device.getName() == null ?
+                    device.getAddress() : device.getName() + " " + device.getAddress();
+                mPickedDeviceText.setText(text);
+
+                // The receiver can now be disabled.
+                getContext().unregisterReceiver(mPickerReceiver);
+            }
+        }
+    };
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mBluetoothAdapter.getProfileProxy(
+            getContext(), new ProfileServiceListener(), BluetoothProfile.HEADSET_CLIENT);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+    }
+
+    class ProfileServiceListener implements BluetoothProfile.ServiceListener {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            Log.d(TAG, "Proxy connected for profile: " + profile);
+            switch (profile) {
+                case BluetoothProfile.HEADSET_CLIENT:
+                    mHfpClientProfile = (BluetoothHeadsetClient) proxy;
+                    break;
+                default:
+                    Log.w(TAG, "onServiceConnected not supported profile: " + profile);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            Log.d(TAG, "Proxy disconnected for profile: " + profile);
+            switch (profile) {
+                case BluetoothProfile.HEADSET_CLIENT:
+                    mHfpClientProfile = null;
+                    break;
+                default:
+                    Log.w(TAG, "onServiceDisconnected not supported profile: " + profile);
+            }
+        }
+    }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapMceTestFragment.java.disabled b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapMceTestFragment.java
similarity index 100%
rename from tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapMceTestFragment.java.disabled
rename to tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapMceTestFragment.java
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java.disabled b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java
similarity index 100%
rename from tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java.disabled
rename to tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java