diff --git a/android/Bluetooth4Facade/AndroidManifest.xml b/android/Bluetooth4Facade/AndroidManifest.xml
index 6db1de0cd..0d5006bdf 100644
--- a/android/Bluetooth4Facade/AndroidManifest.xml
+++ b/android/Bluetooth4Facade/AndroidManifest.xml
@@ -1,6 +1,6 @@
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/bluetooth/BluetoothNonpublicApi.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/bluetooth/BluetoothNonpublicApi.java
index 1d7742723..1ab57335f 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/bluetooth/BluetoothNonpublicApi.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/bluetooth/BluetoothNonpublicApi.java
@@ -1,5 +1,6 @@
package com.googlecode.android_scripting.bluetooth;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -13,11 +14,18 @@
*/
public class BluetoothNonpublicApi {
- // BUG: negative number = under searching...
+ // adapter
+ public static final int STATE_UNKNOWN = -1; // temporary
+ public static final int STATE_BLE_ON = 15;
+ public static final String ACTION_BLE_STATE_CHANGED =
+ "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
+
+ // profile
+ // BUG: numbers around 65536 = under searching...
public static final int PRIORITY_AUTO_CONNECT = 1000;
public static final int PRIORITY_ON = 100;
public static final int PRIORITY_OFF = 0;
- public static final int PRIORITY_UNDEFINED = -65536;
+ public static final int PRIORITY_UNDEFINED = -1;
public static final int MAP = 9;
public static final int PAN = 5;
@@ -29,6 +37,21 @@ public class BluetoothNonpublicApi {
public static final int INPUT_DEVICE = 4;
public static final int AVRCP_CONTROLLER = 12;
+ // Device
+ public static final String ACTION_CONNECTION_ACCESS_REQUEST =
+ "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST";
+ public static final String ACTION_CONNECTION_ACCESS_REPLY =
+ "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY";
+ public static final int PAIRING_VARIANT_CONSENT = 3;
+
+ // MapClient
+ public static final String ACTION_MESSAGE_RECEIVED =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
+ public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
+ public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
+
public static boolean connectProfile(BluetoothProfile prf, BluetoothDevice sink) {
Log.e("connect function won't work with no-system app.");
if (prf == null) {return false;}
@@ -122,4 +145,59 @@ public static boolean setPriorityProfile(BluetoothProfile prf,
}
return false;
}
+
+ public static Integer getLeState(BluetoothAdapter adp
+ ) {
+ Log.e("getLeState function won't work with no-system app.");
+ if (adp == null) {return STATE_UNKNOWN;}
+
+ try {
+ Method method = adp.getClass().getMethod("getLeState");
+ if(method != null) {
+ return (Integer)method.invoke(adp);
+ }
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return STATE_UNKNOWN;
+ }
+
+ public static void setScanMode(BluetoothAdapter adp,
+ Integer mode) {
+
+ setScanMode(true, adp, mode, null);
+ }
+
+ public static void setScanMode(BluetoothAdapter adp,
+ Integer mode,
+ Integer duration) {
+ setScanMode(false, adp, mode, duration);
+ }
+
+ public static void setScanMode(boolean f2opt,
+ BluetoothAdapter adp,
+ Integer mode,
+ Integer duration) {
+ Log.e("setScanMode function won't work with no-system app.");
+ if (adp == null) {return;}
+
+ try {
+ Method method = adp.getClass().getMethod("setScanMode");
+ if(method != null) {
+ if (f2opt) {method.invoke(adp, mode);}
+ else {method.invoke(adp, mode, duration);}
+ return;
+ }
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/bluetooth/BluetoothPairingHelper.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/bluetooth/BluetoothPairingHelper.java
index 67de98947..142c67791 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/bluetooth/BluetoothPairingHelper.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/bluetooth/BluetoothPairingHelper.java
@@ -14,7 +14,7 @@
* the License.
*/
-package com.googlecode.android_scripting.facade.bluetooth;
+package com.googlecode.android_scripting.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
@@ -46,8 +46,10 @@ public void onReceive(Context c, Intent intent) {
Log.d("Bluetooth pairing intent received: " + action);
BluetoothDevice mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
+ /* TODO: try to implement.
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+ */
int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
Log.d("Processing Action Paring Request with type " + type);
int pin = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY,0);
@@ -57,7 +59,7 @@ public void onReceive(Context c, Intent intent) {
result.putString("DeviceAddress", deviceAddress);
mEventFacade.postEvent("BluetoothActionPairingRequest", result.clone());
result.clear();
- if(type == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
+ if(type == BluetoothNonpublicApi.PAIRING_VARIANT_CONSENT) {
mDevice.setPairingConfirmation(true);
Log.d("Connection auto-confirmed by consent");
abortBroadcast(); // Abort the broadcast so Settings app doesn't get it.
@@ -71,7 +73,8 @@ public void onReceive(Context c, Intent intent) {
try {
userConfirmEvent = mEventFacade.eventWaitFor(
"BluetoothActionPairingRequestUserConfirm",
- true, DEFAULT_TIMEOUT_MS);
+ DEFAULT_TIMEOUT_MS);
+ // true, DEFAULT_TIMEOUT_MS);
} catch (InterruptedException e) {
Log.d("Connection interrupted");
userConfirmEvent = null;
@@ -93,7 +96,8 @@ public void onReceive(Context c, Intent intent) {
abortBroadcast(); // Abort the broadcast so Settings app doesn't get it.
}
}
- else if(action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) {
+ else if(action.equals(BluetoothNonpublicApi.ACTION_CONNECTION_ACCESS_REQUEST)) {
+ /* TODO: try to implement.
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 ||
@@ -115,7 +119,8 @@ else if(action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) {
Log.d("Sending connection access acceptance intent.");
abortBroadcast();
c.sendBroadcast(newIntent, android.Manifest.permission.BLUETOOTH_ADMIN);
- }
+ }
+ */
}
}
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/Bluetooth4Facade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/Bluetooth4Facade.java
index 949580515..73b669ffb 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/Bluetooth4Facade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/Bluetooth4Facade.java
@@ -32,6 +32,7 @@
import com.googlecode.android_scripting.Constants;
import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.MainThread;
+import com.googlecode.android_scripting.bluetooth.BluetoothNonpublicApi;
import com.googlecode.android_scripting.facade.EventFacade;
import com.googlecode.android_scripting.facade.FacadeManager;
import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
@@ -162,9 +163,9 @@ class BleStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(BluetoothAdapter.ACTION_BLE_STATE_CHANGED)) {
- int state = mBluetoothAdapter.getLeState();
- if (state == BluetoothAdapter.STATE_BLE_ON) {
+ if (action.equals(BluetoothNonpublicApi.ACTION_BLE_STATE_CHANGED)) {
+ int state = BluetoothNonpublicApi.getLeState(mBluetoothAdapter);
+ if (state == BluetoothNonpublicApi.STATE_BLE_ON) {
mEventFacade.postEvent("BleStateChangedOn", new Bundle());
mService.unregisterReceiver(mBleStateReceiver);
} else if (state == BluetoothAdapter.STATE_OFF) {
@@ -177,7 +178,8 @@ public void onReceive(Context context, Intent intent) {
public static boolean deviceMatch(BluetoothDevice device, String deviceID) {
- return deviceID.equals(device.getAliasName()) || deviceID.equals(device.getAddress());
+ // if (deviceID.equals(device.getAliasName())) {return true;}
+ return deviceID.equals(device.getAddress());
}
public static BluetoothDevice getDevice(ConcurrentHashMap devices, String device)
@@ -214,8 +216,8 @@ public static boolean deviceExists(Collection devices, String d
@Rpc(description = "Requests that the device be made connectable.")
public void bluetoothMakeConnectable() {
- mBluetoothAdapter
- .setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ BluetoothNonpublicApi.setScanMode(mBluetoothAdapter,
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE);
}
@Rpc(description = "Returns active Bluetooth connections.")
@@ -327,8 +329,8 @@ public void bluetoothMakeDiscoverable(
@RpcDefault("300")
Integer duration) {
Log.d("Making discoverable for " + duration + " seconds.\n");
- mBluetoothAdapter
- .setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, duration);
+ BluetoothNonpublicApi.setScanMode(mBluetoothAdapter,
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, duration);
if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration);
@@ -393,7 +395,7 @@ public String bluetoothReadLine(
@Rpc(description = "Requests that the device be not discoverable.")
public void bluetoothMakeUndiscoverable() {
Log.d("Making undiscoverable\n");
- mBluetoothAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_NONE);
+ BluetoothNonpublicApi.setScanMode(mBluetoothAdapter, BluetoothAdapter.SCAN_MODE_NONE);
}
@Rpc(description = "Queries a remote device for it's name or null if it can't be resolved")
@@ -457,7 +459,9 @@ public Boolean checkBluetoothState() {
@Rpc(description = "Factory reset bluetooth settings.", returns = "True if successful.")
public boolean bluetoothFactoryReset() {
- return mBluetoothAdapter.factoryReset();
+ Log.e("factoryReset won't work in no-system app.");
+ return false;
+ // return mBluetoothAdapter.factoryReset();
}
@Rpc(description = "Toggle Bluetooth on and off.", returns = "True if Bluetooth is enabled.")
@@ -473,7 +477,6 @@ public Boolean toggleBluetoothState(
enabled = !checkBluetoothState();
}
if (enabled) {
- return mBluetoothAdapter.enable();
if (prompt) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
// TODO(damonkohler): Use the result to determine if this was successful. At any rate, keep
@@ -554,7 +557,10 @@ public boolean bluetoothConfigHciSnoopLog(
@RpcParameter(name = "value", description = "enable or disable log")
Boolean value
) {
- return mBluetoothAdapter.configHciSnoopLog(value);
+ Log.e("configHciSnoopLog won't work in no-system app.");
+ return false;
+ // TODO: try to implement.
+ // return mBluetoothAdapter.configHciSnoopLog(value);
}
@Rpc(description = "Get Bluetooth controller activity energy info.")
@@ -562,43 +568,53 @@ public String bluetoothGetControllerActivityEnergyInfo(
@RpcParameter(name = "value")
Integer value
) {
+ return "Disabled in user app SL4A";
+ /* TODO: try to implement.
BluetoothActivityEnergyInfo energyInfo = mBluetoothAdapter
.getControllerActivityEnergyInfo(value);
while (energyInfo == null) {
energyInfo = mBluetoothAdapter.getControllerActivityEnergyInfo(value);
}
return energyInfo.toString();
+ */
}
@Rpc(description = "Return true if hardware has entries" +
"available for matching beacons.")
public boolean bluetoothIsHardwareTrackingFiltersAvailable() {
- return mBluetoothAdapter.isHardwareTrackingFiltersAvailable();
+ Log.e("isHardwareTrackingFiltersAvailable won't in no-system app.");
+ return false;
+ // TODO: try to implement.
+ // return mBluetoothAdapter.isHardwareTrackingFiltersAvailable();
}
@Rpc(description = "Gets the current state of LE.")
public int bluetoothGetLeState() {
- return mBluetoothAdapter.getLeState();
+ return BluetoothNonpublicApi.getLeState(mBluetoothAdapter);
}
@Rpc(description = "Enables BLE functionalities.")
public boolean bluetoothEnableBLE() {
mService.registerReceiver(mBleStateReceiver,
- new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED));
- return mBluetoothAdapter.enableBLE();
+ new IntentFilter(BluetoothNonpublicApi.ACTION_BLE_STATE_CHANGED));
+ Log.e("enableBLE won't work in no-system app.");
+ return false;
+ // return mBluetoothAdapter.enableBLE();
}
@Rpc(description = "Disables BLE functionalities.")
public boolean bluetoothDisableBLE() {
mService.registerReceiver(mBleStateReceiver,
- new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED));
- return mBluetoothAdapter.disableBLE();
+ new IntentFilter(BluetoothNonpublicApi.ACTION_BLE_STATE_CHANGED));
+ Log.e("disableBLE won't work in no-system app.");
+ return false;
+ // return mBluetoothAdapter.disableBLE();
}
@Rpc(description = "Listen for a Bluetooth LE State Change.")
public boolean bluetoothListenForBleStateChange() {
mService.registerReceiver(mBleStateReceiver,
- new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED));
+ new IntentFilter(BluetoothNonpublicApi.ACTION_BLE_STATE_CHANGED));
return true;
}
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothA2dpSinkFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothA2dpSinkFacade.java
index 527e8413a..4c12fa0d0 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothA2dpSinkFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothA2dpSinkFacade.java
@@ -96,7 +96,7 @@ public Integer bluetoothA2dpSinkGetPriority(
@RpcParameter(name = "device", description = "Mac address of a BT device.")
String deviceStr)
throws Exception {
- if (sA2dpSinkProfile == null) return BluetoothProfile.PRIORITY_UNDEFINED;
+ if (sA2dpSinkProfile == null) return BluetoothNonpublicApi.PRIORITY_UNDEFINED;
BluetoothDevice device =
Bluetooth4Facade.getDevice(mBluetoothAdapter.getBondedDevices(), deviceStr);
return BluetoothNonpublicApi.getPriorityProfile(sA2dpSinkProfile, device);
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothConnectionFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothConnectionFacade.java
index 0174d5071..cd10d993c 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothConnectionFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothConnectionFacade.java
@@ -48,6 +48,7 @@
import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.bluetooth.BluetoothNonpublicApi;
import com.googlecode.android_scripting.bluetooth.BluetoothUuid;
+import com.googlecode.android_scripting.bluetooth.BluetoothPairingHelper;
import com.googlecode.android_scripting.facade.Bluetooth4Facade;
import com.googlecode.android_scripting.facade.EventFacade;
import com.googlecode.android_scripting.facade.FacadeManager;
@@ -68,6 +69,7 @@ public class BluetoothConnectionFacade extends RpcReceiver {
private final BluetoothAdapter mBluetoothAdapter;
private final BluetoothManager mBluetoothManager;
// private final BluetoothPairingHelper mPairingHelper;
+ private final BluetoothPairingHelper mPairingHelper;
private final Map listeningDevices;
private final EventFacade mEventFacade;
@@ -110,7 +112,7 @@ public BluetoothConnectionFacade(FacadeManager manager) {
listeningDevices = Collections.synchronizedMap(new HashMap());
mEventFacade = manager.getReceiver(EventFacade.class);
- // mPairingHelper = new BluetoothPairingHelper(mEventFacade);
+ mPairingHelper = new BluetoothPairingHelper(mEventFacade);
mA2dpProfile = manager.getReceiver(BluetoothA2dpFacade.class);
mA2dpSinkProfile = manager.getReceiver(BluetoothA2dpSinkFacade.class);
mHidProfile = manager.getReceiver(BluetoothHidFacade.class);
@@ -126,8 +128,8 @@ public BluetoothConnectionFacade(FacadeManager manager) {
mDiscoverConnectFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
mPairingFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
- mPairingFilter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
- mPairingFilter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
+ mPairingFilter.addAction(BluetoothNonpublicApi.ACTION_CONNECTION_ACCESS_REQUEST);
+ mPairingFilter.addAction(BluetoothNonpublicApi.ACTION_CONNECTION_ACCESS_REPLY);
mPairingFilter.setPriority(999);
mBondFilter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
@@ -135,7 +137,15 @@ public BluetoothConnectionFacade(FacadeManager manager) {
mBondFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
mA2dpStateChangeFilter = new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
- /* NOTE: can't s
+ mA2dpSinkStateChangeFilter = null;
+ mHidStateChangeFilter = null;
+ mHspStateChangeFilter = null;
+ mHfpClientStateChangeFilter = null;
+ mPbapClientStateChangeFilter = null;
+ mPanStateChangeFilter = null;
+ mMapClientStateChangeFilter = null;
+ mMapStateChangeFilter = null;
+ /* NOTE: can't build with normal SDK.
mA2dpSinkStateChangeFilter =
new IntentFilter(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
mHidStateChangeFilter =
@@ -505,7 +515,8 @@ public void bluetoothStartPairingHelper(
public List bluetoothGetConnectedDevices() {
ArrayList results = new ArrayList();
for (BluetoothDevice bd : mBluetoothAdapter.getBondedDevices()) {
- if (bd.isConnected()) {
+ if (true) {
+ // if (bd.isConnected()) { // TODO: try to implement.
results.add(bd);
}
}
@@ -521,7 +532,8 @@ public List bluetoothGetConnectedLeDevices(Integer profile) {
public Boolean bluetoothIsDeviceConnected(String deviceID) {
for (BluetoothDevice bd : mBluetoothAdapter.getBondedDevices()) {
if (Bluetooth4Facade.deviceMatch(bd, deviceID)) {
- return bd.isConnected();
+ return true;
+ // return bd.isConnected(); // TODO: try to implement.
}
}
return false;
@@ -600,7 +612,9 @@ public Boolean bluetoothUnbond(
String deviceID) throws Exception {
BluetoothDevice mDevice = Bluetooth4Facade.getDevice(mBluetoothAdapter.getBondedDevices(),
deviceID);
- return mDevice.removeBond();
+ Log.e("removeBond won't work in no-system app.");
+ return false;
+ // return mDevice.removeBond();
}
@Rpc(description = "Connect to a device that is already bonded.")
@@ -657,7 +671,8 @@ public void bluetoothChangeProfileAccessPermission(
deviceID);
switch (profileID) {
case BluetoothNonpublicApi.PBAP:
- mDevice.setPhonebookAccessPermission(access);
+ // mDevice.setPhonebookAccessPermission(access);
+ Log.e("setPhonebookAccessPermission won't work in no-system app.");
break;
default:
Log.w("Unsupported profile access change.");
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHfpClientFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHfpClientFacade.java
index c1aab8645..588d8234a 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHfpClientFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHfpClientFacade.java
@@ -90,8 +90,10 @@ public void bluetoothHfpClientSetPriority(
@RpcParameter(name = "priority", description = "Priority that needs to be set.")
Integer priority)
throws Exception {
- BluetoothNonpublicApi.setPriorityProfile(
- sHfpClientProfile, device, priority);
+ BluetoothDevice device =
+ Bluetooth4Facade.getDevice(mBluetoothAdapter.getBondedDevices(), deviceStr);
+ BluetoothNonpublicApi.setPriorityProfile(
+ sHfpClientProfile, device, priority);
}
@Rpc(description = "Get priority of the profile")
@@ -99,7 +101,7 @@ public Integer bluetoothHfpClientGetPriority(
@RpcParameter(name = "device", description = "Mac address of a BT device.")
String deviceStr)
throws Exception {
- if (sHfpClientProfile == null) return BluetoothProfile.PRIORITY_UNDEFINED;
+ if (sHfpClientProfile == null) return BluetoothNonpublicApi.PRIORITY_UNDEFINED;
BluetoothDevice device =
Bluetooth4Facade.getDevice(mBluetoothAdapter.getBondedDevices(), deviceStr);
return BluetoothNonpublicApi.getPriorityProfile(
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHidFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHidFacade.java
index e4ae901e4..ad77827ef 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHidFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHidFacade.java
@@ -89,7 +89,7 @@ public Boolean bluetoothHidConnect(
if (sHidProfile == null)
return false;
BluetoothDevice mDevice = Bluetooth4Facade.getDevice(Bluetooth4Facade.DiscoveredDevices, device);
- Log.d("Connecting to device " + mDevice.getAliasName());
+ // Log.d("Connecting to device " + mDevice.getAliasName());
return hidConnect(mDevice);
}
@@ -143,7 +143,9 @@ public Boolean bluetoothHidSetReport(
BluetoothDevice device = Bluetooth4Facade.getDevice(sHidProfile.getConnectedDevices(),
deviceID);
Log.d("type " + type.getBytes()[0]);
- return sHidProfile.setReport(device, type.getBytes()[0], report);
+ Log.e("setReport won't work in no-system app.");
+ return false;
+ // return sHidProfile.setReport(device, type.getBytes()[0], report);
}
@Rpc(description = "Send Get_Report command to the connected HID input device.")
@@ -161,7 +163,9 @@ public Boolean bluetoothHidGetReport(
BluetoothDevice device = Bluetooth4Facade.getDevice(sHidProfile.getConnectedDevices(),
deviceID);
Log.d("type " + type.getBytes()[0] + "reportId " + reportId.getBytes()[0]);
- return sHidProfile.getReport(device, type.getBytes()[0], reportId.getBytes()[0], buffSize);
+ Log.e("getReport won't work in no-system app.");
+ return false;
+ // return sHidProfile.getReport(device, type.getBytes()[0], reportId.getBytes()[0], buffSize);
}
@Rpc(description = "Send data to a connected HID device.")
@@ -173,7 +177,10 @@ public Boolean bluetoothHidSendData(
String report) throws Exception {
BluetoothDevice device = Bluetooth4Facade.getDevice(sHidProfile.getConnectedDevices(),
deviceID);
- return sHidProfile.sendData(device, report);
+ Log.e("sendData won't work in no-system app.");
+ return false;
+ // TODO: try to implement.
+ // return sHidProfile.sendData(device, report);
}
@Rpc(description = "Send virtual unplug to a connected HID device.")
@@ -183,7 +190,9 @@ public Boolean bluetoothHidVirtualUnplug(
String deviceID) throws Exception {
BluetoothDevice device = Bluetooth4Facade.getDevice(sHidProfile.getConnectedDevices(),
deviceID);
- return sHidProfile.virtualUnplug(device);
+ Log.e("virtualUnplug won't work in no-system app.");
+ return false;
+ // return sHidProfile.virtualUnplug(device);
}
@Rpc(description = "Test byte transfer.")
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
index 9dbff2b43..9e05b6c93 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
@@ -68,11 +68,11 @@ public void onServiceDisconnected(int profile) {
}
public Boolean hspConnect(BluetoothDevice device) {
- BluetoothNonpublicApi.connectProfile(sHspProfile, device);
+ return BluetoothNonpublicApi.connectProfile(sHspProfile, device);
}
public Boolean hspDisconnect(BluetoothDevice device) {
- BluetoothNonpublicApi.disconnectProfile(sHspProfile, device);
+ return BluetoothNonpublicApi.disconnectProfile(sHspProfile, device);
}
@Rpc(description = "Is Hsp profile ready.")
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothLeScanFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothLeScanFacade.java
index db739a66c..4231fb5f0 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothLeScanFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothLeScanFacade.java
@@ -34,6 +34,7 @@
import android.bluetooth.le.ScanSettings;
import android.os.Bundle;
import android.os.ParcelUuid;
+import android.os.Parcelable;
import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.MainThread;
@@ -355,7 +356,8 @@ public void bleSetScanSettingsScanMode(
public void bleSetScanSettingsResultType(
@RpcParameter(name = "scanResultType")
Integer scanResultType) {
- mScanSettingsBuilder.setScanResultType(scanResultType);
+ Log.e("setScanResultType won't work in no-system app.");
+ // mScanSettingsBuilder.setScanResultType(scanResultType);
}
/**
* Get ScanSetting's callback type
@@ -787,7 +789,9 @@ public void bleSetScanSettingsMatchMode(
public int bleGetScanSettingsMatchMode(
@RpcParameter(name = "scanSettingsIndex") Integer scanSettingsIndex
) {
- return mScanSettingsList.get(scanSettingsIndex).getMatchMode();
+ Log.e("getMatchMode won't work in no-system app.");
+ return 0;
+ // return mScanSettingsList.get(scanSettingsIndex).getMatchMode();
}
@Rpc(description = "Set the scan setting's number of matches")
@@ -800,7 +804,9 @@ public void bleSetScanSettingsNumOfMatches(
public int bleGetScanSettingsNumberOfMatches(
@RpcParameter(name = "scanSettingsIndex")
Integer scanSettingsIndex) {
- return mScanSettingsList.get(scanSettingsIndex).getNumOfMatches();
+ Log.e("getNumOfMatches won't work in no-system app.");
+ return 0;
+ // return mScanSettingsList.get(scanSettingsIndex).getNumOfMatches();
}
private class myScanCallback extends ScanCallback {
@@ -854,7 +860,7 @@ public void onBatchScanResults(List results) {
mResults.putLong("Timestamp", System.currentTimeMillis() / 1000);
mResults.putInt("ID", index);
mResults.putString("Type", "onBatchScanResults");
- mResults.putParcelableList("Results", results);
+ mResults.putParcelableArray("Results", results.toArray(new ScanResult[0]));
mEventFacade.postEvent(mEventType + index + "onBatchScanResult", mResults.clone());
mResults.clear();
}
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMapClientFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMapClientFacade.java
index 8b83ba5a6..4cbd3d923 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMapClientFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMapClientFacade.java
@@ -93,12 +93,12 @@ public BluetoothMapClientFacade(FacadeManager manager) {
mEventFacade = manager.getReceiver(EventFacade.class);
mNotificationReceiver = new NotificationReceiver();
- mSendIntent = new Intent(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY);
- mDeliveryIntent = new Intent(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY);
+ mSendIntent = new Intent(BluetoothNonpublicApi.ACTION_MESSAGE_SENT_SUCCESSFULLY);
+ mDeliveryIntent = new Intent(BluetoothNonpublicApi.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY);
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_RECEIVED);
- intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY);
- intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY);
+ intentFilter.addAction(BluetoothNonpublicApi.ACTION_MESSAGE_RECEIVED);
+ intentFilter.addAction(BluetoothNonpublicApi.ACTION_MESSAGE_SENT_SUCCESSFULLY);
+ intentFilter.addAction(BluetoothNonpublicApi.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY);
mService.registerReceiver(mNotificationReceiver, intentFilter);
Log.d("notification receiver registered");
}
@@ -155,8 +155,10 @@ public Boolean mapSendMessage(
Log.d("PhoneNumber count: " + phoneNumbers.length + " = " + phoneNumbers[i]);
contacts[i] = Uri.parse(phoneNumbers[i]);
}
- return sMapProfile.sendMessage(device, contacts, message, mSentIntent,
- mDeliveredIntent);
+ Log.e("sendMessage won't work in no-system app.");
+ return false;
+ // return sMapProfile.sendMessage(device, contacts, message, mSentIntent,
+ // mDeliveredIntent);
} catch (Exception e) {
Log.d("Error sending message, no such device " + e.toString());
}
@@ -213,13 +215,13 @@ public class NotificationReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.d("OnReceive" + intent);
String action = intent.getAction();
- if (action.equals(BluetoothMapClient.ACTION_MESSAGE_RECEIVED)) {
+ if (action.equals(BluetoothNonpublicApi.ACTION_MESSAGE_RECEIVED)) {
mEventFacade.postEvent(MAP_EVENT,
intent.getStringExtra(android.content.Intent.EXTRA_TEXT));
- } else if (action.equals(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY)) {
+ } else if (action.equals(BluetoothNonpublicApi.ACTION_MESSAGE_SENT_SUCCESSFULLY)) {
mEventFacade.postEvent(MAP_SMS_SENT_SUCCESS,
intent.getStringExtra(android.content.Intent.EXTRA_TEXT));
- } else if (action.equals(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY)) {
+ } else if (action.equals(BluetoothNonpublicApi.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY)) {
mEventFacade.postEvent(MAP_SMS_DELIVER_SUCCESS,
intent.getStringExtra(android.content.Intent.EXTRA_TEXT));
}
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMapFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMapFacade.java
index 47b5c704b..e39ad500a 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMapFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMapFacade.java
@@ -106,7 +106,9 @@ public List bluetoothMapGetConnectedDevices() {
@Rpc(description = "Get the currently connected remote Bluetooth device (PCE).")
public BluetoothDevice bluetoothMapGetClient() {
if (sMapProfile == null) { return null; }
- return sMapProfile.getClient();
+ return null;
+ // TODO: try to implement.
+ // return sMapProfile.getClient();
}
@Override
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMediaFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMediaFacade.java
new file mode 100644
index 000000000..e9be28538
--- /dev/null
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothMediaFacade.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2016 Google Inc.
+ *
+ * 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.googlecode.android_scripting.facade.bluetooth;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.content.Context;
+
+import android.media.MediaMetadata;
+import android.media.MediaMetadataRetriever;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.googlecode.android_scripting.facade.EventFacade;
+import com.googlecode.android_scripting.facade.FacadeManager;
+// import com.googlecode.android_scripting.facade.bluetooth.media.BluetoothSL4AAudioSrcMBS;
+import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
+import com.googlecode.android_scripting.rpc.Rpc;
+import com.googlecode.android_scripting.rpc.RpcParameter;
+import com.googlecode.android_scripting.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SL4A Facade for running Bluetooth Media related test cases
+ * The APIs provided here can be grouped into 3 categories:
+ * 1. Those that can run on both an Audio Source and Sink
+ * 2. Those that makes sense to run only on a Audio Source like a phone
+ * 3. Those that makes sense to run only on a Audio Sink like a Car.
+ *
+ * This media test framework consists of 3 classes:
+ * 1. BluetoothMediaFacade - this class that provides the APIs that a RPC client can interact with
+ * 2. BluetoothSL4AMBS - This is a MediaBrowserService that is intended to run on the Audio Source
+ * (phone). This MediaBrowserService that runs as part of the SL4A app is used to intercept
+ * Media key events coming in from a AVRCP Controller like Car. Intercepting these events lets us
+ * instrument the Bluetooth media related tests.
+ * 3. BluetoothMediaPlayback - The class that the MediaBrowserService uses to play media files.
+ * It is a UI-less MediaPlayer that serves the purpose of Bluetooth Media testing.
+ *
+ * The idea is for the BluetoothMediaFacade to create a BluetoothSL4AMBS MediaSession on the
+ * Phone (Bluetooth Audio source/Avrcp Target) and use it intercept the Media commands coming
+ * from the CarKitt (Bluetooth Audio Sink / Avrcp Controller).
+ * On the Carkitt side, we just create and connect a MediaBrowser to the A2dpMediaBrowserService
+ * that is part of the Carkitt's Bluetooth Audio App. We use this browser to send media commands
+ * to the Phone side and intercept the commands with the BluetoothSL4AMBS.
+ * This set up helps to instrument tests that can test various Bluetooth Media usecases.
+ */
+
+public class BluetoothMediaFacade extends RpcReceiver {
+ private static final String TAG = "BluetoothMediaFacade";
+ private static final boolean VDBG = false;
+ private final Service mService;
+ private final Context mContext;
+ private Handler mHandler;
+ private MediaSessionManager mSessionManager;
+ private MediaController mMediaController = null;
+ private MediaController.Callback mMediaCtrlCallback = null;
+ private MediaSessionManager.OnActiveSessionsChangedListener mSessionListener;
+ private MediaBrowser mBrowser = null;
+
+ private static EventFacade mEventFacade;
+ // Events posted
+ private static final String EVENT_PLAY_RECEIVED = "playReceived";
+ private static final String EVENT_PAUSE_RECEIVED = "pauseReceived";
+ private static final String EVENT_SKIP_PREV_RECEIVED = "skipPrevReceived";
+ private static final String EVENT_SKIP_NEXT_RECEIVED = "skipNextReceived";
+
+ // Commands received
+ private static final String CMD_MEDIA_PLAY = "play";
+ private static final String CMD_MEDIA_PAUSE = "pause";
+ private static final String CMD_MEDIA_SKIP_NEXT = "skipNext";
+ private static final String CMD_MEDIA_SKIP_PREV = "skipPrev";
+
+ private static final String BLUETOOTH_PKG_NAME = "com.android.bluetooth";
+ private static final String BROWSER_SERVICE_NAME =
+ "com.android.bluetooth.a2dpsink.mbs.A2dpMediaBrowserService";
+ private static final String A2DP_MBS_TAG = "A2dpMediaBrowserService";
+
+ // MediaMetadata keys
+ private static final String MEDIA_KEY_TITLE = "keyTitle";
+ private static final String MEDIA_KEY_ALBUM = "keyAlbum";
+ private static final String MEDIA_KEY_ARTIST = "keyArtist";
+ private static final String MEDIA_KEY_DURATION = "keyDuration";
+ private static final String MEDIA_KEY_NUM_TRACKS = "keyNumTracks";
+
+ /**
+ * Following things are initialized here:
+ * 1. Setup Listeners to Active Media Session changes
+ * 2. Create a new MediaController.callback instance
+ */
+ public BluetoothMediaFacade(FacadeManager manager) {
+ super(manager);
+ mService = manager.getService();
+ mEventFacade = manager.getReceiver(EventFacade.class);
+ mHandler = new Handler(Looper.getMainLooper());
+ mContext = mService.getApplicationContext();
+ mSessionManager =
+ (MediaSessionManager) mContext.getSystemService(mContext.MEDIA_SESSION_SERVICE);
+ mSessionListener = new SessionChangeListener();
+ // Listen on Active MediaSession changes, so we can get the active session's MediaController
+ if (mSessionManager != null) {
+ ComponentName compName =
+ new ComponentName(mContext.getPackageName(), this.getClass().getName());
+ mSessionManager.addOnActiveSessionsChangedListener(mSessionListener, null,
+ mHandler);
+ if (VDBG) {
+ List mcl = mSessionManager.getActiveSessions(null);
+ Log.d(TAG + " Num Sessions " + mcl.size());
+ for (int i = 0; i < mcl.size(); i++) {
+ Log.d(TAG + "Active session : " + i + ((MediaController) (mcl.get(
+ i))).getPackageName() + mcl.get(i));
+ // ((MediaController) (mcl.get(i))).getTag());
+ }
+ }
+ }
+ mMediaCtrlCallback = new MediaControllerCallback();
+ }
+
+ /**
+ * The listener that was setup for listening to changes to Active Media Sessions.
+ * This listener is useful in both Car and Phone sides.
+ */
+ private class SessionChangeListener
+ implements MediaSessionManager.OnActiveSessionsChangedListener {
+ /**
+ * On the Phone side, it listens to the BluetoothSL4AAudioSrcMBS (that the SL4A app runs)
+ * becoming active.
+ * On the Car side, it listens to the A2dpMediaBrowserService (associated with the
+ * Bluetooth Audio App) becoming active.
+ * The idea is to get a handle to the MediaController appropriate for the device, so
+ * that we can send and receive Media commands.
+ */
+ @Override
+ public void onActiveSessionsChanged(List controllers) {
+ if (VDBG) {
+ Log.d(TAG + " onActiveSessionsChanged : " + controllers.size());
+ for (int i = 0; i < controllers.size(); i++) {
+ Log.d(TAG + "Active session : " + i + ((MediaController) (controllers.get(
+ i))).getPackageName()); /* + ((MediaController) (controllers.get(
+ i))).getTag()); */
+ }
+ }
+ // As explained above, looking for the BluetoothSL4AAudioSrcMBS (when running on Phone)
+ // or A2dpMediaBrowserService (when running on Carkitt).
+ for (int i = 0; i < controllers.size(); i++) {
+ MediaController controller = (MediaController) controllers.get(i);
+ Log.e("MediaController.getTag won't work in no-system app." + controller);
+ /* TODO: try to implement.
+ if ((controller.getTag().contains(BluetoothSL4AAudioSrcMBS.getTag()))
+ || (controller.getTag().contains(A2DP_MBS_TAG))) {
+ setCurrentMediaController(controller);
+ return;
+ }
+ */
+ }
+ }
+ }
+
+ /**
+ * When the MediaController for the required MediaSession is obtained, register for its
+ * callbacks.
+ * Not used yet, but this can be used to verify state changes in both ends.
+ */
+ private class MediaControllerCallback extends MediaController.Callback {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ Log.d(TAG + " onPlaybackStateChanged: " + state.getState());
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ Log.d(TAG + " onMetadataChanged ");
+ }
+ }
+
+ /**
+ * Callback on MediaBrowser.connect()
+ * This is relevant only on the Carkitt side, since the intent is to connect a MediaBrowser
+ * to the A2dpMediaBrowser Service that is run by the Car's Bluetooth Audio App.
+ * On successful connection, we obtain the handle to the corresponding MediaController,
+ * so we can imitate sending media commands via the Bluetooth Audio App.
+ */
+ MediaBrowser.ConnectionCallback mBrowserConnectionCallback =
+ new MediaBrowser.ConnectionCallback() {
+ private static final String classTag = TAG + " BrowserConnectionCallback";
+
+ @Override
+ public void onConnected() {
+ Log.d(classTag + " onConnected: session token " + mBrowser.getSessionToken());
+ MediaController mediaController = new MediaController(mContext,
+ mBrowser.getSessionToken());
+ // Update the MediaController
+ setCurrentMediaController(mediaController);
+ }
+
+ @Override
+ public void onConnectionFailed() {
+ Log.d(classTag + " onConnectionFailed");
+ }
+ };
+
+ /**
+ * Update the Current MediaController.
+ * As has been commented above, we need the MediaController handles to the
+ * BluetoothSL4AAudioSrcMBS on Phone and A2dpMediaBrowserService on Car to send and receive
+ * media commands.
+ *
+ * @param controller - Controller to update with
+ */
+ private void setCurrentMediaController(MediaController controller) {
+ Handler mainHandler = new Handler(mContext.getMainLooper());
+ if (mMediaController == null && controller != null) {
+ Log.d(TAG + " Setting MediaController "); // + controller.getTag());
+ mMediaController = controller;
+ mMediaController.registerCallback(mMediaCtrlCallback);
+ } else if (mMediaController != null && controller != null) {
+ // We have a new MediaController that we have to update to.
+ if (controller.getSessionToken().equals(mMediaController.getSessionToken())
+ == false) {
+ Log.d(TAG + " Changing MediaController "); // + controller.getTag());
+ mMediaController.unregisterCallback(mMediaCtrlCallback);
+ mMediaController = controller;
+ mMediaController.registerCallback(mMediaCtrlCallback, mainHandler);
+ }
+ } else if (mMediaController != null && controller == null) {
+ // Clearing the current MediaController
+ Log.d(TAG + " Clearing MediaController "); // + mMediaController.getTag());
+ mMediaController.unregisterCallback(mMediaCtrlCallback);
+ mMediaController = controller;
+ }
+ }
+
+ /**
+ * Class method called from {@link BluetoothSL4AAudioSrcMBS} to post an Event through
+ * EventFacade back to the RPC client.
+ * This is dispatched from the Phone to the host (RPC Client) to acknowledge that it
+ * received a playback command.
+ *
+ * @param playbackState PlaybackState change that is posted as an Event to the client.
+ */
+ public static void dispatchPlaybackStateChanged(int playbackState) {
+ Bundle news = new Bundle();
+ switch (playbackState) {
+ case PlaybackState.STATE_PLAYING:
+ mEventFacade.postEvent(EVENT_PLAY_RECEIVED, news);
+ break;
+ case PlaybackState.STATE_PAUSED:
+ mEventFacade.postEvent(EVENT_PAUSE_RECEIVED, news);
+ break;
+ case PlaybackState.STATE_SKIPPING_TO_NEXT:
+ mEventFacade.postEvent(EVENT_SKIP_NEXT_RECEIVED, news);
+ break;
+ case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+ mEventFacade.postEvent(EVENT_SKIP_PREV_RECEIVED, news);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /******************************RPC APIS************************************************/
+
+ /**
+ * Relevance - Phone and Car.
+ * Sends the passthrough command through the currently active MediaController.
+ * If there isn't one, look for the currently active sessions and just pick the first one,
+ * just a fallback.
+ * This function is generic enough to be used in either a Phone or the Car side, since
+ * all this does is to pick the currently active Media Controller and sends a passthrough
+ * command. In the test setup, this is used to mimic sending a passthrough command from
+ * Car.
+ */
+ @Rpc(description = "Simulate a passthrough command")
+ public void bluetoothMediaPassthrough(
+ @RpcParameter(name = "passthruCmd", description = "play/pause/skipFwd/skipBack")
+ String passthruCmd) {
+ Log.d(TAG + "Passthrough Cmd " + passthruCmd);
+ if (mMediaController == null) {
+ Log.i(TAG + " Media Controller not ready - Grabbing existing one");
+ ComponentName name =
+ new ComponentName(mContext.getPackageName(),
+ mSessionListener.getClass().getName());
+ List listMC = mSessionManager.getActiveSessions(null);
+ if (listMC.size() > 0) {
+ if (VDBG) {
+ Log.d(TAG + " Num Sessions " + listMC.size());
+ for (int i = 0; i < listMC.size(); i++) {
+ Log.d(TAG + "Active session : " + i + ((MediaController) (listMC.get(
+ i))).getPackageName());
+ // TODO: try to implement: + (MediaController) (listMC.get(i))).getTag());
+ }
+ }
+ mMediaController = (MediaController) listMC.get(0);
+ } else {
+ Log.d(TAG + " No Active Media Session to grab");
+ return;
+ }
+ }
+
+ switch (passthruCmd) {
+ case CMD_MEDIA_PLAY:
+ mMediaController.getTransportControls().play();
+ break;
+ case CMD_MEDIA_PAUSE:
+ mMediaController.getTransportControls().pause();
+ break;
+ case CMD_MEDIA_SKIP_NEXT:
+ mMediaController.getTransportControls().skipToNext();
+ break;
+ case CMD_MEDIA_SKIP_PREV:
+ mMediaController.getTransportControls().skipToPrevious();
+ break;
+ default:
+ Log.d(TAG + " Unsupported Passthrough Cmd");
+ break;
+ }
+ }
+
+ /**
+ * Relevance - Phone and Car.
+ * Returns the currently playing media's metadata.
+ * Can be queried on the car and the phone in the middle of a streaming session to
+ * verify they are in sync.
+ *
+ * @return Currently playing Media's metadata
+ */
+ @Rpc(description = "Gets the Metadata of currently playing Media")
+ public Map bluetoothMediaGetCurrentMediaMetaData() {
+ Map track = null;
+ if (mMediaController == null) {
+ Log.d(TAG + "MediaController Not set");
+ return track;
+ }
+ MediaMetadata metadata = mMediaController.getMetadata();
+ if (metadata == null) {
+ Log.e("No Metadata available.");
+ return track;
+ }
+ track = new HashMap<>();
+ track.put(MEDIA_KEY_TITLE, metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
+ track.put(MEDIA_KEY_ALBUM, metadata.getString(MediaMetadata.METADATA_KEY_ALBUM));
+ track.put(MEDIA_KEY_ARTIST, metadata.getString(MediaMetadata.METADATA_KEY_ARTIST));
+ track.put(MEDIA_KEY_DURATION,
+ String.valueOf(metadata.getLong(MediaMetadata.METADATA_KEY_DURATION)));
+ track.put(MEDIA_KEY_NUM_TRACKS,
+ String.valueOf(metadata.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)));
+ return track;
+ }
+
+ /**
+ * Relevance - Phone and Car
+ * Returns the current active media sessions for the device. This is useful to see if a
+ * Media Session we are interested in is currently active.
+ * In the Bluetooth Media tests, this is indirectly used to determine if audio is being
+ * played via BT. For ex., when the Car and Phone are connected via BT and audio is being
+ * streamed, A2dpMediaBrowserService will be active on the Car side. If the connection is
+ * terminated in the middle, A2dpMediaBrowserService will no longer be active on the Carkitt,
+ * whereas BluetoothSL4AAudioSrcMBS will still be active.
+ *
+ * @return A list of names of the active media sessions
+ */
+ @Rpc(description = "Get the current active Media Sessions")
+ public List bluetoothMediaGetActiveMediaSessions() {
+ List controllers = mSessionManager.getActiveSessions(null);
+ List sessions = new ArrayList();
+ for (MediaController mc : controllers) {
+ sessions.add(mc.toString());
+ // sessions.add(mc.getTag());
+ }
+ return sessions;
+ }
+
+ /**
+ * Relevance - Car Only
+ * Called from the Carkitt to connect a MediaBrowser to the Bluetooth Audio App's
+ * A2dpMediaBrowserService. The callback on successful connection gives the handle to
+ * the MediaController through which we can send media commands.
+ */
+ @Rpc(description = "Connect a MediaBrowser to the A2dpMediaBrowserservice in the Carkitt")
+ public void bluetoothMediaConnectToCarMBS() {
+ // Create a MediaBrowser to connect to the A2dpMBS
+ if (mBrowser == null) {
+ final ComponentName compName =
+ new ComponentName(BLUETOOTH_PKG_NAME, BROWSER_SERVICE_NAME);
+ // Note - MediaBrowser connect needs to be done on the Main Thread's handler,
+ // otherwise we never get the ServiceConnected callback.
+ Runnable createAndConnectMediaBrowser = new Runnable() {
+ @Override
+ public void run() {
+ mBrowser = new MediaBrowser(mContext, compName, mBrowserConnectionCallback,
+ null);
+ if (mBrowser != null) {
+ Log.d(TAG + " Connecting to MBS");
+ mBrowser.connect();
+ } else {
+ Log.d(TAG + " Failed to create a MediaBrowser");
+ }
+ }
+ };
+
+ Handler mainHandler = new Handler(mContext.getMainLooper());
+ mainHandler.post(createAndConnectMediaBrowser);
+ } //mBrowser
+ }
+
+ /**
+ * Relevance - Phone Only
+ * Start the BluetoothSL4AAudioSrcMBS on the Phone so the media commands coming in
+ * via Bluetooth AVRCP can be intercepted by the SL4A test
+ */
+ @Rpc(description = "Start the BluetoothSL4AAudioSrcMBS on Phone.")
+ public void bluetoothMediaPhoneSL4AMBSStart() {
+ Log.d(TAG + "Starting BluetoothSL4AAudioSrcMBS");
+ // Start the Avrcp Media Browser service. Starting it sets it to active.
+ /* TODO: try to implement.
+ Intent startIntent = new Intent(mContext, BluetoothSL4AAudioSrcMBS.class);
+ mContext.startService(startIntent);
+ */
+ }
+
+ /**
+ * Relevance - Phone Only
+ * Stop the BluetoothSL4AAudioSrcMBS
+ */
+ @Rpc(description = "Stop the BluetoothSL4AAudioSrcMBS running on Phone.")
+ public void bluetoothMediaPhoneSL4AMBSStop() {
+ Log.d(TAG + "Stopping BluetoothSL4AAudioSrcMBS");
+ // Stop the Avrcp Media Browser service.
+ /* TODO: try to implement.
+ Intent stopIntent = new Intent(mContext, BluetoothSL4AAudioSrcMBS.class);
+ mContext.stopService(stopIntent);
+ */
+ }
+
+ /**
+ * Relevance - Phone only
+ * This is used to simulate play/pause/skip media commands on the Phone directly, as against
+ * receiving these commands via AVRCP from the Carkitt.
+ * This function talks to the BluetoothSL4AAudioSrcMBS to simulate the media command.
+ * An example test where this would be useful - Play music on Phone that is not connected
+ * on bluetooth and connect in the middle to verify if music is steamed to the other end.
+ *
+ * @param command - Media command to simulate on the Phone
+ */
+ @Rpc(description = "Media Commands on the Phone's BluetoothAvrcpMBS.")
+ public void bluetoothMediaHandleMediaCommandOnPhone(String command) {
+ /* TODO: try to implement.
+ BluetoothSL4AAudioSrcMBS mbs =
+ BluetoothSL4AAudioSrcMBS.getAvrcpMediaBrowserService();
+ if (mbs != null) {
+ mbs.handleMediaCommand(command);
+ } else {
+ Log.e(TAG + " No BluetoothSL4AAudioSrcMBS running on the device");
+ }
+ */
+ }
+
+
+ @Override
+ public void shutdown() {
+ setCurrentMediaController(null);
+ }
+}
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothPanFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothPanFacade.java
index 71db0c603..3e9152b8e 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothPanFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothPanFacade.java
@@ -73,7 +73,8 @@ public void onServiceDisconnected(int profile) {
@Rpc(description = "Set Bluetooth Tethering")
public void bluetoothPanSetBluetoothTethering(
@RpcParameter(name = "enable") Boolean enable) {
- sPanProfile.setBluetoothTethering(enable);
+ Log.e("setBluetoothTegthering wont run in no-system app.");
+ // sPanProfile.setBluetoothTethering(enable);
}
public Boolean panConnect(BluetoothDevice device) {
@@ -99,7 +100,10 @@ public Boolean bluetoothPanIsTetheringOn() {
if (!sIsPanReady || sPanProfile == null) {
return false;
}
- return sPanProfile.isTetheringOn();
+ Log.e("isTetheringOn won't work in no-system app.");
+ return false;
+ // TODO: try to implement.
+ // return sPanProfile.isTetheringOn();
}
public void shutdown() {
diff --git a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/GattClientFacade.java b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/GattClientFacade.java
index d99ea8e7a..444cc6af9 100644
--- a/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/GattClientFacade.java
+++ b/android/Bluetooth4Facade/src/com/googlecode/android_scripting/facade/bluetooth/GattClientFacade.java
@@ -644,7 +644,10 @@ public boolean gattClientReadRSSI(
+ "remote device")
public boolean gattClientRefresh(@RpcParameter(name = "index") Integer index) throws Exception {
if (mBluetoothGattList.get(index) != null) {
- return mBluetoothGattList.get(index).refresh();
+ Log.e("refresh won't work in no-system app.");
+ return false;
+ // TODO: try to implement.
+ // return mBluetoothGattList.get(index).refresh();
} else {
throw new Exception("Invalid index input:" + index);
}
diff --git a/android/ScriptingLayer/build.gradle b/android/ScriptingLayer/build.gradle
index 508204d1c..d0a4f89ab 100644
--- a/android/ScriptingLayer/build.gradle
+++ b/android/ScriptingLayer/build.gradle
@@ -14,6 +14,7 @@ dependencies {
compile project(':Common')
compile project(':Utils')
compile project(':BluetoothFacade')
+ compile project(':Bluetooth4Facade')
compile project(':SignalStrengthFacade')
compile project(':TextToSpeechFacade')
compile project(':WebCamFacade')
diff --git a/android/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java b/android/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
index 8a7e52b92..646c4519a 100644
--- a/android/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
+++ b/android/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
@@ -19,6 +19,7 @@
import com.google.common.collect.Maps;
import com.googlecode.android_scripting.Log;
+import com.googlecode.android_scripting.facade.bluetooth.BluetoothA2dpSinkFacade;
import com.googlecode.android_scripting.facade.ui.UiFacade;
import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
import com.googlecode.android_scripting.rpc.MethodDescriptor;
@@ -122,7 +123,7 @@ public class FacadeConfiguration {
sFacadeClassList.add(BluetoothHfpClientFacade.class);
sFacadeClassList.add(BluetoothA2dpSinkFacade.class);
sFacadeClassList.add(BluetoothPbapClientFacade.class);
- sFacadeClassList.add(NsdManagerFacade.class);
+ // sFacadeClassList.add(NsdManagerFacade.class);
sFacadeClassList.add(BluetoothMapClientFacade.class);
}
diff --git a/android/ScriptingLayerForAndroid/build.gradle b/android/ScriptingLayerForAndroid/build.gradle
index 14a8462dc..55d689565 100644
--- a/android/ScriptingLayerForAndroid/build.gradle
+++ b/android/ScriptingLayerForAndroid/build.gradle
@@ -15,6 +15,7 @@ dependencies {
compile project(':Utils')
compile project(':QuickAction')
compile project(':BluetoothFacade')
+ compile project(':Bluetooth4Facade')
compile project(':SignalStrengthFacade')
compile project(':TextToSpeechFacade')
compile project(':WebCamFacade')
diff --git a/android/ScriptingLayerForAndroid/settings.gradle b/android/ScriptingLayerForAndroid/settings.gradle
index 4f81fa3c1..dba78909d 100644
--- a/android/ScriptingLayerForAndroid/settings.gradle
+++ b/android/ScriptingLayerForAndroid/settings.gradle
@@ -1,5 +1,6 @@
includeFlat 'Common', 'Utils', 'QuickAction'
includeFlat 'BluetoothFacade'
+includeFlat 'Bluetooth4Facade'
includeFlat 'SignalStrengthFacade'
includeFlat 'TextToSpeechFacade'
includeFlat 'WebCamFacade'