Framework changes for bluez4.

Changes in the Bluetooth JNI calls and framework functions
for Bluez4.
This commit is contained in:
Jaikumar Ganesh
2009-05-05 22:26:12 -07:00
parent b099c4699b
commit d5ac1ae36b
15 changed files with 1715 additions and 2356 deletions

View File

@@ -89,7 +89,6 @@ LOCAL_SRC_FILES += \
core/java/android/backup/IRestoreSession.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
core/java/android/bluetooth/IBluetoothDevice.aidl \
core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
core/java/android/bluetooth/IBluetoothHeadset.aidl \
core/java/android/content/IContentService.aidl \
core/java/android/content/ISyncAdapter.aidl \
@@ -481,5 +480,3 @@ include $(BUILD_JAVA_LIBRARY)
ifeq (,$(ONE_SHOT_MAKEFILE))
include $(call first-makefiles-under,$(LOCAL_PATH))
endif

View File

@@ -75,7 +75,7 @@ public class BluetoothDevice {
public static final int UNBOND_REASON_REMOVED = 6;
private static final String TAG = "BluetoothDevice";
private final IBluetoothDevice mService;
/**
* @hide - hide this because it takes a parameter of type
@@ -180,31 +180,6 @@ public class BluetoothDevice {
return false;
}
public String getVersion() {
try {
return mService.getVersion();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRevision() {
try {
return mService.getRevision();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getManufacturer() {
try {
return mService.getManufacturer();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getCompany() {
try {
return mService.getCompany();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* Get the current scan mode.
* Used to determine if the local device is connectable and/or discoverable
@@ -241,11 +216,8 @@ public class BluetoothDevice {
}
public boolean startDiscovery() {
return startDiscovery(true);
}
public boolean startDiscovery(boolean resolveNames) {
try {
return mService.startDiscovery(resolveNames);
return mService.startDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
@@ -263,88 +235,17 @@ public class BluetoothDevice {
return false;
}
public boolean startPeriodicDiscovery() {
try {
return mService.startPeriodicDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public boolean stopPeriodicDiscovery() {
try {
return mService.stopPeriodicDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public boolean isPeriodicDiscovery() {
try {
return mService.isPeriodicDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public String[] listRemoteDevices() {
try {
return mService.listRemoteDevices();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* List remote devices that have a low level (ACL) connection.
*
* RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
* an ACL connection even when not paired - this is common for SDP queries
* or for in-progress pairing requests.
*
* In most cases you probably want to test if a higher level protocol is
* connected, rather than testing ACL connections.
*
* @return bluetooth hardware addresses of remote devices with a current
* ACL connection. Array size is 0 if no devices have a
* connection. Null on error.
*/
public String[] listAclConnections() {
try {
return mService.listAclConnections();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* Check if a specified remote device has a low level (ACL) connection.
*
* RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
* an ACL connection even when not paired - this is common for SDP queries
* or for in-progress pairing requests.
*
* In most cases you probably want to test if a higher level protocol is
* connected, rather than testing ACL connections.
*
* @param address the Bluetooth hardware address you want to check.
* @return true if there is an ACL connection, false otherwise and on
* error.
*/
public boolean isAclConnected(String address) {
try {
return mService.isAclConnected(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Perform a low level (ACL) disconnection of a remote device.
*
* This forcably disconnects the ACL layer connection to a remote device,
* which will cause all RFCOMM, SDP and L2CAP connections to this remote
* device to close.
* Removes the remote device and the pairing information associated
* with it.
*
* @param address the Bluetooth hardware address you want to disconnect.
* @return true if the device was disconnected, false otherwise and on
* error.
*/
public boolean disconnectRemoteDeviceAcl(String address) {
public boolean removeBond(String address) {
try {
return mService.disconnectRemoteDeviceAcl(address);
return mService.removeBond(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
@@ -376,16 +277,6 @@ public class BluetoothDevice {
return false;
}
/**
* Remove an already exisiting bonding (delete the link key).
*/
public boolean removeBond(String address) {
try {
return mService.removeBond(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* List remote devices that are bonded (paired) to the local device.
*
@@ -440,78 +331,25 @@ public class BluetoothDevice {
return null;
}
public String getRemoteVersion(String address) {
try {
return mService.getRemoteVersion(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteRevision(String address) {
try {
return mService.getRemoteRevision(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteManufacturer(String address) {
try {
return mService.getRemoteManufacturer(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteCompany(String address) {
try {
return mService.getRemoteCompany(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* Returns the RFCOMM channel associated with the 16-byte UUID on
* the remote Bluetooth address.
*
* Performs a SDP ServiceSearchAttributeRequest transaction. The provided
* uuid is verified in the returned record. If there was a problem, or the
* specified uuid does not exist, -1 is returned.
*/
public boolean getRemoteServiceChannel(String address, short uuid16,
IBluetoothDeviceCallback callback) {
try {
return mService.getRemoteServiceChannel(address, uuid16, callback);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Get the major, minor and servics classes of a remote device.
* These classes are encoded as a 32-bit integer. See BluetoothClass.
* @param address remote device
* @return 32-bit class suitable for use with BluetoothClass, or
* BluetoothClass.ERROR on error
*/
public int getRemoteClass(String address) {
try {
return mService.getRemoteClass(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return BluetoothClass.ERROR;
return BluetoothError.ERROR_IPC;
}
public byte[] getRemoteFeatures(String address) {
public String[] getRemoteUuids(String address) {
try {
return mService.getRemoteFeatures(address);
return mService.getRemoteUuids(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String lastSeen(String address) {
try {
return mService.lastSeen(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String lastUsed(String address) {
try {
return mService.lastUsed(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
public int getRemoteServiceChannel(String address, String uuid) {
try {
return mService.getRemoteServiceChannel(address, uuid);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return BluetoothError.ERROR_IPC;
}
public boolean setPin(String address, byte[] pin) {

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2009 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 android.bluetooth;
import java.util.UUID;
/**
* Static helper methods and constants to decode the UUID of remote devices.
* @hide
*/
public final class BluetoothUuid {
/* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
* for the various services.
*
* The following 128 bit values are calculated as:
* uuid * 2^96 + BASE_UUID
*/
public static final UUID AudioSink = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB");
public static final UUID AudioSource = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB");
public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB");
public static final UUID HSP = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB");
public static final UUID HeadsetHS = UUID.fromString("00001131-0000-1000-8000-00805F9B34FB");
public static final UUID Handsfree = UUID.fromString("0000111e-0000-1000-8000-00805F9B34FB");
public static final UUID HandsfreeAudioGateway
= UUID.fromString("0000111f-0000-1000-8000-00805F9B34FB");
public static boolean isAudioSource(UUID uuid) {
return uuid.equals(AudioSource);
}
public static boolean isAudioSink(UUID uuid) {
return uuid.equals(AudioSink);
}
public static boolean isAdvAudioDist(UUID uuid) {
return uuid.equals(AdvAudioDist);
}
public static boolean isHandsfree(UUID uuid) {
return uuid.equals(Handsfree) || uuid.equals(HandsfreeAudioGateway);
}
public static boolean isHeadset(UUID uuid) {
return uuid.equals(HSP) || uuid.equals(HeadsetHS);
}
}

View File

@@ -16,8 +16,6 @@
package android.bluetooth;
import android.bluetooth.IBluetoothDeviceCallback;
/**
* System private API for talking with the Bluetooth service.
*
@@ -33,10 +31,6 @@ interface IBluetoothDevice
String getAddress();
String getName();
boolean setName(in String name);
String getVersion();
String getRevision();
String getManufacturer();
String getCompany();
int getScanMode();
boolean setScanMode(int mode);
@@ -44,17 +38,9 @@ interface IBluetoothDevice
int getDiscoverableTimeout();
boolean setDiscoverableTimeout(int timeout);
boolean startDiscovery(boolean resolveNames);
boolean startDiscovery();
boolean cancelDiscovery();
boolean isDiscovering();
boolean startPeriodicDiscovery();
boolean stopPeriodicDiscovery();
boolean isPeriodicDiscovery();
String[] listRemoteDevices();
String[] listAclConnections();
boolean isAclConnected(in String address);
boolean disconnectRemoteDeviceAcl(in String address);
boolean createBond(in String address);
boolean cancelBondProcess(in String address);
@@ -63,15 +49,9 @@ interface IBluetoothDevice
int getBondState(in String address);
String getRemoteName(in String address);
String getRemoteVersion(in String address);
String getRemoteRevision(in String address);
int getRemoteClass(in String address);
String getRemoteManufacturer(in String address);
String getRemoteCompany(in String address);
boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback);
byte[] getRemoteFeatures(in String adddress);
String lastSeen(in String address);
String lastUsed(in String address);
String[] getRemoteUuids(in String address);
int getRemoteServiceChannel(in String address, String uuid);
boolean setPin(in String address, in byte[] pin);
boolean cancelPin(in String address);

View File

@@ -1,25 +0,0 @@
/*
* Copyright (C) 2008, 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 android.bluetooth;
/**
* {@hide}
*/
oneway interface IBluetoothDeviceCallback
{
void onGetRemoteServiceChannelResult(in String address, int channel);
}

View File

@@ -26,14 +26,13 @@ import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothError;
import android.bluetooth.BluetoothIntent;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothA2dp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
@@ -44,6 +43,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private static final String TAG = "BluetoothA2dpService";
@@ -58,57 +58,22 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private static final String BLUETOOTH_ENABLED = "bluetooth_enabled";
private static final int MESSAGE_CONNECT_TO = 1;
private static final int MESSAGE_DISCONNECT = 2;
private static final String PROPERTY_STATE = "State";
private static final String SINK_STATE_DISCONNECTED = "disconnected";
private static final String SINK_STATE_CONNECTING = "connecting";
private static final String SINK_STATE_CONNECTED = "connected";
private static final String SINK_STATE_PLAYING = "playing";
private static int mSinkCount;
private final Context mContext;
private final IntentFilter mIntentFilter;
private HashMap<String, SinkState> mAudioDevices;
private HashMap<String, Integer> mAudioDevices;
private final AudioManager mAudioManager;
private final BluetoothDevice mBluetooth;
// list of disconnected sinks to process after a delay
private final ArrayList<String> mPendingDisconnects = new ArrayList<String>();
// number of active sinks
private int mSinkCount = 0;
private class SinkState {
public String address;
public int state;
public SinkState(String a, int s) {address = a; state = s;}
}
public BluetoothA2dpService(Context context) {
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mBluetooth = (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetooth == null) {
throw new RuntimeException("Platform does not support Bluetooth");
}
if (!initNative()) {
throw new RuntimeException("Could not init BluetoothA2dpService");
}
mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
mContext.registerReceiver(mReceiver, mIntentFilter);
if (mBluetooth.isEnabled()) {
onBluetoothEnable();
}
}
@Override
protected void finalize() throws Throwable {
try {
cleanupNative();
} finally {
super.finalize();
}
}
private final BluetoothDeviceService mBluetoothService;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -151,6 +116,40 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
}
};
public BluetoothA2dpService(Context context, BluetoothDeviceService bluetoothService) {
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mBluetoothService = bluetoothService;
if (mBluetoothService == null) {
throw new RuntimeException("Platform does not support Bluetooth");
}
if (!initNative()) {
throw new RuntimeException("Could not init BluetoothA2dpService");
}
mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
mContext.registerReceiver(mReceiver, mIntentFilter);
mAudioDevices = new HashMap<String, Integer>();
if (mBluetoothService.isEnabled())
onBluetoothEnable();
}
@Override
protected void finalize() throws Throwable {
try {
cleanupNative();
} finally {
super.finalize();
}
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -159,7 +158,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
String address = (String)msg.obj;
// check bluetooth is still on, device is still preferred, and
// nothing is currently connected
if (mBluetooth.isEnabled() &&
if (mBluetoothService.isEnabled() &&
getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF &&
lookupSinksMatchingStates(new int[] {
BluetoothA2dp.STATE_CONNECTING,
@@ -170,45 +169,85 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
connectSink(address);
}
break;
case MESSAGE_DISCONNECT:
handleDeferredDisconnect((String)msg.obj);
break;
}
}
};
private int convertBluezSinkStringtoState(String value) {
if (value.equalsIgnoreCase("disconnected"))
return BluetoothA2dp.STATE_DISCONNECTED;
if (value.equalsIgnoreCase("connecting"))
return BluetoothA2dp.STATE_CONNECTING;
if (value.equalsIgnoreCase("connected"))
return BluetoothA2dp.STATE_CONNECTED;
if (value.equalsIgnoreCase("playing"))
return BluetoothA2dp.STATE_PLAYING;
return -1;
}
private synchronized boolean addAudioSink (String address) {
String path = mBluetoothService.getObjectPathFromAddress(address);
String propValues[] = (String []) getSinkPropertiesNative(path);
if (propValues == null) {
Log.e(TAG, "Error while getting AudioSink properties for device: " + address);
return false;
}
Integer state = null;
// Properties are name-value pairs
for (int i = 0; i < propValues.length; i+=2) {
if (propValues[i].equals(PROPERTY_STATE)) {
state = new Integer(convertBluezSinkStringtoState(propValues[i+1]));
break;
}
}
mAudioDevices.put(address, state);
handleSinkStateChange(address, BluetoothA2dp.STATE_DISCONNECTED, state);
return true;
}
private synchronized void onBluetoothEnable() {
mAudioDevices = new HashMap<String, SinkState>();
String[] paths = (String[])listHeadsetsNative();
if (paths != null) {
for (String path : paths) {
mAudioDevices.put(path, new SinkState(getAddressNative(path),
isSinkConnectedNative(path) ? BluetoothA2dp.STATE_CONNECTED :
BluetoothA2dp.STATE_DISCONNECTED));
String devices = mBluetoothService.getProperty("Devices");
mSinkCount = 0;
if (devices != null) {
String [] paths = devices.split(",");
for (String path: paths) {
String address = mBluetoothService.getAddressFromObjectPath(path);
String []uuids = mBluetoothService.getRemoteUuids(address);
if (uuids != null)
for (String uuid: uuids) {
UUID remoteUuid = UUID.fromString(uuid);
if (BluetoothUuid.isAudioSink(remoteUuid) ||
BluetoothUuid.isAudioSource(remoteUuid) ||
BluetoothUuid.isAdvAudioDist(remoteUuid)) {
addAudioSink(address);
break;
}
}
}
}
mAudioManager.setParameter(BLUETOOTH_ENABLED, "true");
}
private synchronized void onBluetoothDisable() {
if (mAudioDevices != null) {
// copy to allow modification during iteration
String[] paths = new String[mAudioDevices.size()];
paths = mAudioDevices.keySet().toArray(paths);
for (String path : paths) {
switch (mAudioDevices.get(path).state) {
if (!mAudioDevices.isEmpty()) {
String [] addresses = new String[mAudioDevices.size()];
addresses = mAudioDevices.keySet().toArray(addresses);
for (String address : addresses) {
int state = getSinkState(address);
switch (state) {
case BluetoothA2dp.STATE_CONNECTING:
case BluetoothA2dp.STATE_CONNECTED:
case BluetoothA2dp.STATE_PLAYING:
disconnectSinkNative(path);
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
disconnectSinkNative(mBluetoothService.getObjectPathFromAddress(address));
handleSinkStateChange(address,state, BluetoothA2dp.STATE_DISCONNECTED);
break;
case BluetoothA2dp.STATE_DISCONNECTING:
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
handleSinkStateChange(address, BluetoothA2dp.STATE_DISCONNECTING,
BluetoothA2dp.STATE_DISCONNECTED);
break;
}
}
mAudioDevices = null;
mAudioDevices.clear();
}
mAudioManager.setBluetoothA2dpOn(false);
mAudioManager.setParameter(BLUETOOTH_ENABLED, "false");
@@ -221,9 +260,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return BluetoothError.ERROR;
}
if (mAudioDevices == null) {
return BluetoothError.ERROR;
}
// ignore if there are any active sinks
if (lookupSinksMatchingStates(new int[] {
BluetoothA2dp.STATE_CONNECTING,
@@ -233,20 +270,11 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return BluetoothError.ERROR;
}
String path = lookupPath(address);
if (path == null) {
path = createHeadsetNative(address);
if (DBG) log("new bluez sink: " + address + " (" + path + ")");
}
if (path == null) {
if (mAudioDevices.get(address) == null && !addAudioSink(address))
return BluetoothError.ERROR;
}
SinkState sink = mAudioDevices.get(path);
int state = BluetoothA2dp.STATE_DISCONNECTED;
if (sink != null) {
state = sink.state;
}
int state = mAudioDevices.get(address);
switch (state) {
case BluetoothA2dp.STATE_CONNECTED:
case BluetoothA2dp.STATE_PLAYING:
@@ -256,11 +284,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return BluetoothError.SUCCESS;
}
String path = mBluetoothService.getObjectPathFromAddress(address);
if (path == null)
return BluetoothError.ERROR;
// State is DISCONNECTED
if (!connectSinkNative(path)) {
return BluetoothError.ERROR;
}
updateState(path, BluetoothA2dp.STATE_CONNECTING);
return BluetoothError.SUCCESS;
}
@@ -271,14 +302,12 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return BluetoothError.ERROR;
}
if (mAudioDevices == null) {
return BluetoothError.ERROR;
}
String path = lookupPath(address);
String path = mBluetoothService.getObjectPathFromAddress(address);
if (path == null) {
return BluetoothError.ERROR;
}
switch (mAudioDevices.get(path).state) {
switch (getSinkState(address)) {
case BluetoothA2dp.STATE_DISCONNECTED:
return BluetoothError.ERROR;
case BluetoothA2dp.STATE_DISCONNECTING:
@@ -289,7 +318,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (!disconnectSinkNative(path)) {
return BluetoothError.ERROR;
} else {
updateState(path, BluetoothA2dp.STATE_DISCONNECTING);
return BluetoothError.SUCCESS;
}
}
@@ -305,15 +333,10 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return BluetoothError.ERROR;
}
if (mAudioDevices == null) {
Integer state = mAudioDevices.get(address);
if (state == null)
return BluetoothA2dp.STATE_DISCONNECTED;
}
for (SinkState sink : mAudioDevices.values()) {
if (address.equals(sink.address)) {
return sink.state;
}
}
return BluetoothA2dp.STATE_DISCONNECTED;
return state;
}
public synchronized int getSinkPriority(String address) {
@@ -337,135 +360,42 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
BluetoothError.SUCCESS : BluetoothError.ERROR;
}
private synchronized void onHeadsetCreated(String path) {
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
}
private synchronized void onHeadsetRemoved(String path) {
if (mAudioDevices == null) return;
mAudioDevices.remove(path);
}
private synchronized void onSinkConnected(String path) {
// if we are reconnected, do not process previous disconnect event.
mPendingDisconnects.remove(path);
if (mAudioDevices == null) return;
// bluez 3.36 quietly disconnects the previous sink when a new sink
// is connected, so we need to mark all previously connected sinks as
// disconnected
// copy to allow modification during iteration
String[] paths = new String[mAudioDevices.size()];
paths = mAudioDevices.keySet().toArray(paths);
for (String oldPath : paths) {
if (path.equals(oldPath)) {
continue;
}
int state = mAudioDevices.get(oldPath).state;
if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) {
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
}
private synchronized void onSinkPropertyChanged(String path, String []propValues) {
String name = propValues[0];
String address = mBluetoothService.getAddressFromObjectPath(path);
if (address == null) {
Log.e(TAG, "onSinkPropertyChanged: Address of the remote device in null");
return;
}
updateState(path, BluetoothA2dp.STATE_CONNECTING);
mAudioManager.setParameter(A2DP_SINK_ADDRESS, lookupAddress(path));
mAudioManager.setBluetoothA2dpOn(true);
updateState(path, BluetoothA2dp.STATE_CONNECTED);
}
private synchronized void onSinkDisconnected(String path) {
// This is to work around a problem in bluez that results
// sink disconnect events being sent, immediately followed by a reconnect.
// To avoid unnecessary audio routing changes, we defer handling
// sink disconnects until after a short delay.
mPendingDisconnects.add(path);
Message msg = Message.obtain(mHandler, MESSAGE_DISCONNECT, path);
mHandler.sendMessageDelayed(msg, 2000);
}
private synchronized void handleDeferredDisconnect(String path) {
if (mPendingDisconnects.contains(path)) {
mPendingDisconnects.remove(path);
if (mSinkCount == 1) {
mAudioManager.setBluetoothA2dpOn(false);
}
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
if (mAudioDevices.get(address) == null) {
// Ignore this state change, since it means we have got it after
// bluetooth has been disabled.
return;
}
if (name.equals(PROPERTY_STATE)) {
int state = convertBluezSinkStringtoState(propValues[1]);
int prevState = mAudioDevices.get(address);
handleSinkStateChange(address, prevState, state);
}
}
private synchronized void onSinkPlaying(String path) {
updateState(path, BluetoothA2dp.STATE_PLAYING);
}
private synchronized void onSinkStopped(String path) {
updateState(path, BluetoothA2dp.STATE_CONNECTED);
}
private synchronized final String lookupAddress(String path) {
if (mAudioDevices == null) return null;
SinkState sink = mAudioDevices.get(path);
if (sink == null) {
Log.w(TAG, "lookupAddress() called for unknown device " + path);
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
}
String address = mAudioDevices.get(path).address;
if (address == null) Log.e(TAG, "Can't find address for " + path);
return address;
}
private synchronized final String lookupPath(String address) {
if (mAudioDevices == null) return null;
for (String path : mAudioDevices.keySet()) {
if (address.equals(mAudioDevices.get(path).address)) {
return path;
}
}
return null;
}
private synchronized List<String> lookupSinksMatchingStates(int[] states) {
List<String> sinks = new ArrayList<String>();
if (mAudioDevices == null) {
return sinks;
}
for (SinkState sink : mAudioDevices.values()) {
for (int state : states) {
if (sink.state == state) {
sinks.add(sink.address);
break;
}
}
}
return sinks;
}
private synchronized void updateState(String path, int state) {
if (mAudioDevices == null) return;
SinkState s = mAudioDevices.get(path);
int prevState;
String address;
if (s == null) {
address = getAddressNative(path);
mAudioDevices.put(path, new SinkState(address, state));
prevState = BluetoothA2dp.STATE_DISCONNECTED;
} else {
address = lookupAddress(path);
prevState = s.state;
s.state = state;
}
private void handleSinkStateChange(String address, int prevState, int state) {
if (state != prevState) {
if (DBG) log("state " + address + " (" + path + ") " + prevState + "->" + state);
// keep track of the number of active sinks
if (prevState == BluetoothA2dp.STATE_DISCONNECTED) {
mSinkCount++;
} else if (state == BluetoothA2dp.STATE_DISCONNECTED) {
mSinkCount--;
if (state == BluetoothA2dp.STATE_DISCONNECTED ||
state == BluetoothA2dp.STATE_DISCONNECTING) {
if (prevState == BluetoothA2dp.STATE_CONNECTED ||
prevState == BluetoothA2dp.STATE_PLAYING) {
// disconnecting or disconnected
Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
mContext.sendBroadcast(intent);
}
if (--mSinkCount == 0)
mAudioManager.setBluetoothA2dpOn(false);
} else if (state == BluetoothA2dp.STATE_CONNECTED) {
mSinkCount ++;
}
mAudioDevices.put(address, state);
Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
@@ -473,25 +403,40 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
intent.putExtra(BluetoothA2dp.SINK_STATE, state);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
if ((prevState == BluetoothA2dp.STATE_CONNECTED ||
prevState == BluetoothA2dp.STATE_PLAYING) &&
(state != BluetoothA2dp.STATE_CONNECTING &&
state != BluetoothA2dp.STATE_CONNECTED &&
state != BluetoothA2dp.STATE_PLAYING)) {
// disconnected
intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
mContext.sendBroadcast(intent);
if (DBG) log("A2DP state : address: " + address + " State:" + prevState + "->" + state);
if (state == BluetoothA2dp.STATE_CONNECTED) {
mAudioManager.setParameter(A2DP_SINK_ADDRESS, address);
mAudioManager.setBluetoothA2dpOn(true);
}
}
}
private synchronized List<String> lookupSinksMatchingStates(int[] states) {
List<String> sinks = new ArrayList<String>();
if (mAudioDevices.isEmpty()) {
return sinks;
}
for (String path: mAudioDevices.keySet()) {
int sinkState = getSinkState(path);
for (int state : states) {
if (state == sinkState) {
sinks.add(path);
break;
}
}
}
return sinks;
}
@Override
protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mAudioDevices == null) return;
if (mAudioDevices.isEmpty()) return;
pw.println("Cached audio devices:");
for (String path : mAudioDevices.keySet()) {
SinkState sink = mAudioDevices.get(path);
pw.println(path + " " + sink.address + " " + BluetoothA2dp.stateToString(sink.state));
for (String address : mAudioDevices.keySet()) {
int state = mAudioDevices.get(address);
pw.println(address + " " + BluetoothA2dp.stateToString(state));
}
}
@@ -501,11 +446,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private native boolean initNative();
private native void cleanupNative();
private synchronized native String[] listHeadsetsNative();
private synchronized native String createHeadsetNative(String address);
private synchronized native boolean removeHeadsetNative(String path);
private synchronized native String getAddressNative(String path);
private synchronized native boolean connectSinkNative(String path);
private synchronized native boolean disconnectSinkNative(String path);
private synchronized native boolean isSinkConnectedNative(String path);
private synchronized native Object []getSinkPropertiesNative(String path);
}

View File

@@ -30,7 +30,6 @@ import android.bluetooth.BluetoothError;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothIntent;
import android.bluetooth.IBluetoothDevice;
import android.bluetooth.IBluetoothDeviceCallback;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -38,7 +37,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -46,6 +44,8 @@ import android.os.SystemService;
import android.provider.Settings;
import android.util.Log;
import com.android.internal.app.IBatteryStats;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
@@ -55,8 +55,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.android.internal.app.IBatteryStats;
public class BluetoothDeviceService extends IBluetoothDevice.Stub {
private static final String TAG = "BluetoothDeviceService";
private static final boolean DBG = true;
@@ -80,10 +78,12 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
private static final int MESSAGE_FINISH_DISABLE = 2;
private Map<String, String> mProperties;
private HashMap <String, Map<String, String>> mRemoteDeviceProperties;
static {
classInitNative();
}
private native static void classInitNative();
public BluetoothDeviceService(Context context) {
mContext = context;
@@ -109,8 +109,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
mIsDiscovering = false;
mEventLoop = new BluetoothEventLoop(mContext, this);
registerForAirplaneMode();
mProperties = new HashMap<String, String>();
mRemoteDeviceProperties = new HashMap<String, Map<String,String>>();
}
private native void initializeNativeDataNative();
@Override
protected void finalize() throws Throwable {
@@ -123,13 +124,11 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
super.finalize();
}
}
private native void cleanupNativeDataNative();
public boolean isEnabled() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return mBluetoothState == BluetoothDevice.BLUETOOTH_STATE_ON;
}
private native int isEnabledNative();
public int getBluetoothState() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
@@ -150,8 +149,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
* @param saveSetting If true, disable BT in settings
*/
public synchronized boolean disable(boolean saveSetting) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
switch (mBluetoothState) {
case BluetoothDevice.BLUETOOTH_STATE_OFF:
@@ -180,6 +178,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
return;
}
mEventLoop.stop();
tearDownNativeDataNative();
disableNative();
// mark in progress bondings as cancelled
@@ -188,25 +187,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
}
// Remove remoteServiceChannelCallbacks
HashMap<String, IBluetoothDeviceCallback> callbacksMap =
mEventLoop.getRemoteServiceChannelCallbacks();
for (Iterator<String> i = callbacksMap.keySet().iterator(); i.hasNext();) {
String address = i.next();
IBluetoothDeviceCallback callback = callbacksMap.get(address);
try {
callback.onGetRemoteServiceChannelResult(address, BluetoothError.ERROR_DISABLED);
} catch (RemoteException e) {}
i.remove();
}
// update mode
Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mIsDiscovering = false;
mProperties.clear();
if (saveSetting) {
persistBluetoothOnSetting(false);
@@ -270,7 +257,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
if (!disable(false)) {
mRestart = false;
}
}
}
private synchronized void setBluetoothState(int state) {
if (state == mBluetoothState) {
@@ -343,6 +330,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
if (res) {
if (!setupNativeDataNative()) {
return;
}
if (mSaveSetting) {
persistBluetoothOnSetting(true);
}
@@ -369,7 +359,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
if (res) {
// Update mode
mEventLoop.onModeChanged(getModeNative());
String[] propVal = {"Pairable", getProperty("Pairable")};
mEventLoop.onPropertyChanged(propVal);
}
if (mIsAirplaneSensitive && isAirplaneModeOn()) {
@@ -386,9 +377,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
Binder.restoreCallingIdentity(origCallerIdentityToken);
}
private native int enableNative();
private native int disableNative();
/* package */ BondState getBondState() {
return mBondState;
}
@@ -423,14 +411,19 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_ON) {
return;
}
String[] bonds = listBondingsNative();
String []bonds = null;
String val = getProperty("Devices");
if (val != null) {
bonds = val.split(",");
}
if (bonds == null) {
return;
}
mState.clear();
if (DBG) log("found " + bonds.length + " bonded devices");
for (String address : bonds) {
mState.put(address.toUpperCase(), BluetoothDevice.BOND_BONDED);
for (String device : bonds) {
mState.put(getAddressFromObjectPath(device).toUpperCase(),
BluetoothDevice.BOND_BONDED);
}
}
@@ -528,7 +521,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
}
}
private native String[] listBondingsNative();
private static String toBondStateString(int bondState) {
switch (bondState) {
@@ -543,17 +535,36 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
}
}
public synchronized String getAddress() {
/*package*/synchronized void getAllProperties() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getAddressNative();
}
private native String getAddressNative();
mProperties.clear();
public synchronized String getName() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getNameNative();
String properties[] = (String [])getAdapterPropertiesNative();
// The String Array consists of key-value pairs.
if (properties == null) {
Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
return;
}
for (int i = 0; i < properties.length; i+=2) {
String value = null;
if (mProperties.containsKey(properties[i])) {
value = mProperties.get(properties[i]);
value = value + ',' + properties[i+1];
} else
value = properties[i+1];
mProperties.put(properties[i], value);
}
// Add adapter object path property.
String adapterPath = getAdapterPathNative();
if (adapterPath != null)
mProperties.put("ObjectPath", adapterPath + "/dev_");
}
/* package */ synchronized void setProperty(String name, String value) {
mProperties.put(name, value);
}
private native String getNameNative();
public synchronized boolean setName(String name) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -561,74 +572,27 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
if (name == null) {
return false;
}
// hcid handles persistance of the bluetooth name
return setNameNative(name);
return setPropertyString("Name", name);
}
private native boolean setNameNative(String name);
/**
* Returns the user-friendly name of a remote device. This value is
* retrned from our local cache, which is updated during device discovery.
* Do not expect to retrieve the updated remote name immediately after
* changing the name on the remote device.
*
* @param address Bluetooth address of remote device.
*
* @return The user-friendly name of the specified remote device.
*/
public synchronized String getRemoteName(String address) {
//TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
// Either have a single property function with Object as the parameter
// or have a function for each property and then obfuscate in the JNI layer.
// The following looks dirty.
private boolean setPropertyString(String key, String value) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
}
return getRemoteNameNative(address);
return setAdapterPropertyStringNative(key, value);
}
private native String getRemoteNameNative(String address);
/* pacakge */ native String getAdapterPathNative();
public synchronized boolean startDiscovery(boolean resolveNames) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
return startDiscoveryNative(resolveNames);
}
private native boolean startDiscoveryNative(boolean resolveNames);
public synchronized boolean cancelDiscovery() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
return cancelDiscoveryNative();
}
private native boolean cancelDiscoveryNative();
public synchronized boolean isDiscovering() {
private boolean setPropertyInteger(String key, int value) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return mIsDiscovering;
return setAdapterPropertyIntegerNative(key, value);
}
/* package */ void setIsDiscovering(boolean isDiscovering) {
mIsDiscovering = isDiscovering;
}
public synchronized boolean startPeriodicDiscovery() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
return startPeriodicDiscoveryNative();
}
private native boolean startPeriodicDiscoveryNative();
public synchronized boolean stopPeriodicDiscovery() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
return stopPeriodicDiscoveryNative();
}
private native boolean stopPeriodicDiscoveryNative();
public synchronized boolean isPeriodicDiscovery() {
private boolean setPropertyBoolean(String key, boolean value) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return isPeriodicDiscoveryNative();
return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
}
private native boolean isPeriodicDiscoveryNative();
/**
* Set the discoverability window for the device. A timeout of zero
@@ -642,9 +606,67 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
public synchronized boolean setDiscoverableTimeout(int timeout) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
return setDiscoverableTimeoutNative(timeout);
return setPropertyInteger("DiscoverableTimeout", timeout);
}
public synchronized boolean setScanMode(int mode) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
boolean pairable = false, discoverable = false;
String modeString = scanModeToBluezString(mode);
if (modeString.equals("off")) {
pairable = false;
discoverable = false;
} else if (modeString.equals("pariable")) {
pairable = true;
discoverable = false;
} else if (modeString.equals("discoverable")) {
pairable = true;
discoverable = true;
}
setPropertyBoolean("Pairable", pairable);
setPropertyBoolean("Discoverable", discoverable);
return true;
}
/*package*/ synchronized String getProperty (String name) {
if (!mProperties.isEmpty())
return mProperties.get(name);
getAllProperties();
return mProperties.get(name);
}
public synchronized String getAddress() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getProperty("Address");
}
public synchronized String getName() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getProperty("Name");
}
/**
* Returns the user-friendly name of a remote device. This value is
* returned from our local cache, which is updated when onPropertyChange
* event is received.
* Do not expect to retrieve the updated remote name immediately after
* changing the name on the remote device.
*
* @param address Bluetooth address of remote device.
*
* @return The user-friendly name of the specified remote device.
*/
public synchronized String getRemoteName(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
}
Map <String, String> properties = mRemoteDeviceProperties.get(address);
if (properties != null) return properties.get("Name");
return null;
}
private native boolean setDiscoverableTimeoutNative(int timeout_s);
/**
* Get the discoverability window for the device. A timeout of zero
@@ -656,45 +678,46 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
*/
public synchronized int getDiscoverableTimeout() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getDiscoverableTimeoutNative();
String timeout = getProperty("DiscoverableTimeout");
if (timeout != null)
return Integer.valueOf(timeout);
else
return -1;
}
private native int getDiscoverableTimeoutNative();
public synchronized boolean isAclConnected(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return false;
}
return isConnectedNative(address);
}
private native boolean isConnectedNative(String address);
public synchronized int getScanMode() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return bluezStringToScanMode(getModeNative());
}
private native String getModeNative();
if (!isEnabled())
return BluetoothError.ERROR;
public synchronized boolean setScanMode(int mode) {
boolean pairable = getProperty("Pairable").equals("true");
boolean discoverable = getProperty("Discoverable").equals("true");
return bluezStringToScanMode (pairable, discoverable);
}
public synchronized boolean startDiscovery() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
String bluezMode = scanModeToBluezString(mode);
if (bluezMode != null) {
return setModeNative(bluezMode);
}
return false;
}
private native boolean setModeNative(String mode);
public synchronized boolean disconnectRemoteDeviceAcl(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
if (!isEnabled()) {
return false;
}
return disconnectRemoteDeviceNative(address);
return startDiscoveryNative();
}
public synchronized boolean cancelDiscovery() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
return stopDiscoveryNative();
}
public synchronized boolean isDiscovering() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return mIsDiscovering;
}
/* package */ void setIsDiscovering(boolean isDiscovering) {
mIsDiscovering = isDiscovering;
}
private native boolean disconnectRemoteDeviceNative(String address);
public synchronized boolean createBond(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -719,14 +742,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
return false;
}
if (!createBondingNative(address, 60000 /* 1 minute */)) {
if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
return false;
}
mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
return true;
}
private native boolean createBondingNative(String address, int timeout_ms);
public synchronized boolean cancelBondProcess(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -741,10 +763,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
cancelBondingProcessNative(address);
cancelDeviceCreationNative(address);
return true;
}
private native boolean cancelBondingProcessNative(String address);
public synchronized boolean removeBond(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -752,9 +773,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return false;
}
return removeBondingNative(address);
return removeDeviceNative(getObjectPathFromAddress(address));
}
private native boolean removeBondingNative(String address);
public synchronized String[] listBonds() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
@@ -769,198 +789,57 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
return mBondState.getBondState(address.toUpperCase());
}
public synchronized String[] listAclConnections() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return listConnectionsNative();
}
private native String[] listConnectionsNative();
/**
* This method lists all remote devices that this adapter is aware of.
* This is a list not only of all most-recently discovered devices, but of
* all devices discovered by this adapter up to some point in the past.
* Note that many of these devices may not be in the neighborhood anymore,
* and attempting to connect to them will result in an error.
*
* @return An array of strings representing the Bluetooth addresses of all
* remote devices that this adapter is aware of.
*/
public synchronized String[] listRemoteDevices() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return listRemoteDevicesNative();
}
private native String[] listRemoteDevicesNative();
/**
* Returns the version of the Bluetooth chip. This version is compiled from
* the LMP version. In case of EDR the features attribute must be checked.
* Example: "Bluetooth 2.0 + EDR".
*
* @return a String representation of the this Adapter's underlying
* Bluetooth-chip version.
*/
public synchronized String getVersion() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getVersionNative();
}
private native String getVersionNative();
/**
* Returns the revision of the Bluetooth chip. This is a vendor-specific
* value and in most cases it represents the firmware version. This might
* derive from the HCI revision and LMP subversion values or via extra
* vendord specific commands.
* In case the revision of a chip is not available. This method should
* return the LMP subversion value as a string.
* Example: "HCI 19.2"
*
* @return The HCI revision of this adapter.
*/
public synchronized String getRevision() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getRevisionNative();
}
private native String getRevisionNative();
/**
* Returns the manufacturer of the Bluetooth chip. If the company id is not
* known the sting "Company ID %d" where %d should be replaced with the
* numeric value from the manufacturer field.
* Example: "Cambridge Silicon Radio"
*
* @return Manufacturer name.
*/
public synchronized String getManufacturer() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getManufacturerNative();
}
private native String getManufacturerNative();
/**
* Returns the company name from the OUI database of the Bluetooth device
* address. This function will need a valid and up-to-date oui.txt from
* the IEEE. This value will be different from the manufacturer string in
* the most cases.
* If the oui.txt file is not present or the OUI part of the Bluetooth
* address is not listed, it should return the string "OUI %s" where %s is
* the actual OUI.
*
* Example: "Apple Computer"
*
* @return company name
*/
public synchronized String getCompany() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return getCompanyNative();
}
private native String getCompanyNative();
/**
* Like getVersion(), but for a remote device.
*
* @param address The Bluetooth address of the remote device.
*
* @return remote-device Bluetooth version
*
* @see #getVersion
*/
public synchronized String getRemoteVersion(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
/*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
Map<String, String> properties = mRemoteDeviceProperties.get(address);
if (properties != null) {
return properties.get(property);
} else {
// Query for remote device properties, again.
// We will need to reload the cache when we switch Bluetooth on / off
// or if we crash.
String objectPath = getObjectPathFromAddress(address);
String propValues[] = (String [])getDevicePropertiesNative(objectPath);
if (propValues != null) {
addRemoteDeviceProperties(address, propValues);
return getRemoteDeviceProperty(address, property);
}
}
return getRemoteVersionNative(address);
Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
return null;
}
private native String getRemoteVersionNative(String address);
/**
* Like getRevision(), but for a remote device.
*
* @param address The Bluetooth address of the remote device.
*
* @return remote-device HCI revision
*
* @see #getRevision
*/
public synchronized String getRemoteRevision(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
/* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
Map<String, String> propertyValues = new HashMap<String, String>();
for (int i = 0; i < properties.length; i+=2) {
String value = null;
if (propertyValues.containsKey(properties[i])) {
value = propertyValues.get(properties[i]);
value = value + ',' + properties[i+1];
} else {
value = properties[i+1];
}
propertyValues.put(properties[i], value);
}
return getRemoteRevisionNative(address);
mRemoteDeviceProperties.put(address, propertyValues);
}
private native String getRemoteRevisionNative(String address);
/**
* Like getManufacturer(), but for a remote device.
*
* @param address The Bluetooth address of the remote device.
*
* @return remote-device Bluetooth chip manufacturer
*
* @see #getManufacturer
*/
public synchronized String getRemoteManufacturer(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
/* package */ void removeRemoteDeviceProperties(String address) {
mRemoteDeviceProperties.remove(address);
}
/* package */ synchronized void setRemoteDeviceProperty(String address, String name,
String value) {
Map <String, String> propVal = mRemoteDeviceProperties.get(address);
if (propVal != null) {
propVal.put(name, value);
mRemoteDeviceProperties.put(address, propVal);
} else {
Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
}
return getRemoteManufacturerNative(address);
}
private native String getRemoteManufacturerNative(String address);
/**
* Like getCompany(), but for a remote device.
*
* @param address The Bluetooth address of the remote device.
*
* @return remote-device company
*
* @see #getCompany
*/
public synchronized String getRemoteCompany(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
}
return getRemoteCompanyNative(address);
}
private native String getRemoteCompanyNative(String address);
/**
* Returns the date and time when the specified remote device has been seen
* by a discover procedure.
* Example: "2006-02-08 12:00:00 GMT"
*
* @return a String with the timestamp.
*/
public synchronized String lastSeen(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
}
return lastSeenNative(address);
}
private native String lastSeenNative(String address);
/**
* Returns the date and time when the specified remote device has last been
* connected to
* Example: "2006-02-08 12:00:00 GMT"
*
* @return a String with the timestamp.
*/
public synchronized String lastUsed(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
}
return lastUsedNative(address);
}
private native String lastUsedNative(String address);
/**
* Gets the remote major, minor, and service classes encoded as a 32-bit
* Gets the remote major, minor classes encoded as a 32-bit
* integer.
*
* Note: this value is retrieved from cache, because we get it during
@@ -968,120 +847,56 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
*
* @return 32-bit integer encoding the remote major, minor, and service
* classes.
*
* @see #getRemoteMajorClass
* @see #getRemoteMinorClass
* @see #getRemoteServiceClasses
*/
public synchronized int getRemoteClass(String address) {
if (!BluetoothDevice.checkBluetoothAddress(address)) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return BluetoothClass.ERROR;
}
return getRemoteClassNative(address);
String val = getRemoteDeviceProperty(address, "Class");
if (val == null)
return BluetoothClass.ERROR;
else {
return Integer.valueOf(val);
}
}
private native int getRemoteClassNative(String address);
/**
* Gets the remote features encoded as bit mask.
*
* Note: This method may be obsoleted soon.
*
* @return byte array of features.
* @return String array of 128bit UUIDs
*/
public synchronized byte[] getRemoteFeatures(String address) {
public synchronized String[] getRemoteUuids(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
}
return getRemoteFeaturesNative(address);
String value = getRemoteDeviceProperty(address, "UUIDs");
String[] uuids = null;
// The UUIDs are stored as a "," separated string.
if (value != null)
uuids = value.split(",");
return uuids;
}
private native byte[] getRemoteFeaturesNative(String address);
/**
* This method and {@link #getRemoteServiceRecord} query the SDP service
* on a remote device. They do not interpret the data, but simply return
* it raw to the user. To read more about SDP service handles and records,
* consult the Bluetooth core documentation (www.bluetooth.com).
* Gets the rfcomm channel associated with the UUID.
*
* @param address Bluetooth address of remote device.
* @param match a String match to narrow down the service-handle search.
* The only supported value currently is "hsp" for the headset
* profile. To retrieve all service handles, simply pass an empty
* match string.
* @param address Address of the remote device
* @param uuid UUID of the service attribute
*
* @return all service handles corresponding to the string match.
*
* @see #getRemoteServiceRecord
* @return rfcomm channel associated with the service attribute
*/
public synchronized int[] getRemoteServiceHandles(String address, String match) {
public int getRemoteServiceChannel(String address, String uuid) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
return BluetoothError.ERROR_IPC;
}
if (match == null) {
match = "";
}
return getRemoteServiceHandlesNative(address, match);
return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid, 0x0004);
}
private native int[] getRemoteServiceHandlesNative(String address, String match);
/**
* This method retrieves the service records corresponding to a given
* service handle (method {@link #getRemoteServiceHandles} retrieves the
* service handles.)
*
* This method and {@link #getRemoteServiceHandles} do not interpret their
* data, but simply return it raw to the user. To read more about SDP
* service handles and records, consult the Bluetooth core documentation
* (www.bluetooth.com).
*
* @param address Bluetooth address of remote device.
* @param handle Service handle returned by {@link #getRemoteServiceHandles}
*
* @return a byte array of all service records corresponding to the
* specified service handle.
*
* @see #getRemoteServiceHandles
*/
public synchronized byte[] getRemoteServiceRecord(String address, int handle) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return null;
}
return getRemoteServiceRecordNative(address, handle);
}
private native byte[] getRemoteServiceRecordNative(String address, int handle);
private static final int MAX_OUTSTANDING_ASYNC = 32;
// AIDL does not yet support short's
public synchronized boolean getRemoteServiceChannel(String address, int uuid16,
IBluetoothDeviceCallback callback) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return false;
}
HashMap<String, IBluetoothDeviceCallback> callbacks =
mEventLoop.getRemoteServiceChannelCallbacks();
if (callbacks.containsKey(address)) {
Log.w(TAG, "SDP request already in progress for " + address);
return false;
}
// Protect from malicious clients - only allow 32 bonding requests per minute.
if (callbacks.size() > MAX_OUTSTANDING_ASYNC) {
Log.w(TAG, "Too many outstanding SDP requests, dropping request for " + address);
return false;
}
callbacks.put(address, callback);
if (!getRemoteServiceChannelNative(address, (short)uuid16)) {
callbacks.remove(address);
return false;
}
return true;
}
private native boolean getRemoteServiceChannelNative(String address, short uuid16);
public synchronized boolean setPin(String address, byte[] pin) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -1108,7 +923,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
}
return setPinNative(address, pinString, data.intValue());
}
private native boolean setPinNative(String address, String pin, int nativeData);
public synchronized boolean cancelPin(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -1126,7 +940,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
}
return cancelPinNative(address, data.intValue());
}
private native boolean cancelPinNative(String address, int natveiData);
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -1190,20 +1003,22 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
String[] addresses = listRemoteDevices();
pw.println("\n--Known devices--");
for (String address : addresses) {
for (String address : mRemoteDeviceProperties.keySet()) {
pw.printf("%s %10s (%d) %s\n", address,
toBondStateString(mBondState.getBondState(address)),
mBondState.getAttempt(address),
getRemoteName(address));
}
addresses = listAclConnections();
String value = getProperty("Devices");
String []devicesObjectPath = null;
if (value != null) {
devicesObjectPath = value.split(",");
}
pw.println("\n--ACL connected devices--");
for (String address : addresses) {
pw.println(address);
for (String device : devicesObjectPath) {
pw.println(getAddressFromObjectPath(device));
}
// Rather not do this from here, but no-where else and I need this
@@ -1227,20 +1042,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
headset.close();
}
/* package */ static int bluezStringToScanMode(String mode) {
if (mode == null) {
return BluetoothError.ERROR;
}
mode = mode.toLowerCase();
if (mode.equals("off")) {
return BluetoothDevice.SCAN_MODE_NONE;
} else if (mode.equals("connectable")) {
return BluetoothDevice.SCAN_MODE_CONNECTABLE;
} else if (mode.equals("discoverable")) {
/* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
if (pairable && discoverable)
return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
} else {
return BluetoothError.ERROR;
}
else if (pairable && !discoverable)
return BluetoothDevice.SCAN_MODE_CONNECTABLE;
else
return BluetoothDevice.SCAN_MODE_NONE;
}
/* package */ static String scanModeToBluezString(int mode) {
@@ -1255,7 +1063,67 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
return null;
}
/*package*/ String getAddressFromObjectPath(String objectPath) {
String adapterObjectPath = getProperty("ObjectPath");
if (adapterObjectPath == null || objectPath == null) {
Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
" or deviceObjectPath:" + objectPath + " is null");
return null;
}
if (!objectPath.startsWith(adapterObjectPath)) {
Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
" is not a prefix of deviceObjectPath:" + objectPath +
"bluetoothd crashed ?");
return null;
}
String address = objectPath.substring(adapterObjectPath.length());
if (address != null) return address.replace('_', ':');
Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
return null;
}
/*package*/ String getObjectPathFromAddress(String address) {
String path = getProperty("ObjectPath");
if (path == null) {
Log.e(TAG, "Error: Object Path is null");
return null;
}
path = path + address.replace(":", "_");
return path;
}
private static void log(String msg) {
Log.d(TAG, msg);
}
private native static void classInitNative();
private native void initializeNativeDataNative();
private native boolean setupNativeDataNative();
private native boolean tearDownNativeDataNative();
private native void cleanupNativeDataNative();
private native String getAdapterPathNative();
private native int isEnabledNative();
private native int enableNative();
private native int disableNative();
private native Object[] getAdapterPropertiesNative();
private native Object[] getDevicePropertiesNative(String objectPath);
private native boolean setAdapterPropertyStringNative(String key, String value);
private native boolean setAdapterPropertyIntegerNative(String key, int value);
private native boolean setAdapterPropertyBooleanNative(String key, int value);
private native boolean startDiscoveryNative();
private native boolean stopDiscoveryNative();
private native boolean createPairedDeviceNative(String address, int timeout_ms);
private native boolean cancelDeviceCreationNative(String address);
private native boolean removeDeviceNative(String objectPath);
private native int getDeviceServiceChannelNative(String objectPath, String uuid,
int attributeId);
private native boolean cancelPinNative(String address, int nativeData);
private native boolean setPinNative(String address, String pin, int nativeData);
}

View File

@@ -21,15 +21,15 @@ import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothError;
import android.bluetooth.BluetoothIntent;
import android.bluetooth.IBluetoothDeviceCallback;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import java.util.HashMap;
import java.util.UUID;
/**
* TODO: Move this to
@@ -47,7 +47,6 @@ class BluetoothEventLoop {
private boolean mStarted;
private boolean mInterrupted;
private final HashMap<String, Integer> mPasskeyAgentRequestData;
private final HashMap<String, IBluetoothDeviceCallback> mGetRemoteServiceChannelCallbacks;
private final BluetoothDeviceService mBluetoothService;
private final Context mContext;
@@ -89,10 +88,8 @@ class BluetoothEventLoop {
mBluetoothService = bluetoothService;
mContext = context;
mPasskeyAgentRequestData = new HashMap();
mGetRemoteServiceChannelCallbacks = new HashMap();
initializeNativeDataNative();
}
private native void initializeNativeDataNative();
protected void finalize() throws Throwable {
try {
@@ -101,20 +98,11 @@ class BluetoothEventLoop {
super.finalize();
}
}
private native void cleanupNativeDataNative();
/* pacakge */ HashMap<String, IBluetoothDeviceCallback> getRemoteServiceChannelCallbacks() {
return mGetRemoteServiceChannelCallbacks;
}
/* pacakge */ HashMap<String, Integer> getPasskeyAgentRequestData() {
/* package */ HashMap<String, Integer> getPasskeyAgentRequestData() {
return mPasskeyAgentRequestData;
}
private native void startEventLoopNative();
private native void stopEventLoopNative();
private native boolean isEventLoopRunningNative();
/* package */ void start() {
if (!isEventLoopRunningNative()) {
@@ -134,79 +122,37 @@ class BluetoothEventLoop {
return isEventLoopRunningNative();
}
/*package*/ void onModeChanged(String bluezMode) {
int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode);
if (mode >= 0) {
Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
intent.putExtra(BluetoothIntent.SCAN_MODE, mode);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
private void onDeviceFound(String address, String[] properties) {
if (properties == null) {
Log.e(TAG, "ERROR: Remote device properties are null");
return;
}
mBluetoothService.addRemoteDeviceProperties(address, properties);
String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI");
String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class");
if (rssi != null && classValue != null) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
intent.putExtra(BluetoothIntent.CLASS, classValue);
intent.putExtra(BluetoothIntent.RSSI, (short)Integer.valueOf(rssi).intValue());
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
} else {
log ("RSSI: " + rssi + " or ClassValue: " + classValue +
" for remote device: " + address + " is null");
}
}
private void onDiscoveryStarted() {
mBluetoothService.setIsDiscovering(true);
Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onDiscoveryCompleted() {
mBluetoothService.setIsDiscovering(false);
Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onDeviceDisappeared(String address) {
mBluetoothService.removeRemoteDeviceProperties(address);
private void onRemoteDeviceFound(String address, int deviceClass, short rssi) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
intent.putExtra(BluetoothIntent.CLASS, deviceClass);
intent.putExtra(BluetoothIntent.RSSI, rssi);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onRemoteDeviceDisappeared(String address) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onRemoteClassUpdated(String address, int deviceClass) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
intent.putExtra(BluetoothIntent.CLASS, deviceClass);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onRemoteDeviceConnected(String address) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onRemoteDeviceDisconnectRequested(String address) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onRemoteDeviceDisconnected(String address) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onRemoteNameUpdated(String address, String name) {
Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
intent.putExtra(BluetoothIntent.NAME, name);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onRemoteNameFailed(String address) {
Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onRemoteNameChanged(String address, String name) {
Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
intent.putExtra(BluetoothIntent.NAME, name);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
private void onCreateBondingResult(String address, int result) {
private void onCreatePairedDeviceResult(String address, int result) {
address = address.toUpperCase();
if (result == BluetoothError.SUCCESS) {
mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
@@ -259,23 +205,118 @@ class BluetoothEventLoop {
mBluetoothService.getBondState().attempt(address);
}
private void onBondingCreated(String address) {
mBluetoothService.getBondState().setBondState(address.toUpperCase(),
BluetoothDevice.BOND_BONDED);
private void onDeviceCreated(String deviceObjectPath) {
// do nothing.
return;
}
private void onBondingRemoved(String address) {
mBluetoothService.getBondState().setBondState(address.toUpperCase(),
BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED);
private void onDeviceRemoved(String deviceObjectPath) {
String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
if (address != null)
mBluetoothService.getBondState().setBondState(address.toUpperCase(),
BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED);
}
private void onNameChanged(String name) {
Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION);
intent.putExtra(BluetoothIntent.NAME, name);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
/*package*/ void onPropertyChanged(String[] propValues) {
String name = propValues[0];
if (name.equals("Name")) {
Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION);
intent.putExtra(BluetoothIntent.NAME, propValues[1]);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mBluetoothService.setProperty(name, propValues[1]);
} else if (name.equals("Pairable") || name.equals("Discoverable")) {
String pairable = name.equals("Pairable") ? propValues[1] :
mBluetoothService.getProperty("Pairable");
String discoverable = name.equals("Discoverable") ? propValues[1] :
mBluetoothService.getProperty("Discoverable");
// This shouldn't happen, unless Adapter Properties are null.
if (pairable == null || discoverable == null)
return;
int mode = BluetoothDeviceService.bluezStringToScanMode(
pairable.equals("true"),
discoverable.equals("true"));
if (mode >= 0) {
Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
intent.putExtra(BluetoothIntent.SCAN_MODE, mode);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
mBluetoothService.setProperty(name, propValues[1]);
} else if (name.equals("Discovering")) {
Intent intent;
if (propValues[1].equals("true")) {
mBluetoothService.setIsDiscovering(true);
intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION);
} else {
// Stop the discovery.
mBluetoothService.cancelDiscovery();
mBluetoothService.setIsDiscovering(false);
intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
}
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mBluetoothService.setProperty(name, propValues[1]);
} else if (name.equals("Devices")) {
String value = "";
for (int i = 1; i < propValues.length; i++) {
value = value + propValues[i] + ',';
}
mBluetoothService.setProperty(name, value.equals("") ? null : value);
} else if (name.equals("Powered")) {
// bluetoothd has restarted, re-read all our properties.
// Note: bluez only sends this property change when it restarts.
if (propValues[1].equals("true"))
onRestartRequired();
}
}
private void onPasskeyAgentRequest(String address, int nativeData) {
private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) {
String name = propValues[0];
String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
if (address == null) {
Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
return;
}
if (name.equals("Name")) {
Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
intent.putExtra(BluetoothIntent.NAME, propValues[1]);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
} else if (name.equals("Class")) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
intent.putExtra(BluetoothIntent.CLASS, propValues[1]);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
} else if (name.equals("Connected")) {
Intent intent = null;
if (propValues[1].equals("true")) {
intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
} else {
intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION);
}
intent.putExtra(BluetoothIntent.ADDRESS, address);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
} else if (name.equals("UUIDs")) {
String uuid = "" ;
for (int i = 1; i < propValues.length; i++) {
uuid = uuid + propValues[i] + ",";
}
mBluetoothService.setRemoteDeviceProperty(address, name,
uuid.equals("") ? null : uuid);
}
}
private void onRequestPinCode(String objectPath, int nativeData) {
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
if (address == null) {
Log.e(TAG, "Unable to get device address in onRequestPinCode, returning null");
return;
}
address = address.toUpperCase();
mPasskeyAgentRequestData.put(address, new Integer(nativeData));
@@ -309,21 +350,21 @@ class BluetoothEventLoop {
Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
return;
}
private void onPasskeyAgentCancel(String address) {
address = address.toUpperCase();
mBluetoothService.cancelPin(address);
Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
}
private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
if (address == null) {
Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
return false;
}
private boolean onAuthAgentAuthorize(String address, String service, String uuid) {
boolean authorized = false;
if (mBluetoothService.isEnabled() && service.endsWith("service_audio")) {
UUID uuid = UUID.fromString(deviceUuid);
if (mBluetoothService.isEnabled() && (BluetoothUuid.isAudioSink(uuid) ||
BluetoothUuid.isAudioSource(uuid) ||
BluetoothUuid.isAdvAudioDist(uuid))) {
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF;
if (authorized) {
@@ -332,30 +373,21 @@ class BluetoothEventLoop {
Log.i(TAG, "Rejecting incoming A2DP connection from " + address);
}
} else {
Log.i(TAG, "Rejecting incoming " + service + " connection from " + address);
Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
}
return authorized;
}
private void onAuthAgentCancel(String address, String service, String uuid) {
private void onAgentCancel() {
// We immediately response to DBUS Authorize() so this should not
// usually happen
log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")");
}
private void onGetRemoteServiceChannelResult(String address, int channel) {
IBluetoothDeviceCallback callback = mGetRemoteServiceChannelCallbacks.get(address);
if (callback != null) {
mGetRemoteServiceChannelCallbacks.remove(address);
try {
callback.onGetRemoteServiceChannelResult(address, channel);
} catch (RemoteException e) {}
}
log("onAgentCancel");
}
private void onRestartRequired() {
if (mBluetoothService.isEnabled()) {
Log.e(TAG, "*** A serious error occured (did hcid crash?) - restarting Bluetooth ***");
Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
"restarting Bluetooth ***");
mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
}
}
@@ -363,4 +395,10 @@ class BluetoothEventLoop {
private static void log(String msg) {
Log.d(TAG, msg);
}
private native void initializeNativeDataNative();
private native void startEventLoopNative();
private native void stopEventLoopNative();
private native boolean isEventLoopRunningNative();
private native void cleanupNativeDataNative();
}

View File

@@ -36,6 +36,38 @@
namespace android {
#ifdef HAVE_BLUETOOTH
static Properties remote_device_properties[] = {
{"Address", DBUS_TYPE_STRING},
{"Name", DBUS_TYPE_STRING},
{"Icon", DBUS_TYPE_STRING},
{"Class", DBUS_TYPE_UINT32},
{"UUIDs", DBUS_TYPE_ARRAY},
{"Paired", DBUS_TYPE_BOOLEAN},
{"Connected", DBUS_TYPE_BOOLEAN},
{"Trusted", DBUS_TYPE_BOOLEAN},
{"Alias", DBUS_TYPE_STRING},
{"Nodes", DBUS_TYPE_ARRAY},
{"Adapter", DBUS_TYPE_OBJECT_PATH},
{"LegacyPairing", DBUS_TYPE_BOOLEAN},
{"RSSI", DBUS_TYPE_INT16},
{"TX", DBUS_TYPE_UINT32}
};
static Properties adapter_properties[] = {
{"Address", DBUS_TYPE_STRING},
{"Name", DBUS_TYPE_STRING},
{"Class", DBUS_TYPE_UINT32},
{"Powered", DBUS_TYPE_BOOLEAN},
{"Discoverable", DBUS_TYPE_BOOLEAN},
{"DiscoverableTimeout", DBUS_TYPE_UINT32},
{"Pairable", DBUS_TYPE_BOOLEAN},
{"PairableTimeout", DBUS_TYPE_UINT32},
{"Discovering", DBUS_TYPE_BOOLEAN},
{"Devices", DBUS_TYPE_ARRAY},
};
jfieldID get_field(JNIEnv *env, jclass clazz, const char *member,
const char *mtype) {
jfieldID field = env->GetFieldID(clazz, member, mtype);
@@ -332,6 +364,44 @@ jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply) {
return ret;
}
static void set_object_array_element(JNIEnv *env, jobjectArray strArray,
const char *value, int index) {
jstring obj;
obj = env->NewStringUTF(value);
env->SetObjectArrayElement(strArray, index, obj);
env->DeleteLocalRef(obj);
}
jobjectArray dbus_returns_array_of_object_path(JNIEnv *env,
DBusMessage *reply) {
DBusError err;
char **list;
int i, len;
jobjectArray strArray = NULL;
dbus_error_init(&err);
if (dbus_message_get_args (reply,
&err,
DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
&list, &len,
DBUS_TYPE_INVALID)) {
jclass stringClass;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
strArray = env->NewObjectArray(len, stringClass, NULL);
for (i = 0; i < len; i++)
set_object_array_element(env, strArray, list[i], i);
} else {
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
}
dbus_message_unref(reply);
return strArray;
}
jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) {
DBusError err;
@@ -353,11 +423,8 @@ jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) {
stringClass = env->FindClass("java/lang/String");
strArray = env->NewObjectArray(len, stringClass, NULL);
for (i = 0; i < len; i++) {
//LOGV("%s: array[%d] = [%s]", __FUNCTION__, i, list[i]);
env->SetObjectArrayElement(strArray, i,
env->NewStringUTF(list[i]));
}
for (i = 0; i < len; i++)
set_object_array_element(env, strArray, list[i], i);
} else {
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
}
@@ -390,6 +457,292 @@ jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply) {
return byteArray;
}
void append_variant(DBusMessageIter *iter, int type, void *val)
{
DBusMessageIter value_iter;
char var_type[2] = { type, '\0'};
dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, var_type, &value_iter);
dbus_message_iter_append_basic(&value_iter, type, val);
dbus_message_iter_close_container(iter, &value_iter);
}
//TODO(): Remove code duplication between parse_properties and
//parse_property_change
jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties,
const int max_num_properties) {
DBusMessageIter dict_entry, dict, prop_val, device_val, array_val_iter;
jobjectArray strArray = NULL;
char * property;
char values[max_num_properties][256];
char **uuid_array = NULL;
char **device_path = NULL;
char **array_elements = NULL;
char *string_val;
uint32_t int_val, bool_val;
int i, j, k, type, array_type, num_array_elements = 0;
int ret, num_properties = 0, num_uuids = 0, num_devices = 0;
jclass stringClass = env->FindClass("java/lang/String");
DBusError err;
dbus_error_init(&err);
for (i = 0; i < max_num_properties; i++)
memset(values[i], '\0', 128 * sizeof(char));
if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
goto failure;
dbus_message_iter_recurse(iter, &dict);
do {
if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY)
goto failure;
dbus_message_iter_recurse(&dict, &dict_entry);
if (dbus_message_iter_get_arg_type(&dict_entry) != DBUS_TYPE_STRING)
goto failure;
dbus_message_iter_get_basic(&dict_entry, &property);
if (!dbus_message_iter_next(&dict_entry))
goto failure;
if (dbus_message_iter_get_arg_type(&dict_entry) != DBUS_TYPE_VARIANT)
goto failure;
for (i = 0; i < max_num_properties; i++) {
if (!strncmp(properties[i].name, property, strlen(property))) {
num_properties ++;
break;
}
}
if (i == max_num_properties)
goto failure;
type = properties[i].type;
dbus_message_iter_recurse(&dict_entry, &prop_val);
if(dbus_message_iter_get_arg_type(&prop_val) != type) {
LOGE("Property type mismatch in parse_properties: %d, expected:%d",
dbus_message_iter_get_arg_type(&prop_val), type);
goto failure;
}
switch(type) {
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
dbus_message_iter_get_basic(&prop_val, &string_val);
strcpy(values[i], string_val);
break;
case DBUS_TYPE_UINT32:
case DBUS_TYPE_INT16:
dbus_message_iter_get_basic(&prop_val, &int_val);
sprintf(values[i], "%d", int_val);
break;
case DBUS_TYPE_BOOLEAN:
dbus_message_iter_get_basic(&prop_val, &bool_val);
sprintf(values[i], "%s", bool_val ? "true" : "false");
break;
case DBUS_TYPE_ARRAY:
dbus_message_iter_recurse(&prop_val, &array_val_iter);
array_type = dbus_message_iter_get_arg_type(&array_val_iter);
num_array_elements = 0;
if (array_type == DBUS_TYPE_OBJECT_PATH ||
array_type == DBUS_TYPE_STRING){
do {
num_array_elements++;
} while(dbus_message_iter_next(&array_val_iter));
dbus_message_iter_recurse(&prop_val, &array_val_iter);
// Allocate an array
array_elements = (char **)malloc(sizeof(char *) *
num_array_elements);
if (!array_elements)
goto failure;
j = 0;
do {
dbus_message_iter_get_basic(&array_val_iter, &array_elements[j]);
j++;
} while(dbus_message_iter_next(&array_val_iter));
if (!strncmp(property, "UUIDs", strlen("UUIDs"))) {
num_uuids = num_array_elements;
uuid_array = array_elements;
} else {
num_devices = num_array_elements;
device_path = array_elements;
}
}
break;
default:
goto failure;
}
} while(dbus_message_iter_next(&dict));
// Convert it to a array of strings.
strArray = env->NewObjectArray((num_properties + num_array_elements) * 2,
stringClass, NULL);
j = 0;
for (i = 0; i < max_num_properties; i++) {
if (properties[i].type == DBUS_TYPE_ARRAY) {
if (!strncmp(properties[i].name, "UUIDs", strlen("UUIDs"))) {
num_array_elements = num_uuids;
array_elements = uuid_array;
} else {
num_array_elements = num_devices;
array_elements = device_path;
}
for (k = 0; k < num_array_elements; k++) {
set_object_array_element(env, strArray, properties[i].name, j++);
set_object_array_element(env, strArray, array_elements[k], j++);
}
} else if (values[i][0] != '\0') {
set_object_array_element(env, strArray, properties[i].name, j++);
set_object_array_element(env, strArray, values[i], j++);
}
}
if (uuid_array)
free(uuid_array);
if (device_path)
free(device_path);
return strArray;
failure:
if (dbus_error_is_set(&err))
LOG_AND_FREE_DBUS_ERROR(&err);
if (uuid_array)
free(uuid_array);
if (device_path)
free(device_path);
return NULL;
}
jobjectArray create_prop_array(JNIEnv *env, Properties *properties,
int prop_index, void *value, int len ) {
jclass stringClass= env->FindClass("java/lang/String");
char **prop_val = NULL;
char buf[32] = {'\0'};
int i, j;
jobjectArray strArray = env->NewObjectArray(1 + len, stringClass, NULL);
j = 0;
set_object_array_element(env, strArray, properties[prop_index].name, j++);
if (properties[prop_index].type == DBUS_TYPE_UINT32) {
sprintf(buf, "%d", *(int *) value);
set_object_array_element(env, strArray, buf, j++);
} else if (properties[prop_index].type == DBUS_TYPE_BOOLEAN) {
sprintf(buf, "%s", *(int *) value ? "true" : "false");
set_object_array_element(env, strArray, buf, j++);
} else if (properties[prop_index].type == DBUS_TYPE_ARRAY) {
prop_val = (char **) value;
for (i = 0; i < len; i++)
set_object_array_element(env, strArray, prop_val[i], j++);
} else {
set_object_array_element(env, strArray, (const char *) value, j++);
}
if (prop_val)
free (prop_val);
return strArray;
}
jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg,
Properties *properties, int max_num_properties) {
DBusMessageIter iter, prop_val, array_val_iter;
DBusError err;
void *value;
char *property;
uint32_t array_type;
int i, j, type, len, prop_index;
dbus_error_init(&err);
if (!dbus_message_iter_init(msg, &iter))
goto failure;
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
goto failure;
dbus_message_iter_get_basic(&iter, &property);
if (!dbus_message_iter_next(&iter))
goto failure;
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
goto failure;
for (i = 0; i < max_num_properties; i++) {
if (!strncmp(property, properties[i].name, strlen(properties[i].name)))
break;
}
prop_index = i;
if (i == max_num_properties)
goto failure;
dbus_message_iter_recurse(&iter, &prop_val);
type = properties[prop_index].type;
if (dbus_message_iter_get_arg_type(&prop_val) != type) {
LOGE("Property type mismatch in parse_properties: %d, expected:%d",
dbus_message_iter_get_arg_type(&prop_val), type);
goto failure;
}
switch(type) {
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
dbus_message_iter_get_basic(&prop_val, &value);
len = 1;
break;
case DBUS_TYPE_UINT32:
case DBUS_TYPE_INT16:
case DBUS_TYPE_BOOLEAN:
uint32_t int_val;
dbus_message_iter_get_basic(&prop_val, &int_val);
value = &int_val;
len = 1;
break;
case DBUS_TYPE_ARRAY:
dbus_message_iter_recurse(&prop_val, &array_val_iter);
array_type = dbus_message_iter_get_arg_type(&array_val_iter);
len = 0;
if (array_type == DBUS_TYPE_OBJECT_PATH ||
array_type == DBUS_TYPE_STRING){
do {
len ++;
} while(dbus_message_iter_next(&array_val_iter));
dbus_message_iter_recurse(&prop_val, &array_val_iter);
// Allocate an array of char *
char **tmp = (char **)malloc(sizeof(char *) * len);
if (!tmp)
goto failure;
j = 0;
do {
dbus_message_iter_get_basic(&array_val_iter, &tmp[j]);
j ++;
} while(dbus_message_iter_next(&array_val_iter));
value = (char **) tmp;
}
break;
default:
goto failure;
}
return create_prop_array(env, properties, prop_index, value, len);
failure:
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return NULL;
}
jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) {
return parse_property_change(env, msg, (Properties *) &adapter_properties,
sizeof(adapter_properties) / sizeof(Properties));
}
jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg) {
return parse_property_change(env, msg, (Properties *) &remote_device_properties,
sizeof(remote_device_properties) / sizeof(Properties));
}
jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) {
return parse_properties(env, iter, (Properties *) &adapter_properties,
sizeof(adapter_properties) / sizeof(Properties));
}
jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter) {
return parse_properties(env, iter, (Properties *) &remote_device_properties,
sizeof(remote_device_properties) / sizeof(Properties));
}
int get_bdaddr(const char *str, bdaddr_t *ba) {
char *d = ((char *)ba) + 5, *endp;
int i;

View File

@@ -68,6 +68,7 @@ jfieldID get_field(JNIEnv *env,
struct event_loop_native_data_t {
DBusConnection *conn;
const char *adapter;
/* protects the thread */
pthread_mutex_t thread_mutex;
@@ -89,6 +90,12 @@ struct event_loop_native_data_t {
jobject me;
};
struct _Properties {
char name[32];
int type;
};
typedef struct _Properties Properties;
dbus_bool_t dbus_func_args_async(JNIEnv *env,
DBusConnection *conn,
int timeout_ms,
@@ -142,8 +149,18 @@ jint dbus_returns_uint32(JNIEnv *env, DBusMessage *reply);
jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply);
jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply);
jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply);
jobjectArray dbus_returns_array_of_object_path(JNIEnv *env, DBusMessage *reply);
jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply);
jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties,
const int max_num_properties);
jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg,
Properties *properties, int max_num_properties);
jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter);
jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter);
jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg);
jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg);
void append_variant(DBusMessageIter *iter, int type, void *val);
int get_bdaddr(const char *str, bdaddr_t *ba);
void get_bdaddr_as_string(const bdaddr_t *ba, char *str);

View File

@@ -37,12 +37,7 @@
namespace android {
#ifdef HAVE_BLUETOOTH
static jmethodID method_onHeadsetCreated;
static jmethodID method_onHeadsetRemoved;
static jmethodID method_onSinkConnected;
static jmethodID method_onSinkDisconnected;
static jmethodID method_onSinkPlaying;
static jmethodID method_onSinkStopped;
static jmethodID method_onSinkPropertyChanged;
typedef struct {
JavaVM *vm;
@@ -53,11 +48,11 @@ typedef struct {
static native_data_t *nat = NULL; // global native data
#endif
#ifdef HAVE_BLUETOOTH
static void onConnectSinkResult(DBusMessage *msg, void *user, void *nat);
static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *nat);
static Properties sink_properties[] = {
{"State", DBUS_TYPE_STRING},
{"Connected", DBUS_TYPE_BOOLEAN},
{"Playing", DBUS_TYPE_BOOLEAN},
};
#endif
/* Returns true on success (even if adapter is present but disabled).
@@ -99,91 +94,58 @@ static void cleanupNative(JNIEnv* env, jobject object) {
}
#endif
}
static jobjectArray listHeadsetsNative(JNIEnv *env, jobject object) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
DBusMessage *reply =
dbus_func_args(env, nat->conn, "/org/bluez/audio",
"org.bluez.audio.Manager", "ListHeadsets",
DBUS_TYPE_INVALID);
return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
}
#endif
return NULL;
}
static jstring createHeadsetNative(JNIEnv *env, jobject object,
jstring address) {
static jobjectArray getSinkPropertiesNative(JNIEnv *env, jobject object,
jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_address = env->GetStringUTFChars(address, NULL);
LOGV("... address = %s\n", c_address);
DBusMessage *reply =
dbus_func_args(env, nat->conn, "/org/bluez/audio",
"org.bluez.audio.Manager", "CreateHeadset",
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(address, c_address);
return reply ? dbus_returns_string(env, reply) : NULL;
}
#endif
return NULL;
}
DBusMessage *msg, *reply;
DBusError err;
dbus_error_init(&err);
static jstring removeHeadsetNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
DBusMessage *reply =
dbus_func_args(env, nat->conn, "/org/bluez/audio",
"org.bluez.audio.Manager", "RemoveHeadset",
DBUS_TYPE_STRING, &c_path,
DBUS_TYPE_INVALID);
reply = dbus_func_args_timeout(env,
nat->conn, -1, c_path,
"org.bluez.AudioSink", "GetProperties",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
return reply ? dbus_returns_string(env, reply) : NULL;
if (!reply && dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
return NULL;
} else if (!reply) {
LOGE("DBus reply is NULL in function %s", __FUNCTION__);
return NULL;
}
DBusMessageIter iter;
if (dbus_message_iter_init(reply, &iter))
return parse_properties(env, &iter, (Properties *)&sink_properties,
sizeof(sink_properties) / sizeof(Properties));
}
#endif
return NULL;
}
static jstring getAddressNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
DBusMessage *reply =
dbus_func_args(env, nat->conn, c_path,
"org.bluez.audio.Device", "GetAddress",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
return reply ? dbus_returns_string(env, reply) : NULL;
}
#endif
return NULL;
}
static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
size_t path_sz = env->GetStringUTFLength(path) + 1;
char *c_path_copy = (char *)malloc(path_sz); // callback data
strncpy(c_path_copy, c_path, path_sz);
bool ret =
dbus_func_args_async(env, nat->conn, -1,
onConnectSinkResult, (void *)c_path_copy, nat,
c_path,
"org.bluez.audio.Sink", "Connect",
DBUS_TYPE_INVALID);
DBusError err;
dbus_error_init(&err);
DBusMessage *reply =
dbus_func_args_timeout(env, nat->conn, -1, c_path,
"org.bluez.AudioSink", "Connect",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
if (!ret) {
free(c_path_copy);
if (!reply && dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
return JNI_FALSE;
} else if (!reply) {
LOGE("DBus reply is NULL in function %s", __FUNCTION__);
return JNI_FALSE;
}
return JNI_TRUE;
@@ -198,19 +160,20 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object,
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
size_t path_sz = env->GetStringUTFLength(path) + 1;
char *c_path_copy = (char *)malloc(path_sz); // callback data
strncpy(c_path_copy, c_path, path_sz);
DBusError err;
dbus_error_init(&err);
bool ret =
dbus_func_args_async(env, nat->conn, -1,
onDisconnectSinkResult, (void *)c_path_copy, nat,
c_path,
"org.bluez.audio.Sink", "Disconnect",
DBUS_TYPE_INVALID);
DBusMessage *reply =
dbus_func_args_timeout(env, nat->conn, -1, c_path,
"org.bluez.AudioSink", "Disconnect",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
if (!ret) {
free(c_path_copy);
if (!reply && dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
return JNI_FALSE;
} else if (!reply) {
LOGE("DBus reply is NULL in function %s", __FUNCTION__);
return JNI_FALSE;
}
return JNI_TRUE;
@@ -219,94 +182,8 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object,
return JNI_FALSE;
}
static jboolean isSinkConnectedNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
DBusMessage *reply =
dbus_func_args(env, nat->conn, c_path,
"org.bluez.audio.Sink", "IsConnected",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
#ifdef HAVE_BLUETOOTH
static void onConnectSinkResult(DBusMessage *msg, void *user, void *natData) {
LOGV(__FUNCTION__);
char *c_path = (char *)user;
DBusError err;
JNIEnv *env;
if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) {
LOGE("%s: error finding Env for our VM\n", __FUNCTION__);
return;
}
dbus_error_init(&err);
LOGV("... path = %s", c_path);
if (dbus_set_error_from_message(&err, msg)) {
/* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
dbus_error_free(&err);
env->CallVoidMethod(nat->me,
method_onSinkDisconnected,
env->NewStringUTF(c_path));
if (env->ExceptionCheck()) {
LOGE("VM Exception occurred in native function %s (%s:%d)",
__FUNCTION__, __FILE__, __LINE__);
}
} // else Java callback is triggered by signal in a2dp_event_filter
free(c_path);
}
static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *natData) {
LOGV(__FUNCTION__);
char *c_path = (char *)user;
DBusError err;
JNIEnv *env;
if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) {
LOGE("%s: error finding Env for our VM\n", __FUNCTION__);
return;
}
dbus_error_init(&err);
LOGV("... path = %s", c_path);
if (dbus_set_error_from_message(&err, msg)) {
/* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
if (strcmp(err.name, "org.bluez.Error.NotConnected") == 0) {
// we were already disconnected, so report disconnect
env->CallVoidMethod(nat->me,
method_onSinkDisconnected,
env->NewStringUTF(c_path));
} else {
// Assume it is still connected
env->CallVoidMethod(nat->me,
method_onSinkConnected,
env->NewStringUTF(c_path));
}
dbus_error_free(&err);
if (env->ExceptionCheck()) {
LOGE("VM Exception occurred in native function %s (%s:%d)",
__FUNCTION__, __FILE__, __LINE__);
}
} // else Java callback is triggered by signal in a2dp_event_filter
free(c_path);
}
DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) {
#ifdef HAVE_BLUETOOTH
DBusError err;
if (!nat) {
@@ -323,71 +200,23 @@ DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) {
DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (dbus_message_is_signal(msg,
"org.bluez.audio.Manager",
"HeadsetCreated")) {
char *c_path;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_path,
DBUS_TYPE_INVALID)) {
LOGV("... path = %s", c_path);
env->CallVoidMethod(nat->me,
method_onHeadsetCreated,
env->NewStringUTF(c_path));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
result = DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.audio.Manager",
"HeadsetRemoved")) {
char *c_path;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_path,
DBUS_TYPE_INVALID)) {
LOGV("... path = %s", c_path);
env->CallVoidMethod(nat->me,
method_onHeadsetRemoved,
env->NewStringUTF(c_path));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
result = DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.audio.Sink",
"Connected")) {
if (dbus_message_is_signal(msg, "org.bluez.AudioSink",
"PropertyChanged")) {
jobjectArray str_array =
parse_property_change(env, msg, (Properties *)&sink_properties,
sizeof(sink_properties) / sizeof(Properties));
const char *c_path = dbus_message_get_path(msg);
LOGV("... path = %s", c_path);
env->CallVoidMethod(nat->me,
method_onSinkConnected,
env->NewStringUTF(c_path));
method_onSinkPropertyChanged,
env->NewStringUTF(c_path),
str_array);
for (int i = 0; i < env->GetArrayLength(str_array); i++) {
env->DeleteLocalRef(env->GetObjectArrayElement(str_array, i));
}
env->DeleteLocalRef(str_array);
result = DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.audio.Sink",
"Disconnected")) {
const char *c_path = dbus_message_get_path(msg);
LOGV("... path = %s", c_path);
env->CallVoidMethod(nat->me,
method_onSinkDisconnected,
env->NewStringUTF(c_path));
result = DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.audio.Sink",
"Playing")) {
const char *c_path = dbus_message_get_path(msg);
LOGV("... path = %s", c_path);
env->CallVoidMethod(nat->me,
method_onSinkPlaying,
env->NewStringUTF(c_path));
result = DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.audio.Sink",
"Stopped")) {
const char *c_path = dbus_message_get_path(msg);
LOGV("... path = %s", c_path);
env->CallVoidMethod(nat->me,
method_onSinkStopped,
env->NewStringUTF(c_path));
result = DBUS_HANDLER_RESULT_HANDLED;
}
if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) {
return result;
} else {
LOGV("... ignored");
}
if (env->ExceptionCheck()) {
@@ -406,14 +235,11 @@ static JNINativeMethod sMethods[] = {
{"initNative", "()Z", (void *)initNative},
{"cleanupNative", "()V", (void *)cleanupNative},
/* Bluez audio 3.36 API */
{"listHeadsetsNative", "()[Ljava/lang/String;", (void*)listHeadsetsNative},
{"createHeadsetNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)createHeadsetNative},
{"removeHeadsetNative", "(Ljava/lang/String;)Z", (void*)removeHeadsetNative},
{"getAddressNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAddressNative},
{"connectSinkNative", "(Ljava/lang/String;)Z", (void*)connectSinkNative},
{"disconnectSinkNative", "(Ljava/lang/String;)Z", (void*)disconnectSinkNative},
{"isSinkConnectedNative", "(Ljava/lang/String;)Z", (void*)isSinkConnectedNative},
/* Bluez audio 4.40 API */
{"connectSinkNative", "(Ljava/lang/String;)Z", (void *)connectSinkNative},
{"disconnectSinkNative", "(Ljava/lang/String;)Z", (void *)disconnectSinkNative},
{"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
(void *)getSinkPropertiesNative},
};
int register_android_server_BluetoothA2dpService(JNIEnv *env) {
@@ -424,12 +250,8 @@ int register_android_server_BluetoothA2dpService(JNIEnv *env) {
}
#ifdef HAVE_BLUETOOTH
method_onHeadsetCreated = env->GetMethodID(clazz, "onHeadsetCreated", "(Ljava/lang/String;)V");
method_onHeadsetRemoved = env->GetMethodID(clazz, "onHeadsetRemoved", "(Ljava/lang/String;)V");
method_onSinkConnected = env->GetMethodID(clazz, "onSinkConnected", "(Ljava/lang/String;)V");
method_onSinkDisconnected = env->GetMethodID(clazz, "onSinkDisconnected", "(Ljava/lang/String;)V");
method_onSinkPlaying = env->GetMethodID(clazz, "onSinkPlaying", "(Ljava/lang/String;)V");
method_onSinkStopped = env->GetMethodID(clazz, "onSinkStopped", "(Ljava/lang/String;)V");
method_onSinkPropertyChanged = env->GetMethodID(clazz, "onSinkPropertyChanged",
"(Ljava/lang/String;[Ljava/lang/String;)V");
#endif
return AndroidRuntime::registerNativeMethods(env,

File diff suppressed because it is too large Load Diff

View File

@@ -39,30 +39,19 @@ namespace android {
#ifdef HAVE_BLUETOOTH
static jfieldID field_mNativeData;
static jmethodID method_onModeChanged;
static jmethodID method_onNameChanged;
static jmethodID method_onDiscoveryStarted;
static jmethodID method_onDiscoveryCompleted;
static jmethodID method_onRemoteDeviceFound;
static jmethodID method_onRemoteDeviceDisappeared;
static jmethodID method_onRemoteClassUpdated;
static jmethodID method_onRemoteNameUpdated;
static jmethodID method_onRemoteNameFailed;
static jmethodID method_onRemoteDeviceConnected;
static jmethodID method_onRemoteDeviceDisconnectRequested;
static jmethodID method_onRemoteDeviceDisconnected;
static jmethodID method_onBondingCreated;
static jmethodID method_onBondingRemoved;
static jmethodID method_onPropertyChanged;
static jmethodID method_onDevicePropertyChanged;
static jmethodID method_onDeviceFound;
static jmethodID method_onDeviceDisappeared;
static jmethodID method_onDeviceCreated;
static jmethodID method_onDeviceRemoved;
static jmethodID method_onCreateBondingResult;
static jmethodID method_onGetRemoteServiceChannelResult;
static jmethodID method_onCreatePairedDeviceResult;
static jmethodID method_onGetDeviceServiceChannelResult;
static jmethodID method_onPasskeyAgentRequest;
static jmethodID method_onPasskeyAgentCancel;
static jmethodID method_onAuthAgentAuthorize;
static jmethodID method_onAuthAgentCancel;
static jmethodID method_onRestartRequired;
static jmethodID method_onRequestPinCode;
static jmethodID method_onAgentAuthorize;
static jmethodID method_onAgentCancel;
typedef event_loop_native_data_t native_data_t;
@@ -80,30 +69,26 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
LOGV(__FUNCTION__);
#ifdef HAVE_BLUETOOTH
method_onModeChanged = env->GetMethodID(clazz, "onModeChanged", "(Ljava/lang/String;)V");
method_onNameChanged = env->GetMethodID(clazz, "onNameChanged", "(Ljava/lang/String;)V");
method_onDiscoveryStarted = env->GetMethodID(clazz, "onDiscoveryStarted", "()V");
method_onDiscoveryCompleted = env->GetMethodID(clazz, "onDiscoveryCompleted", "()V");
method_onRemoteDeviceFound = env->GetMethodID(clazz, "onRemoteDeviceFound", "(Ljava/lang/String;IS)V");
method_onRemoteDeviceDisappeared = env->GetMethodID(clazz, "onRemoteDeviceDisappeared", "(Ljava/lang/String;)V");
method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V");
method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V");
method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V");
method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V");
method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V");
method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V");
method_onBondingCreated = env->GetMethodID(clazz, "onBondingCreated", "(Ljava/lang/String;)V");
method_onBondingRemoved = env->GetMethodID(clazz, "onBondingRemoved", "(Ljava/lang/String;)V");
method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
"([Ljava/lang/String;)V");
method_onDevicePropertyChanged = env->GetMethodID(clazz,
"onDevicePropertyChanged",
"(Ljava/lang/String;[Ljava/lang/String;)V");
method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound",
"(Ljava/lang/String;[Ljava/lang/String;)V");
method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared",
"(Ljava/lang/String;)V");
method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V");
method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V");
method_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;I)V");
method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
"(Ljava/lang/String;I)V");
method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V");
method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V");
method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V");
method_onRestartRequired = env->GetMethodID(clazz, "onRestartRequired", "()V");
method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
"(Ljava/lang/String;Ljava/lang/String;)Z");
method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
"(Ljava/lang/String;I)V");
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
#endif
@@ -153,9 +138,11 @@ static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
#ifdef HAVE_BLUETOOTH
static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
void *data);
static DBusHandlerResult agent_event_filter(DBusConnection *conn,
DBusMessage *msg,
void *data);
DBusHandlerResult agent_event_filter(DBusConnection *conn,
DBusMessage *msg,
void *data);
static int register_agent(native_data_t *nat,
const char *agent_path, const char *capabilities);
static const DBusObjectPathVTable agent_vtable = {
NULL, agent_event_filter, NULL, NULL, NULL, NULL
@@ -164,11 +151,12 @@ static const DBusObjectPathVTable agent_vtable = {
static jboolean setUpEventLoop(native_data_t *nat) {
LOGV(__FUNCTION__);
dbus_threads_init_default();
DBusError err;
dbus_error_init(&err);
if (nat != NULL && nat->conn != NULL) {
dbus_threads_init_default();
DBusError err;
dbus_error_init(&err);
// Add a filter for all incoming messages
if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){
return JNI_FALSE;
@@ -190,108 +178,141 @@ static jboolean setUpEventLoop(native_data_t *nat) {
return JNI_FALSE;
}
dbus_bus_add_match(nat->conn,
"type='signal',interface='org.bluez.audio.Manager'",
"type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
&err);
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
return JNI_FALSE;
}
dbus_bus_add_match(nat->conn,
"type='signal',interface='org.bluez.audio.Device'",
&err);
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
return JNI_FALSE;
}
dbus_bus_add_match(nat->conn,
"type='signal',interface='org.bluez.audio.Sink'",
"type='signal',interface='org.bluez.AudioSink'",
&err);
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
return JNI_FALSE;
}
// Add an object handler for passkey agent method calls
const char *path = "/android/bluetooth/Agent";
if (!dbus_connection_register_object_path(nat->conn, path,
&agent_vtable, nat)) {
LOGE("%s: Can't register object path %s for agent!",
__FUNCTION__, path);
const char *agent_path = "/android/bluetooth/agent";
const char *capabilities = "DisplayYesNo";
if (register_agent(nat, agent_path, capabilities) < 0) {
dbus_connection_unregister_object_path (nat->conn, agent_path);
return JNI_FALSE;
}
// RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep
// trying for 10 seconds.
int attempt;
for (attempt = 0; attempt < 1000; attempt++) {
DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err,
BLUEZ_DBUS_BASE_PATH,
"org.bluez.Security", "RegisterDefaultPasskeyAgent",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
if (reply) {
// Success
dbus_message_unref(reply);
LOGV("Registered agent on attempt %d of 1000\n", attempt);
break;
} else if (dbus_error_has_name(&err,
"org.freedesktop.DBus.Error.ServiceUnknown")) {
// hcid is still down, retry
dbus_error_free(&err);
usleep(10000); // 10 ms
} else {
// Some other error we weren't expecting
LOG_AND_FREE_DBUS_ERROR(&err);
return JNI_FALSE;
}
}
if (attempt == 1000) {
LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), "
"is hcid running?");
return JNI_FALSE;
}
// Now register the Auth agent
DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err,
BLUEZ_DBUS_BASE_PATH,
"org.bluez.Security", "RegisterDefaultAuthorizationAgent",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
if (!reply) {
LOG_AND_FREE_DBUS_ERROR(&err);
return JNI_FALSE;
}
dbus_message_unref(reply);
return JNI_TRUE;
}
return JNI_FALSE;
}
const char * get_adapter_path(DBusConnection *conn) {
DBusMessage *msg, *reply;
DBusError err;
const char *device_path = NULL;
msg = dbus_message_new_method_call("org.bluez", "/",
"org.bluez.Manager", "DefaultAdapter");
if (!msg) {
LOGE("%s: Can't allocate new method call for GetProperties!",
__FUNCTION__);
return NULL;
}
dbus_message_append_args(msg, DBUS_TYPE_INVALID);
dbus_error_init(&err);
reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
dbus_message_unref(msg);
if (!reply) {
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
}
return NULL;
}
if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
&device_path, DBUS_TYPE_INVALID)
|| !device_path){
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
}
return NULL;
}
return device_path;
}
static int register_agent(native_data_t *nat,
const char * agent_path, const char * capabilities)
{
DBusMessage *msg, *reply;
DBusError err;
if (!dbus_connection_register_object_path(nat->conn, agent_path,
&agent_vtable, nat)) {
LOGE("%s: Can't register object path %s for agent!",
__FUNCTION__, agent_path);
return -1;
}
nat->adapter = get_adapter_path(nat->conn);
msg = dbus_message_new_method_call("org.bluez", nat->adapter,
"org.bluez.Adapter", "RegisterAgent");
if (!msg) {
LOGE("%s: Can't allocate new method call for agent!",
__FUNCTION__);
return -1;
}
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
DBUS_TYPE_STRING, &capabilities,
DBUS_TYPE_INVALID);
dbus_error_init(&err);
reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
dbus_message_unref(msg);
if (!reply) {
LOGE("%s: Can't register agent!", __FUNCTION__);
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
}
return -1;
}
dbus_message_unref(reply);
dbus_connection_flush(nat->conn);
return 0;
}
static void tearDownEventLoop(native_data_t *nat) {
LOGV(__FUNCTION__);
if (nat != NULL && nat->conn != NULL) {
DBusMessage *msg, *reply;
DBusError err;
dbus_error_init(&err);
const char * agent_path = "/android/bluetooth/agent";
const char *path = "/android/bluetooth/Agent";
DBusMessage *reply =
dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH,
"org.bluez.Security", "UnregisterDefaultPasskeyAgent",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
if (reply) dbus_message_unref(reply);
msg = dbus_message_new_method_call("org.bluez",
nat->adapter,
"org.bluez.Adapter",
"UnregisterAgent");
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
dbus_error_free(&err);
}
reply =
dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH,
"org.bluez.Security", "UnregisterDefaultAuthorizationAgent",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
if (reply) dbus_message_unref(reply);
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
DBUS_TYPE_INVALID);
reply = dbus_connection_send_with_reply_and_block(nat->conn,
msg, -1, &err);
dbus_message_unref(msg);
dbus_connection_unregister_object_path(nat->conn, path);
if (!reply && dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
dbus_error_free(&err);
}
dbus_message_unref(reply);
dbus_connection_flush(nat->conn);
dbus_connection_unregister_object_path(nat->conn, agent_path);
dbus_bus_remove_match(nat->conn,
"type='signal',interface='org.bluez.audio.Sink'",
@@ -649,267 +670,129 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
LOGV("%s: Received signal %s:%s from %s", __FUNCTION__,
dbus_message_get_interface(msg), dbus_message_get_member(msg),
dbus_message_get_path(msg));
// STOPSHIP: Change to LOGV
LOGE("%s: Received signal %s:%s from %s", __FUNCTION__,
dbus_message_get_interface(msg), dbus_message_get_member(msg),
dbus_message_get_path(msg));
if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"RemoteDeviceFound")) {
"DeviceFound")) {
char *c_address;
int n_class;
short n_rssi;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_UINT32, &n_class,
DBUS_TYPE_INT16, &n_rssi,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s class = %#X rssi = %hd", c_address, n_class,
n_rssi);
DBusMessageIter iter;
jobjectArray str_array = NULL;
if (dbus_message_iter_init(msg, &iter)) {
dbus_message_iter_get_basic(&iter, &c_address);
if (dbus_message_iter_next(&iter))
str_array =
parse_remote_device_properties(env, &iter);
}
if (str_array != NULL) {
env->CallVoidMethod(nat->me,
method_onRemoteDeviceFound,
method_onDeviceFound,
env->NewStringUTF(c_address),
(jint)n_class,
(jshort)n_rssi);
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
str_array);
} else
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"DiscoveryStarted")) {
LOGI("DiscoveryStarted signal received");
env->CallVoidMethod(nat->me, method_onDiscoveryStarted);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"DiscoveryCompleted")) {
LOGI("DiscoveryCompleted signal received");
env->CallVoidMethod(nat->me, method_onDiscoveryCompleted);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"RemoteDeviceDisappeared")) {
"org.bluez.Adapter",
"DeviceDisappeared")) {
char *c_address;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
env->CallVoidMethod(nat->me, method_onRemoteDeviceDisappeared,
env->CallVoidMethod(nat->me, method_onDeviceDisappeared,
env->NewStringUTF(c_address));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"RemoteClassUpdated")) {
char *c_address;
int n_class;
"org.bluez.Adapter",
"DeviceCreated")) {
char *c_object_path;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_UINT32, &n_class,
DBUS_TYPE_OBJECT_PATH, &c_object_path,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
env->CallVoidMethod(nat->me, method_onRemoteClassUpdated,
env->NewStringUTF(c_address), (jint)n_class);
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"RemoteNameUpdated")) {
char *c_address;
char *c_name;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_STRING, &c_name,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s, name = %s", c_address, c_name);
LOGV("... address = %s", c_object_path);
env->CallVoidMethod(nat->me,
method_onRemoteNameUpdated,
env->NewStringUTF(c_address),
env->NewStringUTF(c_name));
method_onDeviceCreated,
env->NewStringUTF(c_object_path));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"RemoteNameFailed")) {
char *c_address;
"org.bluez.Adapter",
"DeviceRemoved")) {
char *c_object_path;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
env->CallVoidMethod(nat->me,
method_onRemoteNameFailed,
env->NewStringUTF(c_address));
DBUS_TYPE_OBJECT_PATH, &c_object_path,
DBUS_TYPE_INVALID)) {
LOGV("... Object Path = %s", c_object_path);
env->CallVoidMethod(nat->me,
method_onDeviceRemoved,
env->NewStringUTF(c_object_path));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"RemoteDeviceConnected")) {
char *c_address;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
env->CallVoidMethod(nat->me,
method_onRemoteDeviceConnected,
env->NewStringUTF(c_address));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"RemoteDeviceDisconnectRequested")) {
char *c_address;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
env->CallVoidMethod(nat->me,
method_onRemoteDeviceDisconnectRequested,
env->NewStringUTF(c_address));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"RemoteDeviceDisconnected")) {
char *c_address;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
env->CallVoidMethod(nat->me,
method_onRemoteDeviceDisconnected,
env->NewStringUTF(c_address));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"BondingCreated")) {
char *c_address;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
env->CallVoidMethod(nat->me,
method_onBondingCreated,
env->NewStringUTF(c_address));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"BondingRemoved")) {
char *c_address;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
env->CallVoidMethod(nat->me,
method_onBondingRemoved,
env->NewStringUTF(c_address));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"ModeChanged")) {
char *c_mode;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_mode,
DBUS_TYPE_INVALID)) {
LOGV("... mode = %s", c_mode);
env->CallVoidMethod(nat->me,
method_onModeChanged,
env->NewStringUTF(c_mode));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"NameChanged")) {
char *c_name;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_name,
DBUS_TYPE_INVALID)) {
LOGV("... name = %s", c_name);
env->CallVoidMethod(nat->me,
method_onNameChanged,
env->NewStringUTF(c_name));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.freedesktop.DBus",
"NameOwnerChanged")) {
char *c_name;
char *c_old_owner;
char *c_new_owner;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_name,
DBUS_TYPE_STRING, &c_old_owner,
DBUS_TYPE_STRING, &c_new_owner,
DBUS_TYPE_INVALID)) {
LOGV("... name = %s", c_name);
LOGV("... old_owner = %s", c_old_owner);
LOGV("... new_owner = %s", c_new_owner);
if (!strcmp(c_name, "org.bluez") && c_new_owner[0] != '\0') {
// New owner of org.bluez. This can only happen when hcid
// restarts. Need to restart framework BT services to recover.
LOGE("Looks like hcid restarted");
env->CallVoidMethod(nat->me, method_onRestartRequired);
}
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
"PropertyChanged")) {
jobjectArray str_array = parse_adapter_property_change(env, msg);
if (str_array != NULL) {
/* Check if bluetoothd has (re)started, if so update the path. */
jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
const char *c_property = env->GetStringUTFChars(property, NULL);
if (!strncmp(c_property, "Powered", strlen("Powered"))) {
jstring value =
(jstring) env->GetObjectArrayElement(str_array, 1);
const char *c_value = env->GetStringUTFChars(value, NULL);
if (!strncmp(c_value, "true", strlen("true")))
nat->adapter = get_adapter_path(nat->conn);
env->ReleaseStringUTFChars(value, c_value);
}
env->ReleaseStringUTFChars(property, c_property);
env->CallVoidMethod(nat->me,
method_onPropertyChanged,
str_array);
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(msg,
"org.bluez.Device",
"PropertyChanged")) {
jobjectArray str_array = parse_remote_device_property_change(env, msg);
if (str_array != NULL) {
const char *remote_device_path = dbus_message_get_path(msg);
env->CallVoidMethod(nat->me,
method_onDevicePropertyChanged,
env->NewStringUTF(remote_device_path),
str_array);
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
return DBUS_HANDLER_RESULT_HANDLED;
}
return a2dp_event_filter(msg, env);
}
// Called by dbus during WaitForAndDispatchEventNative()
static DBusHandlerResult agent_event_filter(DBusConnection *conn,
DBusMessage *msg, void *data) {
DBusHandlerResult agent_event_filter(DBusConnection *conn,
DBusMessage *msg, void *data) {
native_data_t *nat = (native_data_t *)data;
JNIEnv *env;
nat->vm->GetEnv((void**)&env, nat->envVer);
if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
LOGV("%s: not interested (not a method call).", __FUNCTION__);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
LOGV("%s: Received method %s:%s", __FUNCTION__,
LOGI("%s: Received method %s:%s", __FUNCTION__,
dbus_message_get_interface(msg), dbus_message_get_member(msg));
if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED;
nat->vm->GetEnv((void**)&env, nat->envVer);
if (dbus_message_is_method_call(msg,
"org.bluez.PasskeyAgent", "Request")) {
"org.bluez.Agent", "Cancel")) {
const char *adapter;
const char *address;
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &adapter,
DBUS_TYPE_STRING, &address,
DBUS_TYPE_INVALID)) {
LOGE("%s: Invalid arguments for Request() method", __FUNCTION__);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
LOGV("... address = %s", address);
dbus_message_ref(msg); // increment refcount because we pass to java
env->CallVoidMethod(nat->me, method_onPasskeyAgentRequest,
env->NewStringUTF(address), (int)msg);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(msg,
"org.bluez.PasskeyAgent", "Cancel")) {
const char *adapter;
const char *address;
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &adapter,
DBUS_TYPE_STRING, &address,
DBUS_TYPE_INVALID)) {
LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
LOGV("... address = %s", address);
env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel,
env->NewStringUTF(address));
env->CallVoidMethod(nat->me, method_onAgentCancel);
// reply
DBusMessage *reply = dbus_message_new_method_return(msg);
@@ -922,41 +805,23 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn,
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(msg,
"org.bluez.PasskeyAgent", "Release")) {
LOGW("We are no longer the passkey agent!");
// reply
DBusMessage *reply = dbus_message_new_method_return(msg);
if (!reply) {
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
dbus_connection_send(nat->conn, reply, NULL);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(msg,
"org.bluez.AuthorizationAgent", "Authorize")) {
const char *adapter;
const char *address;
const char *service;
"org.bluez.Agent", "Authorize")) {
char *object_path;
const char *uuid;
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &adapter,
DBUS_TYPE_STRING, &address,
DBUS_TYPE_STRING, &service,
DBUS_TYPE_OBJECT_PATH, &object_path,
DBUS_TYPE_STRING, &uuid,
DBUS_TYPE_INVALID)) {
LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
LOGV("... address = %s", address);
LOGV("... service = %s", service);
LOGV("... object_path = %s", object_path);
LOGV("... uuid = %s", uuid);
bool auth_granted = env->CallBooleanMethod(nat->me,
method_onAuthAgentAuthorize, env->NewStringUTF(address),
env->NewStringUTF(service), env->NewStringUTF(uuid));
bool auth_granted =
env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
env->NewStringUTF(object_path), env->NewStringUTF(uuid));
// reply
if (auth_granted) {
@@ -979,43 +844,22 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn,
}
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(msg,
"org.bluez.AuthorizationAgent", "Cancel")) {
const char *adapter;
const char *address;
const char *service;
const char *uuid;
"org.bluez.Agent", "RequestPinCode")) {
char *object_path;
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &adapter,
DBUS_TYPE_STRING, &address,
DBUS_TYPE_STRING, &service,
DBUS_TYPE_STRING, &uuid,
DBUS_TYPE_OBJECT_PATH, &object_path,
DBUS_TYPE_INVALID)) {
LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__);
LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
LOGV("... address = %s", address);
LOGV("... service = %s", service);
LOGV("... uuid = %s", uuid);
env->CallVoidMethod(nat->me,
method_onAuthAgentCancel, env->NewStringUTF(address),
env->NewStringUTF(service), env->NewStringUTF(uuid));
// reply
DBusMessage *reply = dbus_message_new_method_return(msg);
if (!reply) {
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
dbus_connection_send(nat->conn, reply, NULL);
dbus_message_unref(reply);
dbus_message_ref(msg); // increment refcount because we pass to java
env->CallVoidMethod(nat->me, method_onRequestPinCode,
env->NewStringUTF(object_path),
int(msg));
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(msg,
"org.bluez.AuthorizationAgent", "Release")) {
LOGW("We are no longer the auth agent!");
"org.bluez.Agent", "Release")) {
// reply
DBusMessage *reply = dbus_message_new_method_return(msg);
if (!reply) {
@@ -1026,7 +870,7 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn,
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
} else {
LOGV("... ignored");
LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg));
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -1044,7 +888,7 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn,
#define BOND_RESULT_REMOTE_DEVICE_DOWN 4
#define BOND_RESULT_DISCOVERY_IN_PROGRESS 5
void onCreateBondingResult(DBusMessage *msg, void *user, void *n) {
void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) {
LOGV(__FUNCTION__);
native_data_t *nat = (native_data_t *)n;
@@ -1095,7 +939,7 @@ void onCreateBondingResult(DBusMessage *msg, void *user, void *n) {
}
env->CallVoidMethod(nat->me,
method_onCreateBondingResult,
method_onCreatePairedDeviceResult,
env->NewStringUTF(address),
result);
done:
@@ -1103,7 +947,7 @@ done:
free(user);
}
void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) {
void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
LOGV(__FUNCTION__);
const char *address = (const char *) user;
@@ -1122,14 +966,13 @@ void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) {
!dbus_message_get_args(msg, &err,
DBUS_TYPE_INT32, &channel,
DBUS_TYPE_INVALID)) {
/* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
dbus_error_free(&err);
}
done:
env->CallVoidMethod(nat->me,
method_onGetRemoteServiceChannelResult,
method_onGetDeviceServiceChannelResult,
env->NewStringUTF(address),
channel);
free(user);

View File

@@ -51,8 +51,7 @@ ifeq ($(BOARD_HAVE_BLUETOOTH),true)
LOCAL_SRC_FILES += A2dpAudioInterface.cpp
LOCAL_SHARED_LIBRARIES += liba2dp
LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs)
LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils)
LOCAL_C_INCLUDES += $(call include-path-for, bluez)
endif
include $(BUILD_SHARED_LIBRARY)

View File

@@ -173,7 +173,7 @@ class ServerThread extends Thread {
bluetooth = new BluetoothDeviceService(context);
bluetooth.init();
ServiceManager.addService(Context.BLUETOOTH_SERVICE, bluetooth);
bluetoothA2dp = new BluetoothA2dpService(context);
bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
bluetoothA2dp);
@@ -414,19 +414,19 @@ public class SystemServer
public static final int FACTORY_TEST_OFF = 0;
public static final int FACTORY_TEST_LOW_LEVEL = 1;
public static final int FACTORY_TEST_HIGH_LEVEL = 2;
/**
* This method is called from Zygote to initialize the system. This will cause the native
/**
* This method is called from Zygote to initialize the system. This will cause the native
* services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
* up into init2() to start the Android services.
*/
*/
native public static void init1(String[] args);
public static void main(String[] args) {
// The system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
System.loadLibrary("android_servers");
init1(args);
}