Framework changes for bluez4.
Changes in the Bluetooth JNI calls and framework functions for Bluez4.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
62
core/java/android/bluetooth/BluetoothUuid.java
Normal file
62
core/java/android/bluetooth/BluetoothUuid.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user