Add sl4a support for automatically accepting Bluetooth MAP connection.

Change sl4a to run with system uid.
Add handling logic in paring helper.
Other tweaks.

Change-Id: Ia701648f041a92459ad9d8aa7e5c7a2523925f26
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothAvrcpFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothAvrcpFacade.java
index 1fc26aa..334ff51 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothAvrcpFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothAvrcpFacade.java
@@ -13,6 +13,7 @@
 import com.googlecode.android_scripting.facade.FacadeManager;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
 import com.googlecode.android_scripting.rpc.Rpc;
+import com.googlecode.android_scripting.rpc.RpcParameter;
 
 public class BluetoothAvrcpFacade extends RpcReceiver {
   static final ParcelUuid[] AVRCP_UUIDS = {
@@ -56,6 +57,18 @@
     return sAvrcpProfile.getConnectedDevices();
   }
 
+  @Rpc(description = "Send AVRPC passthrough command.")
+  public void bluetoothAvrcpSendPassThroughCmd(
+          @RpcParameter(name = "deviceID", description = "Name or MAC address of a bluetooth device.")
+          String deviceID,
+          @RpcParameter(name = "keyCode")
+          Integer keyCode,
+          @RpcParameter(name = "keyState")
+          Integer keyState) throws Exception {
+      BluetoothDevice mDevice = BluetoothFacade.getDevice(sAvrcpProfile.getConnectedDevices(), deviceID);
+      sAvrcpProfile.sendPassThroughCmd(mDevice, keyCode, keyState);
+  }
+
   @Override
   public void shutdown() {
   }
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothConnectionFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothConnectionFacade.java
index b19ab0d..1deef1e 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothConnectionFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothConnectionFacade.java
@@ -37,7 +37,6 @@
     private final EventFacade mEventFacade;
 
     private final IntentFilter mDiscoverConnectFilter;
-    private final IntentFilter mConnectionStateChangeFilter;
     private final IntentFilter mPairingFilter;
     private final IntentFilter mBondFilter;
     private final IntentFilter mA2dpStateChangeFilter;
@@ -56,7 +55,7 @@
         mService = manager.getService();
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         mPairingHelper = new BluetoothPairingHelper();
-        // Use a synchronized map to avoid running problems
+        // Use a synchronized map to avoid racing problems
         listeningDevices = Collections.synchronizedMap(new HashMap<String, BroadcastReceiver>());
 
         mEventFacade = manager.getReceiver(EventFacade.class);
@@ -68,15 +67,13 @@
         mDiscoverConnectFilter.addAction(BluetoothDevice.ACTION_UUID);
         mDiscoverConnectFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
 
-        mConnectionStateChangeFilter = new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-        mConnectionStateChangeFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-
         mPairingFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
+        mPairingFilter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
+        mPairingFilter.setPriority(999);
 
         mBondFilter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mBondFilter.addAction(BluetoothDevice.ACTION_FOUND);
         mBondFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
-        mBondFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1);
 
         mA2dpStateChangeFilter = new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         mHidStateChangeFilter = new IntentFilter(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
@@ -86,6 +83,8 @@
         mGoodNews.putBoolean("Status", true);
         mBadNews = new Bundle();
         mBadNews.putBoolean("Status", false);
+
+        mService.registerReceiver(mPairingHelper, mPairingFilter);
     }
 
     /**
@@ -218,7 +217,9 @@
             if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
                 int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
                 if (state == BluetoothA2dp.STATE_CONNECTED) {
-                    mEventFacade.postEvent("A2dpConnect" + mDeviceID, mGoodNews);
+                	Bundle a2dpGoodNews = (Bundle) mGoodNews.clone();
+                	a2dpGoodNews.putString("Type", "a2dp");
+                    mEventFacade.postEvent("A2dpConnect" + mDeviceID, a2dpGoodNews);
                     mService.unregisterReceiver(listeningDevices.remove("A2dpConnecting"
                             + mDeviceID));
                 } else if (state == BluetoothA2dp.STATE_CONNECTING) {
@@ -242,9 +243,12 @@
     }
 
     private void connectProfile(BluetoothDevice device, String deviceID) {
+        mService.registerReceiver(mPairingHelper, mPairingFilter);
         ParcelUuid[] deviceUuids = device.getUuids();
         Log.d("Device uuid is " + deviceUuids);
-        mService.registerReceiver(mPairingHelper, mPairingFilter);
+        if (deviceUuids == null) {
+        	mEventFacade.postEvent("Connect", mBadNews);
+        }
         if (BluetoothUuid.containsAnyUuid(BluetoothA2dpFacade.SINK_UUIDS, deviceUuids)) {
             Log.d("Connecting to " + device.getAliasName());
             boolean status = mA2dpProfile.a2dpConnect(device);
@@ -255,10 +259,12 @@
                 listeningDevices.put("A2dpConnecting" + deviceID, receiver);
             } else {
                 Log.d("Failed starting A2dp connection.");
-                mEventFacade.postEvent("A2dpConnect", mBadNews);
+                Bundle a2dpBadNews = (Bundle) mBadNews.clone();
+                a2dpBadNews.putString("Type", "a2dp");
+                mEventFacade.postEvent("Connect", a2dpBadNews);
             }
         }
-        if (BluetoothUuid.containsAnyUuid(BluetoothHidFacade.HID_UUID, deviceUuids)) {
+        if (BluetoothUuid.containsAnyUuid(BluetoothHidFacade.UUIDS, deviceUuids)) {
             boolean status = mHidProfile.hidConnect(device);
             if (status) {
                 Log.d("Connecting Hid...");
@@ -270,7 +276,7 @@
                 mEventFacade.postEvent("HidConnect", mBadNews);
             }
         }
-        if (BluetoothUuid.containsAnyUuid(BluetoothHspFacade.HSP_UUIDS, deviceUuids)) {
+        if (BluetoothUuid.containsAnyUuid(BluetoothHspFacade.UUIDS, deviceUuids)) {
             boolean status = mHspProfile.hspConnect(device);
             if (status) {
                 Log.d("Connecting Hsp...");
@@ -296,7 +302,7 @@
         return results;
     }
 
-    @Rpc(description = "Return ture if a bluetooth device is connected.")
+    @Rpc(description = "Return true if a bluetooth device is connected.")
     public Boolean bluetoothIsDeviceConnected(String deviceID) {
         for (BluetoothDevice bd : mBluetoothAdapter.getBondedDevices()) {
             if (BluetoothFacade.deviceMatch(bd, deviceID)) {
@@ -367,10 +373,21 @@
             String deviceID) throws Exception {
         BluetoothDevice mDevice = BluetoothFacade.getDevice(mBluetoothAdapter.getBondedDevices(),
                 deviceID);
+        mDevice.setTrust(true);
         connectProfile(mDevice, deviceID);
     }
+//
+//    @Rpc(description = "Register pairing helper.")
+//    public void bluetoothAutoAccept() {
+//        mService.registerReceiver(mPairingHelper, mPairingFilter);
+//    }
 
     @Override
     public void shutdown() {
+        for(BroadcastReceiver receiver : listeningDevices.values()) {
+            mService.unregisterReceiver(receiver);
+        }
+        listeningDevices.clear();
+        mService.unregisterReceiver(mPairingHelper);
     }
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHidFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHidFacade.java
index e456933..37875d6 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHidFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHidFacade.java
@@ -18,7 +18,7 @@
 import com.googlecode.android_scripting.rpc.RpcParameter;
 
 public class BluetoothHidFacade extends RpcReceiver {
-  public final static ParcelUuid[] HID_UUID = { BluetoothUuid.Hid };
+  public final static ParcelUuid[] UUIDS = { BluetoothUuid.Hid };
 
   private final Service mService;
   private final BluetoothAdapter mBluetoothAdapter;
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
index e9e10d2..8f39491 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
@@ -17,7 +17,7 @@
 import com.googlecode.android_scripting.rpc.RpcParameter;
 
 public class BluetoothHspFacade extends RpcReceiver {
-  static final ParcelUuid[] HSP_UUIDS = {
+  static final ParcelUuid[] UUIDS = {
     BluetoothUuid.HSP, BluetoothUuid.Handsfree
   };
 
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothPairingHelper.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothPairingHelper.java
index 12a25d6..1e98a15 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothPairingHelper.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothPairingHelper.java
@@ -10,24 +10,49 @@
 public class BluetoothPairingHelper extends BroadcastReceiver {
   public BluetoothPairingHelper() {
     super();
+    Log.d("Pairing helper created.");
   }
   /**
-   * Blindly confirm passkey
+   * Blindly confirm bluetooth connection/bonding requests.
    */
   @Override
   public void onReceive(Context c, Intent intent) {
     String action = intent.getAction();
-    int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
-    Log.d("Bluetooth pairing intent received: " + action + " with type " + type);
-    BluetoothDevice mBtDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+    Log.d("Bluetooth pairing intent received: " + action);
+    BluetoothDevice mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     if(action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
-      Log.d("Processing Action Paring Request.");
+      int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
+      Log.d("Processing Action Paring Request with type " + type);
       if(type == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION ||
          type == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
-        mBtDevice.setPairingConfirmation(true);
+        mDevice.setPairingConfirmation(true);
         Log.d("Connection confirmed");
         abortBroadcast(); // Abort the broadcast so Settings app doesn't get it.
       }
     }
+    else if(action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) {
+      int type = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, BluetoothDevice.ERROR);
+      Log.d("Processing Action Connection Access Request type " + type);
+      if(type == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS ||
+         type == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS ||
+         type == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION) {
+    	  Intent newIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
+    	  String mReturnPackage = intent.getStringExtra(BluetoothDevice.EXTRA_PACKAGE_NAME);
+          String mReturnClass = intent.getStringExtra(BluetoothDevice.EXTRA_CLASS_NAME);
+          int mRequestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
+                  BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS);
+          if (mReturnPackage != null && mReturnClass != null) {
+              newIntent.setClassName(mReturnPackage, mReturnClass);
+          }
+    	  newIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
+    			             BluetoothDevice.CONNECTION_ACCESS_YES);
+          newIntent.putExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, true);
+    	  newIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+    	  newIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType);
+          Log.d("Sending connection access acceptance intent.");
+          abortBroadcast();
+          c.sendBroadcast(newIntent, android.Manifest.permission.BLUETOOTH_ADMIN);
+      }
+    }
   }
 }
