mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
2635 lines
85 KiB
C++
2635 lines
85 KiB
C++
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/*
|
|
** Copyright 2006, 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.
|
|
*/
|
|
|
|
#include "base/basictypes.h"
|
|
#include "BluetoothDBusService.h"
|
|
#include "BluetoothHfpManager.h"
|
|
#include "BluetoothOppManager.h"
|
|
#include "BluetoothReplyRunnable.h"
|
|
#include "BluetoothScoManager.h"
|
|
#include "BluetoothUnixSocketConnector.h"
|
|
#include "BluetoothUtils.h"
|
|
#include "BluetoothUuid.h"
|
|
|
|
#include <cstdio>
|
|
#include <dbus/dbus.h>
|
|
|
|
#include "nsAutoPtr.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsDebug.h"
|
|
#include "nsDataHashtable.h"
|
|
#include "mozilla/Hal.h"
|
|
#include "mozilla/ipc/UnixSocket.h"
|
|
#include "mozilla/ipc/DBusThread.h"
|
|
#include "mozilla/ipc/DBusUtils.h"
|
|
#include "mozilla/ipc/RawDBusConnection.h"
|
|
#include "mozilla/Util.h"
|
|
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
|
#if defined(MOZ_WIDGET_GONK)
|
|
#include "cutils/properties.h"
|
|
#endif
|
|
|
|
/**
|
|
* Some rules for dealing with memory in DBus:
|
|
* - A DBusError only needs to be deleted if it's been set, not just
|
|
* initialized. This is why LOG_AND_FREE... is called only when an error is
|
|
* set, and the macro cleans up the error itself.
|
|
* - A DBusMessage needs to be unrefed when it is newed explicitly. DBusMessages
|
|
* from signals do not need to be unrefed, as they will be cleaned by DBus
|
|
* after DBUS_HANDLER_RESULT_HANDLED is returned from the filter.
|
|
*/
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::ipc;
|
|
USING_BLUETOOTH_NAMESPACE
|
|
|
|
#undef LOG
|
|
#if defined(MOZ_WIDGET_GONK)
|
|
#include <android/log.h>
|
|
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkDBus", args);
|
|
#else
|
|
#define BTDEBUG true
|
|
#define LOG(args...) if (BTDEBUG) printf(args);
|
|
#endif
|
|
|
|
#define B2G_AGENT_CAPABILITIES "DisplayYesNo"
|
|
#define DBUS_MANAGER_IFACE BLUEZ_DBUS_BASE_IFC ".Manager"
|
|
#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
|
|
#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
|
|
#define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent"
|
|
#define BLUEZ_DBUS_BASE_PATH "/org/bluez"
|
|
#define BLUEZ_DBUS_BASE_IFC "org.bluez"
|
|
#define BLUEZ_ERROR_IFC "org.bluez.Error"
|
|
|
|
#define PROP_DEVICE_CONNECTED_TYPE "org.bluez.device.conn.type"
|
|
|
|
typedef struct {
|
|
const char* name;
|
|
int type;
|
|
} Properties;
|
|
|
|
static Properties sDeviceProperties[] = {
|
|
{"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_ARRAY},
|
|
{"Trusted", DBUS_TYPE_BOOLEAN},
|
|
{"Blocked", 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},
|
|
{"Type", DBUS_TYPE_STRING},
|
|
{"Broadcaster", DBUS_TYPE_BOOLEAN},
|
|
{"Services", DBUS_TYPE_ARRAY}
|
|
};
|
|
|
|
static Properties sAdapterProperties[] = {
|
|
{"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},
|
|
{"UUIDs", DBUS_TYPE_ARRAY},
|
|
{"Type", DBUS_TYPE_STRING}
|
|
};
|
|
|
|
static Properties sManagerProperties[] = {
|
|
{"Adapters", DBUS_TYPE_ARRAY},
|
|
};
|
|
|
|
static const char* sBluetoothDBusIfaces[] =
|
|
{
|
|
DBUS_MANAGER_IFACE,
|
|
DBUS_ADAPTER_IFACE,
|
|
DBUS_DEVICE_IFACE
|
|
};
|
|
|
|
static const char* sBluetoothDBusSignals[] =
|
|
{
|
|
"type='signal',interface='org.freedesktop.DBus'",
|
|
"type='signal',interface='org.bluez.Adapter'",
|
|
"type='signal',interface='org.bluez.Manager'",
|
|
"type='signal',interface='org.bluez.Device'",
|
|
"type='signal',interface='org.bluez.Input'",
|
|
"type='signal',interface='org.bluez.Network'",
|
|
"type='signal',interface='org.bluez.NetworkServer'",
|
|
"type='signal',interface='org.bluez.HealthDevice'",
|
|
"type='signal',interface='org.bluez.AudioSink'"
|
|
};
|
|
|
|
/**
|
|
* DBus Connection held for the BluetoothCommandThread to use. Should never be
|
|
* used by any other thread.
|
|
*
|
|
*/
|
|
static nsAutoPtr<RawDBusConnection> gThreadConnection;
|
|
static nsDataHashtable<nsStringHashKey, DBusMessage* > sPairingReqTable;
|
|
static nsDataHashtable<nsStringHashKey, DBusMessage* > sAuthorizeReqTable;
|
|
typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&);
|
|
|
|
class DistributeBluetoothSignalTask : public nsRunnable {
|
|
BluetoothSignal mSignal;
|
|
public:
|
|
DistributeBluetoothSignalTask(const BluetoothSignal& aSignal) :
|
|
mSignal(aSignal)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
BluetoothService* bs = BluetoothService::Get();
|
|
if (!bs) {
|
|
NS_WARNING("BluetoothService not available!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
bs->DistributeSignal(mSignal);
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class PrepareAdapterTask : public nsRunnable {
|
|
public:
|
|
PrepareAdapterTask(const nsAString& aPath) :
|
|
mPath(aPath)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
BluetoothService* bs = BluetoothService::Get();
|
|
if (!bs) {
|
|
NS_WARNING("BluetoothService not available!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Due to the fact that we need to queue the dbus call to the command thread
|
|
// inside the bluetoothservice, we have to route the call down to the main
|
|
// thread and then back out to the command thread. There has to be a better
|
|
// way to do this.
|
|
if (NS_FAILED(bs->PrepareAdapterInternal(mPath))) {
|
|
NS_WARNING("Prepare adapter failed");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsString mPath;
|
|
};
|
|
|
|
class DevicePropertiesSignalHandler : public nsRunnable
|
|
{
|
|
public:
|
|
DevicePropertiesSignalHandler(const BluetoothSignal& aSignal) :
|
|
mSignal(aSignal)
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Get device properties and then send to BluetoothAdapter
|
|
BluetoothService* bs = BluetoothService::Get();
|
|
if (!bs) {
|
|
NS_WARNING("BluetoothService not available!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Due to the fact that we need to queue the dbus call to the command thread
|
|
// inside the bluetoothservice, we have to route the call down to the main
|
|
// thread and then back out to the command thread. There has to be a better
|
|
// way to do this.
|
|
if (NS_FAILED(bs->GetDevicePropertiesInternal(mSignal))) {
|
|
NS_WARNING("Get device properties failed");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
BluetoothSignal mSignal;
|
|
};
|
|
|
|
static bool
|
|
IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr)
|
|
{
|
|
if (aErr && dbus_error_is_set(aErr)) {
|
|
aErrorStr = NS_ConvertUTF8toUTF16(aErr->message);
|
|
LOG_AND_FREE_DBUS_ERROR(aErr);
|
|
return true;
|
|
}
|
|
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
if (dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_ERROR) {
|
|
const char* error_msg;
|
|
if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_STRING,
|
|
&error_msg, DBUS_TYPE_INVALID) ||
|
|
!error_msg) {
|
|
if (dbus_error_is_set(&err)) {
|
|
aErrorStr = NS_ConvertUTF8toUTF16(err.message);
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
return true;
|
|
} else {
|
|
aErrorStr.AssignLiteral("Unknown Error");
|
|
return true;
|
|
}
|
|
} else {
|
|
aErrorStr = NS_ConvertUTF8toUTF16(error_msg);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
UnpackIntMessage(DBusMessage* aMsg, DBusError* aErr,
|
|
BluetoothValue& aValue, nsAString& aErrorStr)
|
|
{
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
if (!IsDBusMessageError(aMsg, aErr, aErrorStr)) {
|
|
NS_ASSERTION(dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN,
|
|
"Got dbus callback that's not a METHOD_RETURN!");
|
|
int i;
|
|
if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_INT32,
|
|
&i, DBUS_TYPE_INVALID)) {
|
|
if (dbus_error_is_set(&err)) {
|
|
aErrorStr = NS_ConvertUTF8toUTF16(err.message);
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
}
|
|
} else {
|
|
aValue = (uint32_t)i;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
UnpackObjectPathMessage(DBusMessage* aMsg, DBusError* aErr,
|
|
BluetoothValue& aValue, nsAString& aErrorStr)
|
|
{
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
if (!IsDBusMessageError(aMsg, aErr, aErrorStr)) {
|
|
NS_ASSERTION(dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN,
|
|
"Got dbus callback that's not a METHOD_RETURN!");
|
|
const char* object_path;
|
|
if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_OBJECT_PATH,
|
|
&object_path, DBUS_TYPE_INVALID) ||
|
|
!object_path) {
|
|
if (dbus_error_is_set(&err)) {
|
|
aErrorStr = NS_ConvertUTF8toUTF16(err.message);
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
}
|
|
} else {
|
|
aValue = NS_ConvertUTF8toUTF16(object_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
KeepDBusPairingMessage(const nsString& aDeviceAddress, DBusMessage* aMsg)
|
|
{
|
|
sPairingReqTable.Put(aDeviceAddress, aMsg);
|
|
|
|
// Increase ref count here because we need this message later.
|
|
// It'll be unrefed when set*Internal() is called.
|
|
dbus_message_ref(aMsg);
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data)
|
|
{
|
|
if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
|
|
LOG("%s: agent handler not interested (not a method call).\n", __FUNCTION__);
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
|
|
nsString signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(msg));
|
|
nsString signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(msg));
|
|
nsString errorStr;
|
|
BluetoothValue v;
|
|
InfallibleTArray<BluetoothNamedValue> parameters;
|
|
|
|
// The following descriptions of each signal are retrieved from:
|
|
//
|
|
// http://maemo.org/api_refs/5.0/beta/bluez/agent.html
|
|
//
|
|
if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Cancel")) {
|
|
// This method gets called to indicate that the agent request failed before a reply
|
|
// was returned.
|
|
|
|
// Return directly
|
|
DBusMessage *reply = dbus_message_new_method_return(msg);
|
|
|
|
if (!reply) {
|
|
errorStr.AssignLiteral("Memory can't be allocated for the message.");
|
|
} else {
|
|
dbus_connection_send(conn, reply, NULL);
|
|
dbus_message_unref(reply);
|
|
v = parameters;
|
|
}
|
|
} else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Authorize")) {
|
|
// This method gets called when the service daemon needs to authorize a
|
|
// connection/service request.
|
|
char *objectPath;
|
|
const char *uuid;
|
|
if (!dbus_message_get_args(msg, NULL,
|
|
DBUS_TYPE_OBJECT_PATH, &objectPath,
|
|
DBUS_TYPE_STRING, &uuid,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG("%s: Invalid arguments for Authorize() method", __FUNCTION__);
|
|
errorStr.AssignLiteral("Invalid arguments for Authorize() method");
|
|
} else {
|
|
nsString deviceAddress =
|
|
GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
|
|
|
|
parameters.AppendElement(BluetoothNamedValue(
|
|
NS_LITERAL_STRING("deviceAddress"),
|
|
deviceAddress));
|
|
parameters.AppendElement(BluetoothNamedValue(
|
|
NS_LITERAL_STRING("uuid"),
|
|
NS_ConvertUTF8toUTF16(uuid)));
|
|
|
|
// Because we may have authorization request and pairing request from the
|
|
// same remote device at the same time, we need two tables to keep these messages.
|
|
sAuthorizeReqTable.Put(deviceAddress, msg);
|
|
|
|
// Increase ref count here because we need this message later.
|
|
// It'll be unrefed when setAuthorizationInternal() is called.
|
|
dbus_message_ref(msg);
|
|
|
|
v = parameters;
|
|
}
|
|
} else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestConfirmation")) {
|
|
// This method gets called when the service daemon needs to confirm a passkey for
|
|
// an authentication.
|
|
char *objectPath;
|
|
uint32_t passkey;
|
|
if (!dbus_message_get_args(msg, NULL,
|
|
DBUS_TYPE_OBJECT_PATH, &objectPath,
|
|
DBUS_TYPE_UINT32, &passkey,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
|
|
errorStr.AssignLiteral("Invalid arguments for RequestConfirmation() method");
|
|
} else {
|
|
parameters.AppendElement(BluetoothNamedValue(
|
|
NS_LITERAL_STRING("address"),
|
|
NS_ConvertUTF8toUTF16(objectPath)));
|
|
parameters.AppendElement(BluetoothNamedValue(
|
|
NS_LITERAL_STRING("passkey"),
|
|
passkey));
|
|
|
|
KeepDBusPairingMessage(GetAddressFromObjectPath(
|
|
NS_ConvertUTF8toUTF16(objectPath)), msg);
|
|
|
|
BluetoothSignal signal(signalName, signalPath, parameters);
|
|
|
|
// Fire a Device properties fetcher at the main thread
|
|
nsRefPtr<DevicePropertiesSignalHandler> b =
|
|
new DevicePropertiesSignalHandler(signal);
|
|
if (NS_FAILED(NS_DispatchToMainThread(b))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
// Since we're handling this in other threads, just fall out here
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
} else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPinCode")) {
|
|
// This method gets called when the service daemon needs to get the passkey for an
|
|
// authentication. The return value should be a string of 1-16 characters length.
|
|
// The string can be alphanumeric.
|
|
char *objectPath;
|
|
if (!dbus_message_get_args(msg, NULL,
|
|
DBUS_TYPE_OBJECT_PATH, &objectPath,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
|
|
errorStr.AssignLiteral("Invalid arguments for RequestPinCode() method");
|
|
} else {
|
|
parameters.AppendElement(BluetoothNamedValue(
|
|
NS_LITERAL_STRING("address"),
|
|
NS_ConvertUTF8toUTF16(objectPath)));
|
|
|
|
KeepDBusPairingMessage(GetAddressFromObjectPath(
|
|
NS_ConvertUTF8toUTF16(objectPath)), msg);
|
|
|
|
BluetoothSignal signal(signalName, signalPath, parameters);
|
|
|
|
// Fire a Device properties fetcher at the main thread
|
|
nsRefPtr<DevicePropertiesSignalHandler> b =
|
|
new DevicePropertiesSignalHandler(signal);
|
|
if (NS_FAILED(NS_DispatchToMainThread(b))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
// Since we're handling this in other threads, just fall out here
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
} else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPasskey")) {
|
|
// This method gets called when the service daemon needs to get the passkey for an
|
|
// authentication. The return value should be a numeric value between 0-999999.
|
|
char *objectPath;
|
|
if (!dbus_message_get_args(msg, NULL,
|
|
DBUS_TYPE_OBJECT_PATH, &objectPath,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
|
|
errorStr.AssignLiteral("Invalid arguments for RequestPasskey() method");
|
|
} else {
|
|
parameters.AppendElement(BluetoothNamedValue(
|
|
NS_LITERAL_STRING("address"),
|
|
NS_ConvertUTF8toUTF16(objectPath)));
|
|
|
|
KeepDBusPairingMessage(GetAddressFromObjectPath(
|
|
NS_ConvertUTF8toUTF16(objectPath)), msg);
|
|
|
|
BluetoothSignal signal(signalName, signalPath, parameters);
|
|
|
|
// Fire a Device properties fetcher at the main thread
|
|
nsRefPtr<DevicePropertiesSignalHandler> b =
|
|
new DevicePropertiesSignalHandler(signal);
|
|
if (NS_FAILED(NS_DispatchToMainThread(b))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
// Since we're handling this in other threads, just fall out here
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
} else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Release")) {
|
|
// This method gets called when the service daemon unregisters the agent. An agent
|
|
// can use it to do cleanup tasks. There is no need to unregister the agent, because
|
|
// when this method gets called it has already been unregistered.
|
|
DBusMessage *reply = dbus_message_new_method_return(msg);
|
|
|
|
if (!reply) {
|
|
errorStr.AssignLiteral("Memory can't be allocated for the message.");
|
|
} else {
|
|
dbus_connection_send(conn, reply, NULL);
|
|
dbus_message_unref(reply);
|
|
|
|
// Do not send an notification to upper layer, too annoying.
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
LOG("agent handler %s: Unhandled event. Ignore.", __FUNCTION__);
|
|
#endif
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
if (!errorStr.IsEmpty()) {
|
|
NS_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
BluetoothSignal signal(signalName, signalPath, v);
|
|
nsRefPtr<DistributeBluetoothSignalTask> t =
|
|
new DistributeBluetoothSignalTask(signal);
|
|
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
static const DBusObjectPathVTable agentVtable = {
|
|
NULL, AgentEventFilter, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
// Local agent means agent for Adapter, not agent for Device. Some signals
|
|
// will be passed to local agent, some will be passed to device agent.
|
|
// For example, if a remote device would like to pair with us, then the
|
|
// signal will be passed to local agent. If we start pairing process with
|
|
// calling CreatePairedDevice, we'll get signal which should be passed to
|
|
// device agent.
|
|
static bool
|
|
RegisterLocalAgent(const char* adapterPath,
|
|
const char* agentPath,
|
|
const char* capabilities)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(),
|
|
agentPath,
|
|
&agentVtable,
|
|
NULL)) {
|
|
LOG("%s: Can't register object path %s for agent!",
|
|
__FUNCTION__, agentPath);
|
|
return false;
|
|
}
|
|
|
|
DBusMessage* msg =
|
|
dbus_message_new_method_call("org.bluez", adapterPath,
|
|
DBUS_ADAPTER_IFACE, "RegisterAgent");
|
|
if (!msg) {
|
|
LOG("%s: Can't allocate new method call for agent!", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
if (!dbus_message_append_args(msg,
|
|
DBUS_TYPE_OBJECT_PATH, &agentPath,
|
|
DBUS_TYPE_STRING, &capabilities,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
|
|
DBusMessage* reply =
|
|
dbus_connection_send_with_reply_and_block(gThreadConnection->GetConnection(),
|
|
msg, -1, &err);
|
|
dbus_message_unref(msg);
|
|
|
|
if (!reply) {
|
|
if (dbus_error_is_set(&err)) {
|
|
if (!strcmp(err.name, "org.bluez.Error.AlreadyExists")) {
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
#ifdef DEBUG
|
|
LOG("Agent already registered, still returning true");
|
|
#endif
|
|
} else {
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
LOG("%s: Can't register agent!", __FUNCTION__);
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
dbus_message_unref(reply);
|
|
}
|
|
|
|
dbus_connection_flush(gThreadConnection->GetConnection());
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
RegisterAgent(const nsAString& aAdapterPath)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
if (!RegisterLocalAgent(NS_ConvertUTF16toUTF8(aAdapterPath).get(),
|
|
LOCAL_AGENT_PATH,
|
|
B2G_AGENT_CAPABILITIES)) {
|
|
return false;
|
|
}
|
|
|
|
// There is no "RegisterAgent" function defined in device interface.
|
|
// When we call "CreatePairedDevice", it will do device agent registration for us.
|
|
// (See maemo.org/api_refs/5.0/beta/bluez/adapter.html)
|
|
if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(),
|
|
REMOTE_AGENT_PATH,
|
|
&agentVtable,
|
|
NULL)) {
|
|
LOG("%s: Can't register object path %s for remote device agent!",
|
|
__FUNCTION__, REMOTE_AGENT_PATH);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
ExtractHandles(DBusMessage *aReply, nsTArray<uint32_t>& aOutHandles)
|
|
{
|
|
uint32_t* handles = NULL;
|
|
int len;
|
|
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
|
|
if (dbus_message_get_args(aReply, &err,
|
|
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &handles, &len,
|
|
DBUS_TYPE_INVALID)) {
|
|
if (!handles) {
|
|
LOG("Null array in extract_handles");
|
|
} else {
|
|
for (int i = 0; i < len; ++i) {
|
|
aOutHandles.AppendElement(handles[i]);
|
|
}
|
|
}
|
|
} else {
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool
|
|
BluetoothDBusService::AddServiceRecords(const nsAString& aAdapterPath,
|
|
const char* serviceName,
|
|
unsigned long long uuidMsb,
|
|
unsigned long long uuidLsb,
|
|
int channel)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
DBusMessage *reply;
|
|
reply = dbus_func_args(gThreadConnection->GetConnection(),
|
|
NS_ConvertUTF16toUTF8(aAdapterPath).get(),
|
|
DBUS_ADAPTER_IFACE, "AddRfcommServiceRecord",
|
|
DBUS_TYPE_STRING, &serviceName,
|
|
DBUS_TYPE_UINT64, &uuidMsb,
|
|
DBUS_TYPE_UINT64, &uuidLsb,
|
|
DBUS_TYPE_UINT16, &channel,
|
|
DBUS_TYPE_INVALID);
|
|
|
|
return reply ? dbus_returns_uint32(reply) : -1;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
BluetoothDBusService::AddReservedServicesInternal(const nsAString& aAdapterPath,
|
|
const nsTArray<uint32_t>& aServices,
|
|
nsTArray<uint32_t>& aServiceHandlesContainer)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
int length = aServices.Length();
|
|
if (length == 0) return false;
|
|
|
|
const uint32_t* services = aServices.Elements();
|
|
DBusMessage* reply =
|
|
dbus_func_args(gThreadConnection->GetConnection(),
|
|
NS_ConvertUTF16toUTF8(aAdapterPath).get(),
|
|
DBUS_ADAPTER_IFACE, "AddReservedServiceRecords",
|
|
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
|
|
&services, length, DBUS_TYPE_INVALID);
|
|
|
|
if (!reply) {
|
|
LOG("Null DBus message. Couldn't extract handles.");
|
|
return false;
|
|
}
|
|
|
|
ExtractHandles(reply, aServiceHandlesContainer);
|
|
return true;
|
|
}
|
|
|
|
class PrepareProfileManagersRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
NS_IMETHOD
|
|
Run()
|
|
{
|
|
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
|
if (!hfp || !hfp->Listen()) {
|
|
NS_WARNING("Failed to start listening for BluetoothHfpManager!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
BluetoothScoManager* sco = BluetoothScoManager::Get();
|
|
if (!sco || !sco->Listen()) {
|
|
NS_WARNING("Failed to start listening for BluetoothScoManager!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
|
if (!opp || !opp->Listen()) {
|
|
NS_WARNING("Failed to start listening for BluetoothOppManager!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class ShutdownProfileManagersRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
NS_IMETHOD
|
|
Run()
|
|
{
|
|
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
|
if (hfp) {
|
|
hfp->CloseSocket();
|
|
}
|
|
|
|
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
|
if (opp) {
|
|
opp->CloseSocket();
|
|
}
|
|
|
|
BluetoothScoManager* sco = BluetoothScoManager::Get();
|
|
if (sco) {
|
|
sco->CloseSocket();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class PrepareAdapterRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
PrepareAdapterRunnable(const nsAString& aPath) :
|
|
mPath(aPath)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run()
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
nsTArray<uint32_t> uuids;
|
|
|
|
uuids.AppendElement(BluetoothServiceClass::HANDSFREE_AG);
|
|
uuids.AppendElement(BluetoothServiceClass::HEADSET_AG);
|
|
uuids.AppendElement(BluetoothServiceClass::OBJECT_PUSH);
|
|
|
|
// TODO/qdot: This needs to be held for the life of the bluetooth connection
|
|
// so we could clean it up. For right now though, we can throw it away.
|
|
nsTArray<uint32_t> handles;
|
|
|
|
if (!BluetoothDBusService::AddReservedServicesInternal(mPath, uuids, handles)) {
|
|
NS_WARNING("Failed to add reserved services");
|
|
#ifdef MOZ_WIDGET_GONK
|
|
return NS_ERROR_FAILURE;
|
|
#endif
|
|
}
|
|
|
|
if(!RegisterAgent(mPath)) {
|
|
NS_WARNING("Failed to register agent");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_DispatchToMainThread(new PrepareProfileManagersRunnable());
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsString mPath;
|
|
};
|
|
|
|
void
|
|
RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable,
|
|
UnpackFunc aFunc)
|
|
{
|
|
#ifdef MOZ_WIDGET_GONK
|
|
// Due to the fact that we're running two dbus loops on desktop implicitly by
|
|
// being gtk based, sometimes we'll get signals/reply coming in on the main
|
|
// thread. There's not a lot we can do about that for the time being and it
|
|
// (technically) shouldn't hurt anything. However, on gonk, die.
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
#endif
|
|
nsRefPtr<BluetoothReplyRunnable> replyRunnable =
|
|
dont_AddRef(static_cast< BluetoothReplyRunnable* >(aBluetoothReplyRunnable));
|
|
|
|
NS_ASSERTION(replyRunnable, "Callback reply runnable is null!");
|
|
|
|
nsString replyError;
|
|
BluetoothValue v;
|
|
aFunc(aMsg, nullptr, v, replyError);
|
|
DispatchBluetoothReply(replyRunnable, v, replyError);
|
|
}
|
|
|
|
void
|
|
GetObjectPathCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
|
|
{
|
|
RunDBusCallback(aMsg, aBluetoothReplyRunnable,
|
|
UnpackObjectPathMessage);
|
|
}
|
|
|
|
void
|
|
UnpackVoidMessage(DBusMessage* aMsg, DBusError* aErr, BluetoothValue& aValue,
|
|
nsAString& aErrorStr)
|
|
{
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
if (!IsDBusMessageError(aMsg, aErr, aErrorStr) &&
|
|
dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN &&
|
|
!dbus_message_get_args(aMsg, &err, DBUS_TYPE_INVALID)) {
|
|
if (dbus_error_is_set(&err)) {
|
|
aErrorStr = NS_ConvertUTF8toUTF16(err.message);
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
}
|
|
}
|
|
// XXXbent Need to figure out something better than this here.
|
|
if (aErrorStr.IsEmpty()) {
|
|
aValue = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
GetVoidCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
|
|
{
|
|
RunDBusCallback(aMsg, aBluetoothReplyRunnable,
|
|
UnpackVoidMessage);
|
|
}
|
|
|
|
void
|
|
GetIntCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
|
|
{
|
|
RunDBusCallback(aMsg, aBluetoothReplyRunnable,
|
|
UnpackIntMessage);
|
|
}
|
|
|
|
bool
|
|
IsDeviceConnectedTypeBoolean()
|
|
{
|
|
#if defined(MOZ_WIDGET_GONK)
|
|
char connProp[PROPERTY_VALUE_MAX];
|
|
|
|
property_get(PROP_DEVICE_CONNECTED_TYPE, connProp, "boolean");
|
|
if (strcmp(connProp, "boolean") == 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
#else
|
|
// Assume it's always a boolean on desktop. Fixing someday in Bug 806457.
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
CopyProperties(Properties* inProp, Properties* outProp, int aPropertyTypeLen)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < aPropertyTypeLen; i++) {
|
|
outProp[i].name = inProp[i].name;
|
|
outProp[i].type = inProp[i].type;
|
|
}
|
|
}
|
|
|
|
int
|
|
GetPropertyIndex(Properties* prop, const char* propertyName,
|
|
int aPropertyTypeLen)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < aPropertyTypeLen; i++) {
|
|
if (!strncmp(propertyName, prop[i].name, strlen(propertyName))) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool
|
|
GetProperty(DBusMessageIter aIter, Properties* aPropertyTypes,
|
|
int aPropertyTypeLen, int* aPropIndex,
|
|
InfallibleTArray<BluetoothNamedValue>& aProperties)
|
|
{
|
|
DBusMessageIter prop_val, array_val_iter;
|
|
char* property = NULL;
|
|
uint32_t array_type;
|
|
int i, type;
|
|
|
|
if (dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_STRING) {
|
|
return false;
|
|
}
|
|
|
|
dbus_message_iter_get_basic(&aIter, &property);
|
|
|
|
if (!dbus_message_iter_next(&aIter) ||
|
|
dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_VARIANT) {
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < aPropertyTypeLen; i++) {
|
|
if (!strncmp(property, aPropertyTypes[i].name, strlen(property))) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == aPropertyTypeLen) {
|
|
return false;
|
|
}
|
|
|
|
nsString propertyName;
|
|
propertyName.AssignASCII(aPropertyTypes[i].name);
|
|
*aPropIndex = i;
|
|
|
|
dbus_message_iter_recurse(&aIter, &prop_val);
|
|
type = aPropertyTypes[*aPropIndex].type;
|
|
|
|
if (dbus_message_iter_get_arg_type(&prop_val) != type) {
|
|
NS_WARNING("Iterator not type we expect!");
|
|
nsAutoCString str;
|
|
str += "Property Name: ;";
|
|
str += NS_ConvertUTF16toUTF8(propertyName);
|
|
str += " Property Type Expected: ;";
|
|
str += type;
|
|
str += " Property Type Received: ";
|
|
str += dbus_message_iter_get_arg_type(&prop_val);
|
|
NS_WARNING(str.get());
|
|
return false;
|
|
}
|
|
|
|
BluetoothValue propertyValue;
|
|
switch (type) {
|
|
case DBUS_TYPE_STRING:
|
|
case DBUS_TYPE_OBJECT_PATH:
|
|
const char* c;
|
|
dbus_message_iter_get_basic(&prop_val, &c);
|
|
propertyValue = NS_ConvertUTF8toUTF16(c);
|
|
break;
|
|
case DBUS_TYPE_UINT32:
|
|
case DBUS_TYPE_INT16:
|
|
uint32_t i;
|
|
dbus_message_iter_get_basic(&prop_val, &i);
|
|
propertyValue = i;
|
|
break;
|
|
case DBUS_TYPE_BOOLEAN:
|
|
bool b;
|
|
dbus_message_iter_get_basic(&prop_val, &b);
|
|
propertyValue = b;
|
|
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);
|
|
if (array_type == DBUS_TYPE_OBJECT_PATH ||
|
|
array_type == DBUS_TYPE_STRING) {
|
|
InfallibleTArray<nsString> arr;
|
|
do {
|
|
const char* tmp;
|
|
dbus_message_iter_get_basic(&array_val_iter, &tmp);
|
|
nsString s;
|
|
s = NS_ConvertUTF8toUTF16(tmp);
|
|
arr.AppendElement(s);
|
|
} while (dbus_message_iter_next(&array_val_iter));
|
|
propertyValue = arr;
|
|
} else if (array_type == DBUS_TYPE_BYTE) {
|
|
InfallibleTArray<uint8_t> arr;
|
|
do {
|
|
uint8_t tmp;
|
|
dbus_message_iter_get_basic(&array_val_iter, &tmp);
|
|
arr.AppendElement(tmp);
|
|
} while (dbus_message_iter_next(&array_val_iter));
|
|
propertyValue = arr;
|
|
} else {
|
|
// This happens when the array is 0-length. Apparently we get a
|
|
// DBUS_TYPE_INVALID type.
|
|
propertyValue = InfallibleTArray<nsString>();
|
|
#ifdef DEBUG
|
|
NS_WARNING("Received array type that's not a string array!");
|
|
#endif
|
|
}
|
|
break;
|
|
default:
|
|
NS_NOTREACHED("Cannot find dbus message type!");
|
|
}
|
|
aProperties.AppendElement(BluetoothNamedValue(propertyName, propertyValue));
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ParseProperties(DBusMessageIter* aIter,
|
|
BluetoothValue& aValue,
|
|
nsAString& aErrorStr,
|
|
Properties* aPropertyTypes,
|
|
const int aPropertyTypeLen)
|
|
{
|
|
DBusMessageIter dict_entry, dict;
|
|
int prop_index = -1;
|
|
|
|
NS_ASSERTION(dbus_message_iter_get_arg_type(aIter) == DBUS_TYPE_ARRAY,
|
|
"Trying to parse a property from something that's not an array!");
|
|
|
|
dbus_message_iter_recurse(aIter, &dict);
|
|
InfallibleTArray<BluetoothNamedValue> props;
|
|
do {
|
|
NS_ASSERTION(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY,
|
|
"Trying to parse a property from something that's not an dict!");
|
|
dbus_message_iter_recurse(&dict, &dict_entry);
|
|
|
|
if (!GetProperty(dict_entry, aPropertyTypes, aPropertyTypeLen, &prop_index,
|
|
props)) {
|
|
aErrorStr.AssignLiteral("Can't Create Property!");
|
|
NS_WARNING("Can't create property!");
|
|
return;
|
|
}
|
|
} while (dbus_message_iter_next(&dict));
|
|
|
|
aValue = props;
|
|
}
|
|
|
|
void
|
|
UnpackPropertiesMessage(DBusMessage* aMsg, DBusError* aErr,
|
|
BluetoothValue& aValue, nsAString& aErrorStr,
|
|
Properties* aPropertyTypes,
|
|
const int aPropertyTypeLen)
|
|
{
|
|
if (!IsDBusMessageError(aMsg, aErr, aErrorStr) &&
|
|
dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
|
|
DBusMessageIter iter;
|
|
if (!dbus_message_iter_init(aMsg, &iter)) {
|
|
aErrorStr.AssignLiteral("Cannot create dbus message iter!");
|
|
} else {
|
|
ParseProperties(&iter, aValue, aErrorStr, aPropertyTypes,
|
|
aPropertyTypeLen);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
UnpackAdapterPropertiesMessage(DBusMessage* aMsg, DBusError* aErr,
|
|
BluetoothValue& aValue,
|
|
nsAString& aErrorStr)
|
|
{
|
|
UnpackPropertiesMessage(aMsg, aErr, aValue, aErrorStr,
|
|
sAdapterProperties,
|
|
ArrayLength(sAdapterProperties));
|
|
}
|
|
|
|
bool
|
|
ReplaceConnectedType(Properties* sourceProperties, Properties** destProperties, int aPropertyTypeLen)
|
|
{
|
|
if (!IsDeviceConnectedTypeBoolean()) {
|
|
return false;
|
|
}
|
|
*destProperties = (Properties *) malloc(sizeof(Properties) * aPropertyTypeLen);
|
|
if (*destProperties) {
|
|
CopyProperties(sourceProperties, *destProperties, aPropertyTypeLen);
|
|
int index = GetPropertyIndex(*destProperties, "Connected", aPropertyTypeLen);
|
|
if (index >= 0) {
|
|
(*destProperties)[index].type = DBUS_TYPE_BOOLEAN;
|
|
return true;
|
|
} else {
|
|
free(*destProperties);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
UnpackDevicePropertiesMessage(DBusMessage* aMsg, DBusError* aErr,
|
|
BluetoothValue& aValue,
|
|
nsAString& aErrorStr)
|
|
{
|
|
Properties* props = sDeviceProperties;
|
|
Properties* newProps;
|
|
bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, ArrayLength(sDeviceProperties));
|
|
if (replaced) {
|
|
props = newProps;
|
|
}
|
|
UnpackPropertiesMessage(aMsg, aErr, aValue, aErrorStr,
|
|
props,
|
|
ArrayLength(sDeviceProperties));
|
|
if (replaced) {
|
|
free(newProps);
|
|
}
|
|
}
|
|
|
|
void
|
|
UnpackManagerPropertiesMessage(DBusMessage* aMsg, DBusError* aErr,
|
|
BluetoothValue& aValue,
|
|
nsAString& aErrorStr)
|
|
{
|
|
UnpackPropertiesMessage(aMsg, aErr, aValue, aErrorStr,
|
|
sManagerProperties,
|
|
ArrayLength(sManagerProperties));
|
|
}
|
|
|
|
void
|
|
GetManagerPropertiesCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
|
|
{
|
|
RunDBusCallback(aMsg, aBluetoothReplyRunnable,
|
|
UnpackManagerPropertiesMessage);
|
|
}
|
|
|
|
void
|
|
GetAdapterPropertiesCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
|
|
{
|
|
RunDBusCallback(aMsg, aBluetoothReplyRunnable,
|
|
UnpackAdapterPropertiesMessage);
|
|
}
|
|
|
|
void
|
|
GetDevicePropertiesCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
|
|
{
|
|
RunDBusCallback(aMsg, aBluetoothReplyRunnable,
|
|
UnpackDevicePropertiesMessage);
|
|
}
|
|
|
|
static DBusCallback sBluetoothDBusPropCallbacks[] =
|
|
{
|
|
GetManagerPropertiesCallback,
|
|
GetAdapterPropertiesCallback,
|
|
GetDevicePropertiesCallback
|
|
};
|
|
|
|
MOZ_STATIC_ASSERT(sizeof(sBluetoothDBusPropCallbacks) == sizeof(sBluetoothDBusIfaces),
|
|
"DBus Property callback array and DBus interface array must be same size");
|
|
|
|
void
|
|
ParsePropertyChange(DBusMessage* aMsg, BluetoothValue& aValue,
|
|
nsAString& aErrorStr, Properties* aPropertyTypes,
|
|
const int aPropertyTypeLen)
|
|
{
|
|
DBusMessageIter iter;
|
|
DBusError err;
|
|
int prop_index = -1;
|
|
InfallibleTArray<BluetoothNamedValue> props;
|
|
|
|
dbus_error_init(&err);
|
|
if (!dbus_message_iter_init(aMsg, &iter)) {
|
|
NS_WARNING("Can't create iterator!");
|
|
return;
|
|
}
|
|
|
|
if (!GetProperty(iter, aPropertyTypes, aPropertyTypeLen,
|
|
&prop_index, props)) {
|
|
NS_WARNING("Can't get property!");
|
|
aErrorStr.AssignLiteral("Can't get property!");
|
|
return;
|
|
}
|
|
aValue = props;
|
|
}
|
|
|
|
bool
|
|
GetPropertiesInternal(const nsAString& aPath, const char* aIface, BluetoothValue& aValue)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
nsString replyError;
|
|
DBusError err;
|
|
|
|
dbus_error_init(&err);
|
|
|
|
DBusMessage* msg = dbus_func_args_timeout(gThreadConnection->GetConnection(),
|
|
1000,
|
|
&err,
|
|
NS_ConvertUTF16toUTF8(aPath).get(),
|
|
aIface,
|
|
"GetProperties",
|
|
DBUS_TYPE_INVALID);
|
|
if (!strcmp(aIface, DBUS_DEVICE_IFACE)) {
|
|
UnpackDevicePropertiesMessage(msg, &err, aValue, replyError);
|
|
} else if (!strcmp(aIface, DBUS_ADAPTER_IFACE)) {
|
|
UnpackAdapterPropertiesMessage(msg, &err, aValue, replyError);
|
|
} else if (!strcmp(aIface, DBUS_MANAGER_IFACE)) {
|
|
UnpackManagerPropertiesMessage(msg, &err, aValue, replyError);
|
|
} else {
|
|
NS_WARNING("Unknown interface for GetProperties!");
|
|
return false;
|
|
}
|
|
|
|
if (!replyError.IsEmpty()) {
|
|
NS_WARNING("Failed to get device properties");
|
|
return false;
|
|
}
|
|
if (msg) {
|
|
dbus_message_unref(msg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Called by dbus during WaitForAndDispatchEventNative()
|
|
// This function is called on the IOThread
|
|
static
|
|
DBusHandlerResult
|
|
EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
|
|
{
|
|
NS_ASSERTION(!NS_IsMainThread(), "Shouldn't be called from Main Thread!");
|
|
|
|
if (dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_SIGNAL) {
|
|
LOG("%s: event handler not interested in %s (not a signal).\n",
|
|
__FUNCTION__, dbus_message_get_member(aMsg));
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
if (dbus_message_get_path(aMsg) == NULL) {
|
|
LOG("DBusMessage %s has no bluetooth destination, ignoring\n",
|
|
dbus_message_get_member(aMsg));
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
DBusError err;
|
|
nsString signalPath;
|
|
nsString signalName;
|
|
dbus_error_init(&err);
|
|
signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(aMsg));
|
|
signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg));
|
|
nsString errorStr;
|
|
BluetoothValue v;
|
|
|
|
if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceFound")) {
|
|
DBusMessageIter iter;
|
|
|
|
if (!dbus_message_iter_init(aMsg, &iter)) {
|
|
NS_WARNING("Can't create iterator!");
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
const char* addr;
|
|
dbus_message_iter_get_basic(&iter, &addr);
|
|
|
|
if (dbus_message_iter_next(&iter)) {
|
|
Properties* props = sDeviceProperties;
|
|
Properties* newProps;
|
|
bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, ArrayLength(sDeviceProperties));
|
|
if (replaced) {
|
|
props = newProps;
|
|
}
|
|
ParseProperties(&iter,
|
|
v,
|
|
errorStr,
|
|
props,
|
|
ArrayLength(sDeviceProperties));
|
|
if (replaced) {
|
|
free(newProps);
|
|
}
|
|
if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue)
|
|
{
|
|
// The DBus DeviceFound message actually passes back a key value object
|
|
// with the address as the key and the rest of the device properties as
|
|
// a dict value. After we parse out the properties, we need to go back
|
|
// and add the address to the ipdl dict we've created to make sure we
|
|
// have all of the information to correctly build the device.
|
|
nsString addrstr = NS_ConvertUTF8toUTF16(addr);
|
|
nsString path = GetObjectPathFromAddress(signalPath, addrstr);
|
|
|
|
v.get_ArrayOfBluetoothNamedValue()
|
|
.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Address"),
|
|
addrstr));
|
|
|
|
// We also need to create a path for the device, to make sure we know
|
|
// where to access it later.
|
|
v.get_ArrayOfBluetoothNamedValue()
|
|
.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Path"),
|
|
path));
|
|
}
|
|
} else {
|
|
errorStr.AssignLiteral("DBus device found message structure not as expected!");
|
|
}
|
|
} else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceDisappeared")) {
|
|
const char* str;
|
|
if (!dbus_message_get_args(aMsg, &err,
|
|
DBUS_TYPE_STRING, &str,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
|
|
errorStr.AssignLiteral("Cannot parse device address!");
|
|
} else {
|
|
v = NS_ConvertUTF8toUTF16(str);
|
|
}
|
|
} else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceCreated")) {
|
|
const char* str;
|
|
if (!dbus_message_get_args(aMsg, &err,
|
|
DBUS_TYPE_OBJECT_PATH, &str,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
|
|
errorStr.AssignLiteral("Cannot parse device path!");
|
|
} else {
|
|
v = NS_ConvertUTF8toUTF16(str);
|
|
}
|
|
|
|
BluetoothSignal signal(signalName, signalPath, v);
|
|
|
|
// Fire a Device properties fetcher at the main thread
|
|
nsRefPtr<DevicePropertiesSignalHandler> b =
|
|
new DevicePropertiesSignalHandler(signal);
|
|
if (NS_FAILED(NS_DispatchToMainThread(b))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
// Since we're handling this in other threads, just fall out here
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
} else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceRemoved")) {
|
|
const char* str;
|
|
if (!dbus_message_get_args(aMsg, &err,
|
|
DBUS_TYPE_OBJECT_PATH, &str,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
|
|
errorStr.AssignLiteral("Cannot parse device path!");
|
|
} else {
|
|
v = NS_ConvertUTF8toUTF16(str);
|
|
}
|
|
} else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "PropertyChanged")) {
|
|
ParsePropertyChange(aMsg,
|
|
v,
|
|
errorStr,
|
|
sAdapterProperties,
|
|
ArrayLength(sAdapterProperties));
|
|
} else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE, "PropertyChanged")) {
|
|
Properties* props = sDeviceProperties;
|
|
Properties* newProps;
|
|
bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, ArrayLength(sDeviceProperties));
|
|
if (replaced) {
|
|
props = newProps;
|
|
}
|
|
ParsePropertyChange(aMsg,
|
|
v,
|
|
errorStr,
|
|
props,
|
|
ArrayLength(sDeviceProperties));
|
|
if (replaced) {
|
|
free(newProps);
|
|
}
|
|
if (v.get_ArrayOfBluetoothNamedValue()[0].name().EqualsLiteral("Paired")) {
|
|
// transfer signal to BluetoothService and
|
|
// broadcast system message of bluetooth-pairingstatuschanged
|
|
signalName = NS_LITERAL_STRING("PairedStatusChanged");
|
|
signalPath = NS_LITERAL_STRING(LOCAL_AGENT_PATH);
|
|
v.get_ArrayOfBluetoothNamedValue()[0].name() = NS_LITERAL_STRING("paired");
|
|
}
|
|
} else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "AdapterAdded")) {
|
|
const char* str;
|
|
if (!dbus_message_get_args(aMsg, &err,
|
|
DBUS_TYPE_OBJECT_PATH, &str,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
|
|
errorStr.AssignLiteral("Cannot parse manager path!");
|
|
} else {
|
|
v = NS_ConvertUTF8toUTF16(str);
|
|
nsRefPtr<PrepareAdapterTask> b = new PrepareAdapterTask(v.get_nsString());
|
|
if (NS_FAILED(NS_DispatchToMainThread(b))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
}
|
|
} else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "PropertyChanged")) {
|
|
ParsePropertyChange(aMsg,
|
|
v,
|
|
errorStr,
|
|
sManagerProperties,
|
|
ArrayLength(sManagerProperties));
|
|
} else {
|
|
#ifdef DEBUG
|
|
nsAutoCString signalStr;
|
|
signalStr += dbus_message_get_member(aMsg);
|
|
signalStr += " Signal not handled!";
|
|
NS_WARNING(signalStr.get());
|
|
#endif
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
if (!errorStr.IsEmpty()) {
|
|
NS_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
BluetoothSignal signal(signalName, signalPath, v);
|
|
nsRefPtr<DistributeBluetoothSignalTask>
|
|
t = new DistributeBluetoothSignalTask(signal);
|
|
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
nsresult
|
|
GetDefaultAdapterPath(BluetoothValue& aValue, nsString& aError)
|
|
{
|
|
// This could block. It should never be run on the main thread.
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
|
|
DBusMessage* msg = dbus_func_args_timeout(gThreadConnection->GetConnection(),
|
|
1000,
|
|
&err,
|
|
"/",
|
|
DBUS_MANAGER_IFACE,
|
|
"DefaultAdapter",
|
|
DBUS_TYPE_INVALID);
|
|
UnpackObjectPathMessage(msg, &err, aValue, aError);
|
|
if (msg) {
|
|
dbus_message_unref(msg);
|
|
}
|
|
if (!aError.IsEmpty()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::StartInternal()
|
|
{
|
|
// This could block. It should never be run on the main thread.
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
if (!StartDBus()) {
|
|
NS_WARNING("Cannot start DBus thread!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (mConnection) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (NS_FAILED(EstablishDBusConnection())) {
|
|
NS_WARNING("Cannot start Main Thread DBus connection!");
|
|
StopDBus();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
gThreadConnection = new RawDBusConnection();
|
|
|
|
if (NS_FAILED(gThreadConnection->EstablishDBusConnection())) {
|
|
NS_WARNING("Cannot start Sync Thread DBus connection!");
|
|
StopDBus();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
|
|
// Set which messages will be processed by this dbus connection.
|
|
// Since we are maintaining a single thread for all the DBus bluez
|
|
// signals we want, register all of them in this thread at startup.
|
|
// The event handler will sort the destinations out as needed.
|
|
for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
|
|
dbus_bus_add_match(mConnection,
|
|
sBluetoothDBusSignals[i],
|
|
&err);
|
|
if (dbus_error_is_set(&err)) {
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
}
|
|
}
|
|
|
|
// Add a filter for all incoming messages_base
|
|
if (!dbus_connection_add_filter(mConnection, EventFilter,
|
|
NULL, NULL)) {
|
|
NS_WARNING("Cannot create DBus Event Filter for DBus Thread!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
sPairingReqTable.Init();
|
|
sAuthorizeReqTable.Init();
|
|
|
|
BluetoothValue v;
|
|
nsString replyError;
|
|
if (NS_FAILED(GetDefaultAdapterPath(v, replyError))) {
|
|
// Adapter path is not ready yet
|
|
// Let's do PrepareAdapterTask when we receive signal 'AdapterAdded'
|
|
} else {
|
|
// Adapter path has been ready, and we won't receive signal 'AdapterAdded' later
|
|
// Let's do PrepareAdapterTask now
|
|
nsRefPtr<PrepareAdapterTask> b = new PrepareAdapterTask(v.get_nsString());
|
|
if (NS_FAILED(NS_DispatchToMainThread(b))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
PLDHashOperator
|
|
UnrefDBusMessages(const nsAString& key, DBusMessage* value, void* arg)
|
|
{
|
|
dbus_message_unref(value);
|
|
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::StopInternal()
|
|
{
|
|
// This could block. It should never be run on the main thread.
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
NS_DispatchToMainThread(new ShutdownProfileManagersRunnable());
|
|
|
|
if (!mConnection) {
|
|
StopDBus();
|
|
return NS_OK;
|
|
}
|
|
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
|
|
dbus_bus_remove_match(mConnection,
|
|
sBluetoothDBusSignals[i],
|
|
&err);
|
|
if (dbus_error_is_set(&err)) {
|
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
|
}
|
|
}
|
|
|
|
dbus_connection_remove_filter(mConnection, EventFilter, nullptr);
|
|
|
|
if (!dbus_connection_unregister_object_path(gThreadConnection->GetConnection(),
|
|
LOCAL_AGENT_PATH)) {
|
|
LOG("%s: Can't unregister object path %s for agent!",
|
|
__FUNCTION__, LOCAL_AGENT_PATH);
|
|
}
|
|
|
|
if (!dbus_connection_unregister_object_path(gThreadConnection->GetConnection(),
|
|
REMOTE_AGENT_PATH)) {
|
|
LOG("%s: Can't unregister object path %s for agent!",
|
|
__FUNCTION__, LOCAL_AGENT_PATH);
|
|
}
|
|
|
|
mConnection = nullptr;
|
|
gThreadConnection = nullptr;
|
|
|
|
// unref stored DBusMessages before clear the hashtable
|
|
sPairingReqTable.EnumerateRead(UnrefDBusMessages, nullptr);
|
|
sPairingReqTable.Clear();
|
|
|
|
sAuthorizeReqTable.EnumerateRead(UnrefDBusMessages, nullptr);
|
|
sAuthorizeReqTable.Clear();
|
|
|
|
StopDBus();
|
|
return NS_OK;
|
|
}
|
|
|
|
class DefaultAdapterPropertiesRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
DefaultAdapterPropertiesRunnable(BluetoothReplyRunnable* aRunnable)
|
|
: mRunnable(dont_AddRef(aRunnable))
|
|
{
|
|
}
|
|
|
|
nsresult
|
|
Run()
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
BluetoothValue v;
|
|
nsString replyError;
|
|
|
|
if (NS_FAILED(GetDefaultAdapterPath(v, replyError))) {
|
|
DispatchBluetoothReply(mRunnable, v, replyError);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
|
|
nsString objectPath = v.get_nsString();
|
|
v = InfallibleTArray<BluetoothNamedValue>();
|
|
if (!GetPropertiesInternal(objectPath, DBUS_ADAPTER_IFACE, v)) {
|
|
NS_WARNING("Getting properties failed!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// We have to manually attach the path to the rest of the elements
|
|
v.get_ArrayOfBluetoothNamedValue().AppendElement(
|
|
BluetoothNamedValue(NS_LITERAL_STRING("Path"), objectPath));
|
|
|
|
DispatchBluetoothReply(mRunnable, v, replyError);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsRefPtr<BluetoothReplyRunnable> mRunnable;
|
|
};
|
|
|
|
nsresult
|
|
BluetoothDBusService::GetDefaultAdapterPathInternal(BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
if (!mConnection || !gThreadConnection) {
|
|
NS_ERROR("Bluetooth service not started yet!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
|
|
nsRefPtr<nsRunnable> func(new DefaultAdapterPropertiesRunnable(runnable));
|
|
if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
|
|
NS_WARNING("Cannot dispatch firmware loading task!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
runnable.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::SendDiscoveryMessage(const nsAString& aAdapterPath,
|
|
const char* aMessageName,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
NS_ASSERTION(mConnection, "Must have a connection here!");
|
|
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
|
|
NS_ConvertUTF16toUTF8 s(aAdapterPath);
|
|
if (!dbus_func_args_async(mConnection,
|
|
1000,
|
|
GetVoidCallback,
|
|
(void*)aRunnable,
|
|
s.get(),
|
|
DBUS_ADAPTER_IFACE,
|
|
aMessageName,
|
|
DBUS_TYPE_INVALID)) {
|
|
NS_WARNING("Could not start async function!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
runnable.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::StopDiscoveryInternal(const nsAString& aAdapterPath,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
if (!mConnection) {
|
|
NS_WARNING("Bluetooth service not started yet, no need to stop discovery.");
|
|
return NS_OK;
|
|
}
|
|
return SendDiscoveryMessage(aAdapterPath, "StopDiscovery", aRunnable);
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::StartDiscoveryInternal(const nsAString& aAdapterPath,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
if (!mConnection) {
|
|
NS_WARNING("Bluetooth service not started yet, cannot start discovery!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return SendDiscoveryMessage(aAdapterPath, "StartDiscovery", aRunnable);
|
|
}
|
|
|
|
class BluetoothDevicePropertiesRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
BluetoothDevicePropertiesRunnable(const BluetoothSignal& aSignal) :
|
|
mSignal(aSignal)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
~BluetoothDevicePropertiesRunnable()
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
nsString devicePath;
|
|
BluetoothValue v = mSignal.value();
|
|
if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue &&
|
|
v.get_ArrayOfBluetoothNamedValue().Length() ) {
|
|
InfallibleTArray<BluetoothNamedValue> arr = v.get_ArrayOfBluetoothNamedValue();
|
|
NS_ASSERTION(arr[0].value().type() == BluetoothValue::TnsString, "failed to get_nsString");
|
|
devicePath = arr[0].value().get_nsString();
|
|
}
|
|
else if (v.type() == BluetoothValue::TnsString) {
|
|
devicePath = v.get_nsString();
|
|
}
|
|
else {
|
|
NS_WARNING("Invalid value type for GetDeviceProperties() method");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
BluetoothValue prop;
|
|
if (!GetPropertiesInternal(devicePath, DBUS_DEVICE_IFACE, prop)) {
|
|
NS_WARNING("Getting properties failed!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
InfallibleTArray<BluetoothNamedValue> properties = prop.get_ArrayOfBluetoothNamedValue();
|
|
if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue) {
|
|
// Return original dbus message parameters and also device name
|
|
// for agent events "RequestConfirmation", "RequestPinCode", and "RequestPasskey"
|
|
InfallibleTArray<BluetoothNamedValue> parameters = v.get_ArrayOfBluetoothNamedValue();
|
|
|
|
// For consistency, append path
|
|
nsString path = parameters[0].value();
|
|
BluetoothNamedValue pathprop;
|
|
pathprop.name().AssignLiteral("Path");
|
|
pathprop.value() = path;
|
|
parameters.AppendElement(pathprop);
|
|
|
|
// Replace object path with device address
|
|
nsString address = GetAddressFromObjectPath(path);
|
|
parameters[0].value() = address;
|
|
|
|
uint8_t i;
|
|
for (i = 0; i < properties.Length(); i++) {
|
|
// Append device name
|
|
if (properties[i].name().EqualsLiteral("Name")) {
|
|
properties[i].name().AssignLiteral("name");
|
|
parameters.AppendElement(properties[i]);
|
|
mSignal.value() = parameters;
|
|
break;
|
|
}
|
|
}
|
|
NS_ASSERTION(i != properties.Length(), "failed to get device name");
|
|
} else {
|
|
// Return all device properties for event "DeviceCreated", including device path
|
|
properties.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Path"), mSignal.value()));
|
|
mSignal.value() = properties;
|
|
}
|
|
|
|
nsRefPtr<DistributeBluetoothSignalTask> t =
|
|
new DistributeBluetoothSignalTask(mSignal);
|
|
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
BluetoothSignal mSignal;
|
|
};
|
|
|
|
class BluetoothPairedDevicePropertiesRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
BluetoothPairedDevicePropertiesRunnable(BluetoothReplyRunnable* aRunnable,
|
|
const nsTArray<nsString>& aDeviceAddresses)
|
|
: mRunnable(dont_AddRef(aRunnable)),
|
|
mDeviceAddresses(aDeviceAddresses)
|
|
{
|
|
}
|
|
|
|
nsresult
|
|
Run()
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
DBusError err;
|
|
dbus_error_init(&err);
|
|
|
|
BluetoothValue values = InfallibleTArray<BluetoothNamedValue>();
|
|
|
|
for (int i = 0; i < mDeviceAddresses.Length(); i++) {
|
|
BluetoothValue v;
|
|
if (!GetPropertiesInternal(mDeviceAddresses[i], DBUS_DEVICE_IFACE, v)) {
|
|
nsAutoString errorStr;
|
|
errorStr.AssignLiteral("Getting properties failed!");
|
|
NS_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
|
|
mRunnable->SetReply(new BluetoothReply(BluetoothReplyError(errorStr)));
|
|
if (NS_FAILED(NS_DispatchToMainThread(mRunnable))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
return NS_OK;
|
|
}
|
|
v.get_ArrayOfBluetoothNamedValue().AppendElement(
|
|
BluetoothNamedValue(NS_LITERAL_STRING("Path"), mDeviceAddresses[i])
|
|
);
|
|
|
|
InfallibleTArray<BluetoothNamedValue>& deviceProperties =
|
|
v.get_ArrayOfBluetoothNamedValue();
|
|
for (uint32_t p = 0;
|
|
p < v.get_ArrayOfBluetoothNamedValue().Length(); ++p) {
|
|
BluetoothNamedValue& property = v.get_ArrayOfBluetoothNamedValue()[p];
|
|
// Only paired devices will be return back to main thread
|
|
if (property.name().EqualsLiteral("Paired")) {
|
|
bool paired = property.value();
|
|
if (paired) {
|
|
values.get_ArrayOfBluetoothNamedValue().AppendElement(
|
|
BluetoothNamedValue(mDeviceAddresses[i], deviceProperties)
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
mRunnable->SetReply(new BluetoothReply(BluetoothReplySuccess(values)));
|
|
if (NS_FAILED(NS_DispatchToMainThread(mRunnable))) {
|
|
NS_WARNING("Failed to dispatch to main thread!");
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsRefPtr<BluetoothReplyRunnable> mRunnable;
|
|
nsTArray<nsString> mDeviceAddresses;
|
|
};
|
|
|
|
nsresult
|
|
BluetoothDBusService::GetProperties(BluetoothObjectType aType,
|
|
const nsAString& aPath,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
MOZ_ASSERT(aType < ArrayLength(sBluetoothDBusIfaces));
|
|
MOZ_ASSERT(aType < ArrayLength(sBluetoothDBusPropCallbacks));
|
|
|
|
const char* interface = sBluetoothDBusIfaces[aType];
|
|
DBusCallback callback = sBluetoothDBusPropCallbacks[aType];
|
|
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
|
|
if (!dbus_func_args_async(mConnection,
|
|
1000,
|
|
callback,
|
|
(void*)aRunnable,
|
|
NS_ConvertUTF16toUTF8(aPath).get(),
|
|
interface,
|
|
"GetProperties",
|
|
DBUS_TYPE_INVALID)) {
|
|
NS_WARNING("Could not start async function!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
runnable.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::GetDevicePropertiesInternal(const BluetoothSignal& aSignal)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
if (!mConnection || !gThreadConnection) {
|
|
NS_ERROR("Bluetooth service not started yet!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsRefPtr<nsRunnable> func(new BluetoothDevicePropertiesRunnable(aSignal));
|
|
if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
|
|
NS_WARNING("Cannot dispatch task!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::GetPairedDevicePropertiesInternal(const nsTArray<nsString>& aDeviceAddresses,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
if (!mConnection || !gThreadConnection) {
|
|
NS_ERROR("Bluetooth service not started yet!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
|
|
nsRefPtr<nsRunnable> func(new BluetoothPairedDevicePropertiesRunnable(runnable, aDeviceAddresses));
|
|
if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
|
|
NS_WARNING("Cannot dispatch task!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
runnable.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::SetProperty(BluetoothObjectType aType,
|
|
const nsAString& aPath,
|
|
const BluetoothNamedValue& aValue,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
MOZ_ASSERT(aType < ArrayLength(sBluetoothDBusIfaces));
|
|
const char* interface = sBluetoothDBusIfaces[aType];
|
|
|
|
/* Compose the command */
|
|
DBusMessage* msg = dbus_message_new_method_call("org.bluez",
|
|
NS_ConvertUTF16toUTF8(aPath).get(),
|
|
interface,
|
|
"SetProperty");
|
|
|
|
if (!msg) {
|
|
NS_WARNING("Could not allocate D-Bus message object!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
const char* propName = NS_ConvertUTF16toUTF8(aValue.name()).get();
|
|
if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &propName, DBUS_TYPE_INVALID)) {
|
|
NS_WARNING("Couldn't append arguments to dbus message!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
int type;
|
|
int tmp_int;
|
|
void* val;
|
|
nsCString str;
|
|
if (aValue.value().type() == BluetoothValue::Tuint32_t) {
|
|
tmp_int = aValue.value().get_uint32_t();
|
|
val = &tmp_int;
|
|
type = DBUS_TYPE_UINT32;
|
|
} else if (aValue.value().type() == BluetoothValue::TnsString) {
|
|
str = NS_ConvertUTF16toUTF8(aValue.value().get_nsString());
|
|
const char* tempStr = str.get();
|
|
val = &tempStr;
|
|
type = DBUS_TYPE_STRING;
|
|
} else if (aValue.value().type() == BluetoothValue::Tbool) {
|
|
tmp_int = aValue.value().get_bool() ? 1 : 0;
|
|
val = &(tmp_int);
|
|
type = DBUS_TYPE_BOOLEAN;
|
|
} else {
|
|
NS_WARNING("Property type not handled!");
|
|
dbus_message_unref(msg);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
DBusMessageIter value_iter, iter;
|
|
dbus_message_iter_init_append(msg, &iter);
|
|
char var_type[2] = {(char)type, '\0'};
|
|
if (!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)) {
|
|
NS_WARNING("Could not append argument to method call!");
|
|
dbus_message_unref(msg);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
|
|
// msg is unref'd as part of dbus_func_send_async
|
|
if (!dbus_func_send_async(mConnection,
|
|
msg,
|
|
1000,
|
|
GetVoidCallback,
|
|
(void*)aRunnable)) {
|
|
NS_WARNING("Could not start async function!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
runnable.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
bool
|
|
BluetoothDBusService::GetDevicePath(const nsAString& aAdapterPath,
|
|
const nsAString& aDeviceAddress,
|
|
nsAString& aDevicePath)
|
|
{
|
|
aDevicePath = GetObjectPathFromAddress(aAdapterPath, aDeviceAddress);
|
|
return true;
|
|
}
|
|
|
|
int
|
|
GetDeviceServiceChannel(const nsAString& aObjectPath,
|
|
const nsAString& aPattern,
|
|
int aAttributeId)
|
|
{
|
|
// This is a blocking call, should not be run on main thread.
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
// GetServiceAttributeValue only exists in android's bluez dbus binding
|
|
// implementation
|
|
nsCString tempPattern = NS_ConvertUTF16toUTF8(aPattern);
|
|
const char* pattern = tempPattern.get();
|
|
|
|
DBusMessage *reply =
|
|
dbus_func_args(gThreadConnection->GetConnection(),
|
|
NS_ConvertUTF16toUTF8(aObjectPath).get(),
|
|
DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
|
|
DBUS_TYPE_STRING, &pattern,
|
|
DBUS_TYPE_UINT16, &aAttributeId,
|
|
DBUS_TYPE_INVALID);
|
|
|
|
return reply ? dbus_returns_int32(reply) : -1;
|
|
#else
|
|
// FIXME/Bug 793977 qdot: Just return something for desktop, until we have a
|
|
// parser for the GetServiceAttributes xml block
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
// static
|
|
bool
|
|
BluetoothDBusService::RemoveReservedServicesInternal(const nsAString& aAdapterPath,
|
|
const nsTArray<uint32_t>& aServiceHandles)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
int length = aServiceHandles.Length();
|
|
if (length == 0) return false;
|
|
|
|
const uint32_t* services = aServiceHandles.Elements();
|
|
|
|
DBusMessage* reply =
|
|
dbus_func_args(gThreadConnection->GetConnection(),
|
|
NS_ConvertUTF16toUTF8(aAdapterPath).get(),
|
|
DBUS_ADAPTER_IFACE, "RemoveReservedServiceRecords",
|
|
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
|
|
&services, length, DBUS_TYPE_INVALID);
|
|
|
|
if (!reply) return false;
|
|
|
|
dbus_message_unref(reply);
|
|
return true;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::CreatePairedDeviceInternal(const nsAString& aAdapterPath,
|
|
const nsAString& aDeviceAddress,
|
|
int aTimeout,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
const char *capabilities = B2G_AGENT_CAPABILITIES;
|
|
const char *deviceAgentPath = REMOTE_AGENT_PATH;
|
|
|
|
nsCString tempDeviceAddress = NS_ConvertUTF16toUTF8(aDeviceAddress);
|
|
const char *deviceAddress = tempDeviceAddress.get();
|
|
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
// Then send CreatePairedDevice, it will register a temp device agent then
|
|
// unregister it after pairing process is over
|
|
bool ret = dbus_func_args_async(mConnection,
|
|
aTimeout,
|
|
GetObjectPathCallback,
|
|
(void*)runnable,
|
|
NS_ConvertUTF16toUTF8(aAdapterPath).get(),
|
|
DBUS_ADAPTER_IFACE,
|
|
"CreatePairedDevice",
|
|
DBUS_TYPE_STRING, &deviceAddress,
|
|
DBUS_TYPE_OBJECT_PATH, &deviceAgentPath,
|
|
DBUS_TYPE_STRING, &capabilities,
|
|
DBUS_TYPE_INVALID);
|
|
|
|
if (!ret) {
|
|
NS_WARNING("Could not start async function!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
runnable.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::RemoveDeviceInternal(const nsAString& aAdapterPath,
|
|
const nsAString& aDeviceAddress,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
nsCString tempDeviceObjectPath =
|
|
NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress));
|
|
const char* deviceObjectPath = tempDeviceObjectPath.get();
|
|
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
|
|
// We don't really care about how long it would take on removing a device,
|
|
// just to make sure that the value of timeout is reasonable. So, we use
|
|
// -1 for the timeout, which means a reasonable default timeout will be used.
|
|
bool ret = dbus_func_args_async(mConnection,
|
|
-1,
|
|
GetVoidCallback,
|
|
(void*)runnable,
|
|
NS_ConvertUTF16toUTF8(aAdapterPath).get(),
|
|
DBUS_ADAPTER_IFACE,
|
|
"RemoveDevice",
|
|
DBUS_TYPE_OBJECT_PATH, &deviceObjectPath,
|
|
DBUS_TYPE_INVALID);
|
|
if (!ret) {
|
|
NS_WARNING("Could not start async function!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
runnable.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
bool
|
|
BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress,
|
|
const nsAString& aPinCode,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
nsString errorStr;
|
|
BluetoothValue v = true;
|
|
DBusMessage *msg;
|
|
if (!sPairingReqTable.Get(aDeviceAddress, &msg)) {
|
|
LOG("%s: Couldn't get original request message.", __FUNCTION__);
|
|
errorStr.AssignLiteral("Couldn't get original request message.");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return false;
|
|
}
|
|
|
|
DBusMessage *reply = dbus_message_new_method_return(msg);
|
|
|
|
if (!reply) {
|
|
LOG("%s: Memory can't be allocated for the message.", __FUNCTION__);
|
|
dbus_message_unref(msg);
|
|
errorStr.AssignLiteral("Memory can't be allocated for the message.");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return false;
|
|
}
|
|
|
|
bool result;
|
|
|
|
nsCString tempPinCode = NS_ConvertUTF16toUTF8(aPinCode);
|
|
const char* pinCode = tempPinCode.get();
|
|
|
|
if (!dbus_message_append_args(reply,
|
|
DBUS_TYPE_STRING, &pinCode,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
|
|
errorStr.AssignLiteral("Couldn't append arguments to dbus message.");
|
|
result = false;
|
|
} else {
|
|
result = dbus_connection_send(mConnection, reply, NULL);
|
|
}
|
|
|
|
dbus_message_unref(msg);
|
|
dbus_message_unref(reply);
|
|
|
|
sPairingReqTable.Remove(aDeviceAddress);
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress,
|
|
uint32_t aPasskey,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
nsString errorStr;
|
|
BluetoothValue v = true;
|
|
DBusMessage *msg;
|
|
if (!sPairingReqTable.Get(aDeviceAddress, &msg)) {
|
|
LOG("%s: Couldn't get original request message.", __FUNCTION__);
|
|
errorStr.AssignLiteral("Couldn't get original request message.");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return false;
|
|
}
|
|
|
|
DBusMessage *reply = dbus_message_new_method_return(msg);
|
|
|
|
if (!reply) {
|
|
LOG("%s: Memory can't be allocated for the message.", __FUNCTION__);
|
|
dbus_message_unref(msg);
|
|
errorStr.AssignLiteral("Memory can't be allocated for the message.");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return false;
|
|
}
|
|
|
|
uint32_t passkey = aPasskey;
|
|
bool result;
|
|
|
|
if (!dbus_message_append_args(reply,
|
|
DBUS_TYPE_UINT32, &passkey,
|
|
DBUS_TYPE_INVALID)) {
|
|
LOG("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
|
|
errorStr.AssignLiteral("Couldn't append arguments to dbus message.");
|
|
result = false;
|
|
} else {
|
|
result = dbus_connection_send(mConnection, reply, NULL);
|
|
}
|
|
|
|
dbus_message_unref(msg);
|
|
dbus_message_unref(reply);
|
|
|
|
sPairingReqTable.Remove(aDeviceAddress);
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
BluetoothDBusService::SetPairingConfirmationInternal(const nsAString& aDeviceAddress,
|
|
bool aConfirm,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
nsString errorStr;
|
|
BluetoothValue v = true;
|
|
DBusMessage *msg;
|
|
if (!sPairingReqTable.Get(aDeviceAddress, &msg)) {
|
|
LOG("%s: Couldn't get original request message.", __FUNCTION__);
|
|
errorStr.AssignLiteral("Couldn't get original request message.");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return false;
|
|
}
|
|
|
|
DBusMessage *reply;
|
|
|
|
if (aConfirm) {
|
|
reply = dbus_message_new_method_return(msg);
|
|
} else {
|
|
reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected",
|
|
"User rejected confirmation");
|
|
}
|
|
|
|
if (!reply) {
|
|
LOG("%s: Memory can't be allocated for the message.", __FUNCTION__);
|
|
dbus_message_unref(msg);
|
|
errorStr.AssignLiteral("Memory can't be allocated for the message.");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return false;
|
|
}
|
|
|
|
bool result = dbus_connection_send(mConnection, reply, NULL);
|
|
if (!result) {
|
|
errorStr.AssignLiteral("Can't send message!");
|
|
}
|
|
dbus_message_unref(msg);
|
|
dbus_message_unref(reply);
|
|
|
|
sPairingReqTable.Remove(aDeviceAddress);
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
BluetoothDBusService::SetAuthorizationInternal(const nsAString& aDeviceAddress,
|
|
bool aAllow,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
nsString errorStr;
|
|
BluetoothValue v = true;
|
|
DBusMessage *msg;
|
|
|
|
if (!sAuthorizeReqTable.Get(aDeviceAddress, &msg)) {
|
|
LOG("%s: Couldn't get original request message.", __FUNCTION__);
|
|
errorStr.AssignLiteral("Couldn't get original request message.");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return false;
|
|
}
|
|
|
|
DBusMessage *reply;
|
|
|
|
if (aAllow) {
|
|
reply = dbus_message_new_method_return(msg);
|
|
} else {
|
|
reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected",
|
|
"User rejected authorization");
|
|
}
|
|
|
|
if (!reply) {
|
|
LOG("%s: Memory can't be allocated for the message.", __FUNCTION__);
|
|
dbus_message_unref(msg);
|
|
errorStr.AssignLiteral("Memory can't be allocated for the message.");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return false;
|
|
}
|
|
|
|
bool result = dbus_connection_send(mConnection, reply, NULL);
|
|
if (!result) {
|
|
errorStr.AssignLiteral("Can't send message!");
|
|
}
|
|
dbus_message_unref(msg);
|
|
dbus_message_unref(reply);
|
|
|
|
sAuthorizeReqTable.Remove(aDeviceAddress);
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
return result;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::PrepareAdapterInternal(const nsAString& aPath)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
if (!mConnection || !gThreadConnection) {
|
|
NS_ERROR("Bluetooth service not started yet!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsRefPtr<nsRunnable> func(new PrepareAdapterRunnable(aPath));
|
|
if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
|
|
NS_WARNING("Cannot dispatch task!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
BluetoothDBusService::Connect(const nsAString& aDeviceAddress,
|
|
const nsAString& aAdapterPath,
|
|
const uint16_t aProfileId,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
BluetoothValue v;
|
|
nsString errorStr;
|
|
if (aProfileId == BluetoothServiceClass::HANDSFREE) {
|
|
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
|
if (!hfp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress),
|
|
true, aRunnable)) {
|
|
errorStr.AssignLiteral("BluetoothHfpManager has connected/is connecting to a headset!");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
}
|
|
} else if (aProfileId == BluetoothServiceClass::HEADSET) {
|
|
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
|
if (!hfp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress),
|
|
false, aRunnable)) {
|
|
errorStr.AssignLiteral("BluetoothHfpManager has connected/is connecting to a headset!");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
}
|
|
} else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
|
|
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
|
if (!opp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress),
|
|
aRunnable)) {
|
|
errorStr.AssignLiteral("BluetoothOppManager has connected/is connecting!");
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NS_WARNING("Unknown Profile");
|
|
#endif
|
|
}
|
|
|
|
void
|
|
BluetoothDBusService::Disconnect(const uint16_t aProfileId,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
if (aProfileId == BluetoothServiceClass::HANDSFREE ||
|
|
aProfileId == BluetoothServiceClass::HEADSET) {
|
|
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
|
hfp->Disconnect();
|
|
} else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
|
|
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
|
opp->Disconnect();
|
|
} else {
|
|
NS_WARNING("Unknown profile");
|
|
return;
|
|
}
|
|
|
|
// Currently, just fire success because Disconnect() doesn't fail,
|
|
// but we still make aRunnable pass into this function for future
|
|
// once Disconnect will fail.
|
|
nsString replyError;
|
|
BluetoothValue v = true;
|
|
DispatchBluetoothReply(aRunnable, v, replyError);
|
|
}
|
|
|
|
bool
|
|
BluetoothDBusService::IsConnected(const uint16_t aProfileId)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
if (aProfileId == BluetoothServiceClass::HANDSFREE ||
|
|
aProfileId == BluetoothServiceClass::HEADSET) {
|
|
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
|
return hfp->GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED;
|
|
} else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
|
|
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
|
return opp->GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
class ConnectBluetoothSocketRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
ConnectBluetoothSocketRunnable(BluetoothReplyRunnable* aRunnable,
|
|
UnixSocketConsumer* aConsumer,
|
|
const nsAString& aObjectPath,
|
|
const nsAString& aServiceUUID,
|
|
BluetoothSocketType aType,
|
|
bool aAuth,
|
|
bool aEncrypt,
|
|
int aChannel)
|
|
: mRunnable(dont_AddRef(aRunnable))
|
|
, mConsumer(aConsumer)
|
|
, mObjectPath(aObjectPath)
|
|
, mServiceUUID(aServiceUUID)
|
|
, mType(aType)
|
|
, mAuth(aAuth)
|
|
, mEncrypt(aEncrypt)
|
|
, mChannel(aChannel)
|
|
{
|
|
}
|
|
|
|
nsresult
|
|
Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsString address = GetAddressFromObjectPath(mObjectPath);
|
|
BluetoothValue v;
|
|
nsString replyError;
|
|
BluetoothUnixSocketConnector* c =
|
|
new BluetoothUnixSocketConnector(mType, mChannel, mAuth, mEncrypt);
|
|
if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(address).get())) {
|
|
replyError.AssignLiteral("SocketConnectionError");
|
|
DispatchBluetoothReply(mRunnable, v, replyError);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsRefPtr<BluetoothReplyRunnable> mRunnable;
|
|
nsRefPtr<UnixSocketConsumer> mConsumer;
|
|
nsString mObjectPath;
|
|
nsString mServiceUUID;
|
|
BluetoothSocketType mType;
|
|
bool mAuth;
|
|
bool mEncrypt;
|
|
int mChannel;
|
|
};
|
|
|
|
class GetDeviceChannelForConnectRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
GetDeviceChannelForConnectRunnable(BluetoothReplyRunnable* aRunnable,
|
|
UnixSocketConsumer* aConsumer,
|
|
const nsAString& aObjectPath,
|
|
const nsAString& aServiceUUID,
|
|
BluetoothSocketType aType,
|
|
bool aAuth,
|
|
bool aEncrypt)
|
|
: mRunnable(dont_AddRef(aRunnable)),
|
|
mConsumer(aConsumer),
|
|
mObjectPath(aObjectPath),
|
|
mServiceUUID(aServiceUUID),
|
|
mType(aType),
|
|
mAuth(aAuth),
|
|
mEncrypt(aEncrypt)
|
|
{
|
|
}
|
|
|
|
nsresult
|
|
Run()
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
int channel = GetDeviceServiceChannel(mObjectPath, mServiceUUID, 0x0004);
|
|
BluetoothValue v;
|
|
nsString replyError;
|
|
if (channel < 0) {
|
|
replyError.AssignLiteral("DeviceChannelRetrievalError");
|
|
DispatchBluetoothReply(mRunnable, v, replyError);
|
|
return NS_OK;
|
|
}
|
|
nsRefPtr<nsRunnable> func(new ConnectBluetoothSocketRunnable(mRunnable,
|
|
mConsumer,
|
|
mObjectPath,
|
|
mServiceUUID,
|
|
mType, mAuth,
|
|
mEncrypt,
|
|
channel));
|
|
if (NS_FAILED(NS_DispatchToMainThread(func, NS_DISPATCH_NORMAL))) {
|
|
NS_WARNING("Cannot dispatch connection task!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsRefPtr<BluetoothReplyRunnable> mRunnable;
|
|
nsRefPtr<UnixSocketConsumer> mConsumer;
|
|
nsString mObjectPath;
|
|
nsString mServiceUUID;
|
|
BluetoothSocketType mType;
|
|
bool mAuth;
|
|
bool mEncrypt;
|
|
};
|
|
|
|
nsresult
|
|
BluetoothDBusService::GetSocketViaService(const nsAString& aObjectPath,
|
|
const nsAString& aService,
|
|
BluetoothSocketType aType,
|
|
bool aAuth,
|
|
bool aEncrypt,
|
|
mozilla::ipc::UnixSocketConsumer* aConsumer,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
if (!mConnection || !gThreadConnection) {
|
|
NS_ERROR("Bluetooth service not started yet!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
|
|
nsRefPtr<nsRunnable> func(new GetDeviceChannelForConnectRunnable(
|
|
runnable,
|
|
aConsumer,
|
|
aObjectPath,
|
|
aService, aType,
|
|
aAuth, aEncrypt));
|
|
if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
|
|
NS_WARNING("Cannot dispatch firmware loading task!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
runnable.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::GetScoSocket(const nsAString& aAddress,
|
|
bool aAuth,
|
|
bool aEncrypt,
|
|
mozilla::ipc::UnixSocketConsumer* aConsumer)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
if (!mConnection || !gThreadConnection) {
|
|
NS_ERROR("Bluetooth service not started yet!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsString replyError;
|
|
BluetoothUnixSocketConnector* c =
|
|
new BluetoothUnixSocketConnector(BluetoothSocketType::SCO, -1,
|
|
aAuth, aEncrypt);
|
|
|
|
if (!aConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(aAddress).get())) {
|
|
replyError.AssignLiteral("SocketConnectionError");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
|
|
BlobParent* aBlobParent,
|
|
BlobChild* aBlobChild,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
// Currently we only support one device sending one file at a time,
|
|
// so we don't need aDeviceAddress here because the target device
|
|
// has been determined when calling 'Connect()'. Nevertheless, keep
|
|
// it for future use.
|
|
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
|
BluetoothValue v = true;
|
|
nsString errorStr;
|
|
|
|
if (!opp->SendFile(aBlobParent)) {
|
|
errorStr.AssignLiteral("Calling SendFile() failed");
|
|
}
|
|
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
}
|
|
|
|
void
|
|
BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
// Currently we only support one device sending one file at a time,
|
|
// so we don't need aDeviceAddress here because the target device
|
|
// has been determined when calling 'Connect()'. Nevertheless, keep
|
|
// it for future use.
|
|
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
|
BluetoothValue v = true;
|
|
nsString errorStr;
|
|
|
|
if (!opp->StopSendingFile()) {
|
|
errorStr.AssignLiteral("Calling StopSendingFile() failed");
|
|
}
|
|
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
}
|
|
|
|
void
|
|
BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress,
|
|
bool aConfirm,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
// Currently we only support one device sending one file at a time,
|
|
// so we don't need aDeviceAddress here because the target device
|
|
// has been determined when calling 'Connect()'. Nevertheless, keep
|
|
// it for future use.
|
|
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
|
BluetoothValue v = true;
|
|
nsString errorStr;
|
|
|
|
if (!opp->ConfirmReceivingFile(aConfirm)) {
|
|
errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed");
|
|
}
|
|
|
|
DispatchBluetoothReply(aRunnable, v, errorStr);
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDBusService::ListenSocketViaService(int aChannel,
|
|
BluetoothSocketType aType,
|
|
bool aAuth,
|
|
bool aEncrypt,
|
|
mozilla::ipc::UnixSocketConsumer* aConsumer)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
|
|
|
|
BluetoothUnixSocketConnector* c =
|
|
new BluetoothUnixSocketConnector(aType, aChannel, aAuth, aEncrypt);
|
|
if (!aConsumer->ListenSocket(c)) {
|
|
NS_WARNING("Can't listen on socket!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|