\ No newline at end of file
diff --git a/ScriptingLayerForAndroid/AndroidManifest.xml b/ScriptingLayerForAndroid/AndroidManifest.xml
index eed2992..a69bf92 100644
--- a/ScriptingLayerForAndroid/AndroidManifest.xml
+++ b/ScriptingLayerForAndroid/AndroidManifest.xml
@@ -1,5 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.googlecode.android_scripting" android:installLocation="auto" android:versionCode="603" android:versionName="6x03">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.googlecode.android_scripting" android:installLocation="auto" android:versionCode="603" android:versionName="6x03"
+    android:sharedUserId="android.uid.system">
+    <permission android:name="com.android.permission.WHITELIST_BLUETOOTH_DEVICE"
+        android:protectionLevel="signature" />
+    <permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE"
+        android:protectionLevel="signature" />
     <uses-permission android:name="android.permission.RECEIVE_SMS" />
     <uses-permission android:name="net.dinglisch.android.tasker.PERMISSION_RUN_TASKS" />
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
@@ -26,16 +31,17 @@
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
+    <uses-permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE" />
+    <uses-permission android:name="com.android.permission.WHITELIST_BLUETOOTH_DEVICE" />
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
-    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
     <uses-permission android:name="android.permission.NFC" />
     <uses-permission android:name="android.permission.HARDWARE_TEST" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
@@ -80,6 +86,7 @@
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
+    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
     <uses-sdk android:targetSdkVersion="10" android:minSdkVersion="3" />
     <application android:icon="@drawable/sl4a_logo_48" android:label="@string/application_title" android:name=".Sl4aApplication">
         <activity android:name=".activity.ScriptManager" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="adjustResize" android:launchMode="singleTop">
@@ -165,9 +172,12 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <receiver android:name=".facade.bluetooth.BluetoothPairingHelper">
-            <intent-filter android:priority="100">
+        <receiver android:name=".facade.bluetooth.BluetoothPairingHelper"
+                  android:permission="android.permission.BLUETOOTH_ADMIN">
+            <intent-filter android:priority="999">
                 <action android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
+                <action android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
+                <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </receiver>
     </application>