merge b2g-inbound to mozilla-central a=merge

--HG--
extra : amend_source : 6e2548ed562a40244895d9c341546e09f46cb6f4
This commit is contained in:
Carsten "Tomcat" Book 2014-07-14 12:55:09 +02:00
commit f7ad4b9ff3
43 changed files with 1032 additions and 369 deletions

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "db540afcf32e30144e33555a5d6ca860ff8cabc3",
"revision": "168c5ba3a072b6c6b30bcc93586f833cc97fff4a",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="753732a370d2d0e4e45d5eceaa1c53c7d11a9f38"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="88e0a972280bb35847c010b8c3f1481fa80f3847"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -151,6 +151,13 @@ AudioChannelService::RegisterType(AudioChannel aChannel, uint64_t aChildID,
mChannelCounters[type].AppendElement(aChildID);
if (XRE_GetProcessType() == GeckoProcessType_Default) {
// We must keep the childIds in order to decide which app is allowed to play
// with then telephony channel.
if (aChannel == AudioChannel::Telephony) {
RegisterTelephonyChild(aChildID);
}
// Since there is another telephony registered, we can unregister old one
// immediately.
if (mDeferTelChannelTimer && aChannel == AudioChannel::Telephony) {
@ -234,15 +241,21 @@ AudioChannelService::UnregisterType(AudioChannel aChannel,
// There are two reasons to defer the decrease of telephony channel.
// 1. User can have time to remove device from his ear before music resuming.
// 2. Give BT SCO to be disconnected before starting to connect A2DP.
if (XRE_GetProcessType() == GeckoProcessType_Default &&
aChannel == AudioChannel::Telephony &&
(mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].Length() +
mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].Length()) == 1) {
mTimerElementHidden = aElementHidden;
mTimerChildID = aChildID;
mDeferTelChannelTimer = do_CreateInstance("@mozilla.org/timer;1");
mDeferTelChannelTimer->InitWithCallback(this, 1500, nsITimer::TYPE_ONE_SHOT);
return;
if (XRE_GetProcessType() == GeckoProcessType_Default) {
if (aChannel == AudioChannel::Telephony) {
UnregisterTelephonyChild(aChildID);
}
if (aChannel == AudioChannel::Telephony &&
(mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].Length() +
mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].Length()) == 1) {
mTimerElementHidden = aElementHidden;
mTimerChildID = aChildID;
mDeferTelChannelTimer = do_CreateInstance("@mozilla.org/timer;1");
mDeferTelChannelTimer->InitWithCallback(this, 1500, nsITimer::TYPE_ONE_SHOT);
return;
}
}
UnregisterTypeInternal(aChannel, aElementHidden, aChildID, aWithVideo);
@ -358,7 +371,7 @@ AudioChannelService::GetStateInternal(AudioChannel aChannel, uint64_t aChildID,
if (CheckVolumeFadedCondition(newType, aElementHidden)) {
return AUDIO_CHANNEL_STATE_FADED;
}
return AUDIO_CHANNEL_STATE_NORMAL;
return CheckTelephonyPolicy(aChannel, aChildID);
}
// We are not visible, maybe we have to mute.
@ -387,7 +400,34 @@ AudioChannelService::GetStateInternal(AudioChannel aChannel, uint64_t aChildID,
return AUDIO_CHANNEL_STATE_MUTED;
}
return AUDIO_CHANNEL_STATE_NORMAL;
return CheckTelephonyPolicy(aChannel, aChildID);
}
AudioChannelState
AudioChannelService::CheckTelephonyPolicy(AudioChannel aChannel,
uint64_t aChildID)
{
// Only the latest childID is allowed to play with telephony channel.
if (aChannel != AudioChannel::Telephony) {
return AUDIO_CHANNEL_STATE_NORMAL;
}
MOZ_ASSERT(!mTelephonyChildren.IsEmpty());
#if DEBUG
bool found = false;
for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
if (mTelephonyChildren[i].mChildID == aChildID) {
found = true;
break;
}
}
MOZ_ASSERT(found);
#endif
return mTelephonyChildren.LastElement().mChildID == aChildID
? AUDIO_CHANNEL_STATE_NORMAL : AUDIO_CHANNEL_STATE_MUTED;
}
bool
@ -986,3 +1026,39 @@ AudioChannelService::GetDefaultAudioChannelString(nsAString& aString)
}
}
}
void
AudioChannelService::RegisterTelephonyChild(uint64_t aChildID)
{
for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
if (mTelephonyChildren[i].mChildID == aChildID) {
++mTelephonyChildren[i].mInstances;
if (i != len - 1) {
TelephonyChild child = mTelephonyChildren[i];
mTelephonyChildren.RemoveElementAt(i);
mTelephonyChildren.AppendElement(child);
}
return;
}
}
mTelephonyChildren.AppendElement(TelephonyChild(aChildID));
}
void
AudioChannelService::UnregisterTelephonyChild(uint64_t aChildID)
{
for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) {
if (mTelephonyChildren[i].mChildID == aChildID) {
if (!--mTelephonyChildren[i].mInstances) {
mTelephonyChildren.RemoveElementAt(i);
}
return;
}
}
MOZ_ASSERT(false, "This should not happen.");
}

View File

@ -150,6 +150,11 @@ protected:
void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
bool aHidden, uint64_t aChildID);
AudioChannelState CheckTelephonyPolicy(AudioChannel aChannel,
uint64_t aChildID);
void RegisterTelephonyChild(uint64_t aChildID);
void UnregisterTelephonyChild(uint64_t aChildID);
AudioChannelService();
virtual ~AudioChannelService();
@ -225,6 +230,19 @@ protected:
nsTArray<uint64_t> mWithVideoChildIDs;
// Telephony Channel policy is "LIFO", the last app to require the resource is
// allowed to play. The others are muted.
struct TelephonyChild {
uint64_t mChildID;
uint32_t mInstances;
explicit TelephonyChild(uint64_t aChildID)
: mChildID(aChildID)
, mInstances(1)
{}
};
nsTArray<TelephonyChild> mTelephonyChildren;
// mPlayableHiddenContentChildID stores the ChildID of the process which can
// play content channel(s) in the background.
// A background process contained content channel(s) will become playable:

Binary file not shown.

View File

@ -0,0 +1,18 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test Telephony Channel Policy</title>
</head>
<body>
<div id="container"></div>
<script type="application/javascript;version=1.7">
var audio = new Audio();
audio.mozAudioChannelType = 'telephony';
audio.src = "audio.ogg";
audio.play();
</script>
</body>
</html>

View File

@ -1,7 +1,11 @@
[DEFAULT]
support-files =
audio.ogg
file_audio.html
file_telephonyPolicy.html
AudioChannelChromeScript.js
[test_telephonyPolicy.html]
skip-if = (toolkit == 'gonk' || e10s)
[test_audioChannelChange.html]
skip-if = (toolkit != 'gonk')

View File

@ -0,0 +1,85 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test the Telephony Channel Policy</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="container"></div>
<script type="application/javascript;version=1.7">
function mainApp() {
var audio = new Audio();
audio.mozAudioChannelType = 'telephony';
audio.src = "audio.ogg";
audio.loop = true;
audio.play();
audio.addEventListener('mozinterruptbegin', function() {
ok(true, "This element has been muted!");
}, false);
audio.addEventListener('mozinterruptend', function() {
ok(true, "This element has been unmuted!");
audio.pause();
runTest();
}, false);
setTimeout(runTest, 600);
}
function newApp() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
// That needs to be an app.
iframe.setAttribute('mozapp', 'https://acertified.com/manifest.webapp');
iframe.src = "file_telephonyPolicy.html";
document.body.appendChild(iframe);
}
var tests = [
// Permissions
function() {
SpecialPowers.pushPermissions(
[{ "type": "browser", "allow": 1, "context": document },
{ "type": "embed-apps", "allow": 1, "context": document },
{ "type": "webapps-manage", "allow": 1, "context": document },
{ "type": "audio-channel-telephony", "allow": 1, "context": document }], runTest);
},
// Preferences
function() {
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.browser_frames.oop_by_default", true],
["media.useAudioChannelService", true],
["media.defaultAudioChannel", "telephony"],
["dom.mozBrowserFramesEnabled", true],
["network.disable.ipc.security", true]]}, runTest);
},
// Run 2 apps
mainApp,
newApp,
];
function runTest() {
if (!tests.length) {
finish();
return;
}
var test = tests.shift();
test();
}
function finish() {
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
runTest();
</script>
</body>
</html>

View File

@ -20,6 +20,7 @@
#include "mozilla/LazyIdleThread.h"
#include "BluetoothAdapter.h"
#include "BluetoothClassOfDevice.h"
#include "BluetoothDevice.h"
#include "BluetoothDiscoveryHandle.h"
#include "BluetoothReplyRunnable.h"
@ -64,20 +65,23 @@ class StartDiscoveryTask : public BluetoothReplyRunnable
NS_LITERAL_STRING("StartDiscovery"))
, mAdapter(aAdapter)
{
MOZ_ASSERT(aPromise && aAdapter);
MOZ_ASSERT(aPromise);
MOZ_ASSERT(aAdapter);
}
bool
ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
{
BT_API2_LOGR();
aValue.setUndefined();
AutoJSAPI jsapi;
NS_ENSURE_TRUE(jsapi.Init(mAdapter->GetParentObject()), false);
// Wrap BluetoothDiscoveryHandle to return
JSContext* cx = jsapi.cx();
/**
* Create a new discovery handle and wrap it to return. Each
* discovery handle is one-time-use only.
*/
nsRefPtr<BluetoothDiscoveryHandle> discoveryHandle =
BluetoothDiscoveryHandle::Create(mAdapter->GetParentObject());
if (!ToJSValue(cx, discoveryHandle, aValue)) {
@ -85,6 +89,8 @@ class StartDiscoveryTask : public BluetoothReplyRunnable
return false;
}
// Set the created discovery handle as the one in use.
mAdapter->SetDiscoveryHandleInUse(discoveryHandle);
return true;
}
@ -205,6 +211,7 @@ BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow,
: DOMEventTargetHelper(aWindow)
, mJsUuids(nullptr)
, mJsDeviceAddresses(nullptr)
, mDiscoveryHandleInUse(nullptr)
, mState(BluetoothAdapterState::Disabled)
, mDiscoverable(false)
, mDiscovering(false)
@ -273,9 +280,8 @@ BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
const nsString& name = aValue.name();
const BluetoothValue& value = aValue.value();
if (name.EqualsLiteral("State")) {
bool isEnabled = value.get_bool();
mState = isEnabled ? BluetoothAdapterState::Enabled
: BluetoothAdapterState::Disabled;
mState = value.get_bool() ? BluetoothAdapterState::Enabled
: BluetoothAdapterState::Disabled;
} else if (name.EqualsLiteral("Name")) {
mName = value.get_nsString();
} else if (name.EqualsLiteral("Address")) {
@ -284,6 +290,10 @@ BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
mDiscoverable = value.get_bool();
} else if (name.EqualsLiteral("Discovering")) {
mDiscovering = value.get_bool();
if (!mDiscovering) {
// Reset discovery handle in use to nullptr
SetDiscoveryHandleInUse(nullptr);
}
} else if (name.EqualsLiteral("Pairable")) {
mPairable = value.get_bool();
} else if (name.EqualsLiteral("Powered")) {
@ -390,6 +400,18 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData)
}
}
void
BluetoothAdapter::SetDiscoveryHandleInUse(
BluetoothDiscoveryHandle* aDiscoveryHandle)
{
// Stop discovery handle in use from listening to "DeviceFound" signal
if (mDiscoveryHandleInUse) {
mDiscoveryHandleInUse->DisconnectFromOwner();
}
mDiscoveryHandleInUse = aDiscoveryHandle;
}
already_AddRefed<Promise>
BluetoothAdapter::StartStopDiscovery(bool aStart, ErrorResult& aRv)
{
@ -403,10 +425,13 @@ BluetoothAdapter::StartStopDiscovery(bool aStart, ErrorResult& aRv)
/**
* Ensure
* - adapter does not already start/stop discovering,
* (note we reject here to ensure each resolved promise of startDiscovery
* returns a BluetoothDiscoveryHandle)
* - adapter is already enabled, and
* - BluetoothService is available
*/
BT_ENSURE_TRUE_RESOLVE(mDiscovering != aStart, JS::UndefinedHandleValue);
BT_ENSURE_TRUE_REJECT(mDiscovering != aStart,
NS_ERROR_DOM_INVALID_STATE_ERR);
BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
NS_ERROR_DOM_INVALID_STATE_ERR);
BluetoothService* bs = BluetoothService::Get();
@ -824,12 +849,10 @@ BluetoothAdapter::IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
const BluetoothValue& aValue)
{
switch(aType) {
case BluetoothAdapterAttribute::State: {
case BluetoothAdapterAttribute::State:
MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
bool isEnabled = aValue.get_bool();
return isEnabled ? mState != BluetoothAdapterState::Enabled
: mState != BluetoothAdapterState::Disabled;
}
return aValue.get_bool() ? mState != BluetoothAdapterState::Enabled
: mState != BluetoothAdapterState::Disabled;
case BluetoothAdapterAttribute::Name:
MOZ_ASSERT(aValue.type() == BluetoothValue::TnsString);
return !mName.Equals(aValue.get_nsString());
@ -928,7 +951,7 @@ BluetoothAdapter::Connect(BluetoothDevice& aDevice,
nsAutoString address;
aDevice.GetAddress(address);
uint32_t deviceClass = aDevice.Class();
uint32_t deviceClass = aDevice.Cod()->ToUint32();
uint16_t serviceUuid = 0;
if (aServiceUuid.WasPassed()) {
serviceUuid = aServiceUuid.Value();

View File

@ -25,6 +25,7 @@ struct MediaPlayStatus;
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothDevice;
class BluetoothDiscoveryHandle;
class BluetoothSignal;
class BluetoothNamedValue;
class BluetoothValue;
@ -93,6 +94,17 @@ public:
void GetUuids(JSContext* aContext, JS::MutableHandle<JS::Value> aUuids,
ErrorResult& aRv);
/**
* Update this adapter's discovery handle in use (mDiscoveryHandleInUse).
*
* |mDiscoveryHandleInUse| is set to the latest discovery handle when adapter
* just starts discovery, and is reset to nullptr when discovery is stopped
* by some adapter.
*
* @param aDiscoveryHandle [in] the discovery handle to set.
*/
void SetDiscoveryHandleInUse(BluetoothDiscoveryHandle* aDiscoveryHandle);
already_AddRefed<Promise> SetName(const nsAString& aName, ErrorResult& aRv);
already_AddRefed<Promise>
SetDiscoverable(bool aDiscoverable, ErrorResult& aRv);
@ -189,6 +201,7 @@ private:
JS::Heap<JSObject*> mJsUuids;
JS::Heap<JSObject*> mJsDeviceAddresses;
nsRefPtr<BluetoothDiscoveryHandle> mDiscoveryHandleInUse;
BluetoothAdapterState mState;
nsString mAddress;
nsString mName;

View File

@ -4,57 +4,82 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/basictypes.h"
#include "BluetoothClassOfDevice.h"
#include "BluetoothDevice.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothService.h"
#include "BluetoothUtils.h"
#include "nsDOMClassInfo.h"
#include "nsTArrayHelpers.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/dom/BluetoothAttributeEvent.h"
#include "mozilla/dom/BluetoothDevice2Binding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/Promise.h"
using namespace mozilla;
using namespace mozilla::dom;
USING_BLUETOOTH_NAMESPACE
DOMCI_DATA(BluetoothDevice, BluetoothDevice)
NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothDevice)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothDevice,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsUuids)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsServices)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothDevice,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothDevice,
DOMEventTargetHelper)
tmp->Unroot();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothDevice, DOMEventTargetHelper, mCod)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(BluetoothDevice, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(BluetoothDevice, DOMEventTargetHelper)
class FetchUuidsTask : public BluetoothReplyRunnable
{
public:
FetchUuidsTask(Promise* aPromise,
const nsAString& aName,
BluetoothDevice* aDevice)
: BluetoothReplyRunnable(nullptr /* DOMRequest */, aPromise, aName)
, mDevice(aDevice)
{
MOZ_ASSERT(aPromise);
MOZ_ASSERT(aDevice);
}
bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
{
aValue.setUndefined();
const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
NS_ENSURE_TRUE(v.type() == BluetoothValue::TArrayOfnsString, false);
const InfallibleTArray<nsString>& uuids = v.get_ArrayOfnsString();
AutoJSAPI jsapi;
NS_ENSURE_TRUE(jsapi.Init(mDevice->GetParentObject()), false);
JSContext* cx = jsapi.cx();
if (!ToJSValue(cx, uuids, aValue)) {
BT_WARNING("Cannot create JS array!");
JS_ClearPendingException(cx);
return false;
}
return true;
}
virtual void ReleaseMembers() MOZ_OVERRIDE
{
BluetoothReplyRunnable::ReleaseMembers();
mDevice = nullptr;
}
private:
nsRefPtr<BluetoothDevice> mDevice;
};
BluetoothDevice::BluetoothDevice(nsPIDOMWindow* aWindow,
const BluetoothValue& aValue)
: DOMEventTargetHelper(aWindow)
, mJsUuids(nullptr)
, mJsServices(nullptr)
, mIsRooted(false)
{
MOZ_ASSERT(aWindow);
MOZ_ASSERT(IsDOMBinding());
mCod = BluetoothClassOfDevice::Create(aWindow);
const InfallibleTArray<BluetoothNamedValue>& values =
aValue.get_ArrayOfBluetoothNamedValue();
for (uint32_t i = 0; i < values.Length(); ++i) {
@ -72,7 +97,6 @@ BluetoothDevice::~BluetoothDevice()
// bs can be null on shutdown, where destruction might happen.
NS_ENSURE_TRUE_VOID(bs);
bs->UnregisterBluetoothSignalHandler(mAddress, this);
Unroot();
}
void
@ -85,26 +109,6 @@ BluetoothDevice::DisconnectFromOwner()
bs->UnregisterBluetoothSignalHandler(mAddress, this);
}
void
BluetoothDevice::Root()
{
if (!mIsRooted) {
mozilla::HoldJSObjects(this);
mIsRooted = true;
}
}
void
BluetoothDevice::Unroot()
{
if (mIsRooted) {
mJsUuids = nullptr;
mJsServices = nullptr;
mozilla::DropJSObjects(this);
mIsRooted = false;
}
}
void
BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
{
@ -114,54 +118,47 @@ BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
mName = value.get_nsString();
} else if (name.EqualsLiteral("Address")) {
mAddress = value.get_nsString();
} else if (name.EqualsLiteral("Class")) {
mClass = value.get_uint32_t();
} else if (name.EqualsLiteral("Icon")) {
mIcon = value.get_nsString();
} else if (name.EqualsLiteral("Connected")) {
mConnected = value.get_bool();
} else if (name.EqualsLiteral("Cod")) {
mCod->Update(value.get_uint32_t());
} else if (name.EqualsLiteral("Paired")) {
mPaired = value.get_bool();
} else if (name.EqualsLiteral("UUIDs")) {
// We assume the received uuids array is sorted without duplicate items.
// If it's not, we require additional processing before assigning it
// directly.
mUuids = value.get_ArrayOfnsString();
AutoJSAPI jsapi;
if (!jsapi.Init(GetOwner())) {
BT_WARNING("Failed to initialise AutoJSAPI!");
return;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> uuids(cx);
if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &uuids))) {
BT_WARNING("Cannot set JS UUIDs object!");
return;
}
mJsUuids = uuids;
Root();
} else if (name.EqualsLiteral("Services")) {
mServices = value.get_ArrayOfnsString();
AutoJSAPI jsapi;
if (!jsapi.Init(GetOwner())) {
BT_WARNING("Failed to initialise AutoJSAPI!");
return;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> services(cx);
if (NS_FAILED(nsTArrayToJSArray(cx, mServices, &services))) {
BT_WARNING("Cannot set JS Services object!");
return;
}
mJsServices = services;
Root();
BluetoothDeviceBinding::ClearCachedUuidsValue(this);
} else {
nsCString warningMsg;
warningMsg.AssignLiteral("Not handling device property: ");
warningMsg.Append(NS_ConvertUTF16toUTF8(name));
BT_WARNING(warningMsg.get());
BT_WARNING("Not handling device property: %s",
NS_ConvertUTF16toUTF8(name).get());
}
}
already_AddRefed<Promise>
BluetoothDevice::FetchUuids(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<Promise> promise = new Promise(global);
BluetoothService* bs = BluetoothService::Get();
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
nsRefPtr<BluetoothReplyRunnable> result =
new FetchUuidsTask(promise,
NS_LITERAL_STRING("FetchUuids"),
this);
nsresult rv = bs->FetchUuidsInternal(mAddress, result);
BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(rv), NS_ERROR_DOM_OPERATION_ERR);
return promise.forget();
}
// static
already_AddRefed<BluetoothDevice>
BluetoothDevice::Create(nsPIDOMWindow* aWindow,
@ -177,56 +174,112 @@ BluetoothDevice::Create(nsPIDOMWindow* aWindow,
void
BluetoothDevice::Notify(const BluetoothSignal& aData)
{
BT_LOGD("[D] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
BluetoothValue v = aData.value();
if (aData.name().EqualsLiteral("PropertyChanged")) {
MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
const InfallibleTArray<BluetoothNamedValue>& arr =
v.get_ArrayOfBluetoothNamedValue();
for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
SetPropertyByValue(arr[i]);
}
HandlePropertyChanged(v);
} else {
#ifdef DEBUG
nsCString warningMsg;
warningMsg.AssignLiteral("Not handling device signal: ");
warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
BT_WARNING(warningMsg.get());
#endif
BT_WARNING("Not handling device signal: %s",
NS_ConvertUTF16toUTF8(aData.name()).get());
}
}
BluetoothDeviceAttribute
BluetoothDevice::ConvertStringToDeviceAttribute(const nsAString& aString)
{
using namespace
mozilla::dom::BluetoothDeviceAttributeValues;
for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
if (aString.LowerCaseEqualsASCII(strings[index].value,
strings[index].length)) {
return static_cast<BluetoothDeviceAttribute>(index);
}
}
return BluetoothDeviceAttribute::Unknown;
}
bool
BluetoothDevice::IsDeviceAttributeChanged(BluetoothDeviceAttribute aType,
const BluetoothValue& aValue)
{
switch (aType) {
case BluetoothDeviceAttribute::Cod:
MOZ_ASSERT(aValue.type() == BluetoothValue::Tuint32_t);
return !mCod->Equals(aValue.get_uint32_t());
case BluetoothDeviceAttribute::Name:
MOZ_ASSERT(aValue.type() == BluetoothValue::TnsString);
return !mName.Equals(aValue.get_nsString());
case BluetoothDeviceAttribute::Paired:
MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
return mPaired != aValue.get_bool();
case BluetoothDeviceAttribute::Uuids: {
MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfnsString);
const InfallibleTArray<nsString>& uuids = aValue.get_ArrayOfnsString();
// We assume the received uuids array is sorted without duplicate items.
// If it's not, we require additional processing before comparing it
// directly.
return mUuids != uuids;
}
default:
BT_WARNING("Type %d is not handled", uint32_t(aType));
return false;
}
}
void
BluetoothDevice::GetUuids(JSContext* aContext,
JS::MutableHandle<JS::Value> aUuids,
ErrorResult& aRv)
BluetoothDevice::HandlePropertyChanged(const BluetoothValue& aValue)
{
if (!mJsUuids) {
BT_WARNING("UUIDs not yet set!");
aRv.Throw(NS_ERROR_FAILURE);
return;
MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
const InfallibleTArray<BluetoothNamedValue>& arr =
aValue.get_ArrayOfBluetoothNamedValue();
nsTArray<nsString> types;
for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
BluetoothDeviceAttribute type =
ConvertStringToDeviceAttribute(arr[i].name());
// Non-BluetoothDeviceAttribute properties
if (type == BluetoothDeviceAttribute::Unknown) {
SetPropertyByValue(arr[i]);
continue;
}
// BluetoothDeviceAttribute properties
if (IsDeviceAttributeChanged(type, arr[i].value())) {
SetPropertyByValue(arr[i]);
BT_APPEND_ENUM_STRING(types, BluetoothDeviceAttribute, type);
}
}
JS::ExposeObjectToActiveJS(mJsUuids);
aUuids.setObject(*mJsUuids);
DispatchAttributeEvent(types);
}
void
BluetoothDevice::GetServices(JSContext* aCx,
JS::MutableHandle<JS::Value> aServices,
ErrorResult& aRv)
BluetoothDevice::DispatchAttributeEvent(const nsTArray<nsString>& aTypes)
{
if (!mJsServices) {
BT_WARNING("Services not yet set!");
aRv.Throw(NS_ERROR_FAILURE);
NS_ENSURE_TRUE_VOID(aTypes.Length());
AutoJSAPI jsapi;
NS_ENSURE_TRUE_VOID(jsapi.Init(GetOwner()));
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> value(cx);
if (!ToJSValue(cx, aTypes, &value)) {
JS_ClearPendingException(cx);
return;
}
JS::ExposeObjectToActiveJS(mJsServices);
aServices.setObject(*mJsServices);
RootedDictionary<BluetoothAttributeEventInit> init(cx);
init.mAttrs = value;
nsRefPtr<BluetoothAttributeEvent> event =
BluetoothAttributeEvent::Constructor(this,
NS_LITERAL_STRING("attributechanged"),
init);
DispatchTrustedEvent(event);
}
JSObject*

View File

@ -9,24 +9,33 @@
#include "mozilla/Attributes.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/BluetoothDevice2Binding.h"
#include "BluetoothCommon.h"
#include "nsString.h"
#include "nsCOMPtr.h"
namespace mozilla {
namespace dom {
class Promise;
}
}
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothClassOfDevice;
class BluetoothNamedValue;
class BluetoothValue;
class BluetoothSignal;
class BluetoothSocket;
class BluetoothDevice : public DOMEventTargetHelper
, public BluetoothSignalObserver
class BluetoothDevice MOZ_FINAL : public DOMEventTargetHelper
, public BluetoothSignalObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(BluetoothDevice,
DOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothDevice,
DOMEventTargetHelper)
static already_AddRefed<BluetoothDevice>
Create(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
@ -38,73 +47,59 @@ public:
aAddress = mAddress;
}
BluetoothClassOfDevice* Cod() const
{
return mCod;
}
void GetName(nsString& aName) const
{
aName = mName;
}
void GetIcon(nsString& aIcon) const
{
aIcon = mIcon;
}
uint32_t Class() const
{
return mClass;
}
bool Paired() const
{
return mPaired;
}
bool Connected() const
{
return mConnected;
void GetUuids(nsTArray<nsString>& aUuids) {
aUuids = mUuids;
}
void GetUuids(JSContext* aContext, JS::MutableHandle<JS::Value> aUuids,
ErrorResult& aRv);
void GetServices(JSContext* aContext, JS::MutableHandle<JS::Value> aServices,
ErrorResult& aRv);
nsISupports*
ToISupports()
{
return static_cast<EventTarget*>(this);
}
already_AddRefed<Promise> FetchUuids(ErrorResult& aRv);
void SetPropertyByValue(const BluetoothNamedValue& aValue);
void Unroot();
BluetoothDeviceAttribute
ConvertStringToDeviceAttribute(const nsAString& aString);
bool
IsDeviceAttributeChanged(BluetoothDeviceAttribute aType,
const BluetoothValue& aValue);
void HandlePropertyChanged(const BluetoothValue& aValue);
void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
IMPL_EVENT_HANDLER(attributechanged);
nsPIDOMWindow* GetParentObject() const
{
return GetOwner();
}
virtual JSObject*
WrapObject(JSContext* aCx) MOZ_OVERRIDE;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
virtual void DisconnectFromOwner() MOZ_OVERRIDE;
private:
BluetoothDevice(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
~BluetoothDevice();
void Root();
JS::Heap<JSObject*> mJsUuids;
JS::Heap<JSObject*> mJsServices;
nsString mAddress;
nsRefPtr<BluetoothClassOfDevice> mCod;
nsString mName;
nsString mIcon;
uint32_t mClass;
bool mConnected;
bool mPaired;
bool mIsRooted;
nsTArray<nsString> mUuids;
nsTArray<nsString> mServices;
};

View File

@ -91,6 +91,13 @@ BluetoothDiscoveryHandle::Notify(const BluetoothSignal& aData)
}
}
void
BluetoothDiscoveryHandle::DisconnectFromOwner()
{
DOMEventTargetHelper::DisconnectFromOwner();
ListenToBluetoothSignal(false);
}
JSObject*
BluetoothDiscoveryHandle::WrapObject(JSContext* aCx)
{

View File

@ -30,6 +30,8 @@ public:
IMPL_EVENT_HANDLER(devicefound);
void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
virtual void DisconnectFromOwner() MOZ_OVERRIDE;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
private:

View File

@ -151,6 +151,16 @@ public:
GetConnectedDevicePropertiesInternal(uint16_t aServiceUuid,
BluetoothReplyRunnable* aRunnable) = 0;
/**
* Returns up-to-date uuids of given device address,
* implemented via a platform specific methood.
*
* @return NS_OK on success, NS_ERROR_FAILURE otherwise
*/
virtual nsresult
FetchUuidsInternal(const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable) = 0;
/**
* Stop device discovery (platform specific implementation)
*

View File

@ -63,6 +63,7 @@ static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeAdapterStateRunnableAr
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sFetchUuidsRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
@ -123,10 +124,11 @@ public:
// Bluetooth just enabled, clear profile controllers and runnable arrays.
sControllerArray.Clear();
sBondingRunnableArray.Clear();
sChangeDiscoveryRunnableArray.Clear();
sGetDeviceRunnableArray.Clear();
sSetPropertyRunnableArray.Clear();
sGetDeviceRunnableArray.Clear();
sFetchUuidsRunnableArray.Clear();
sBondingRunnableArray.Clear();
sUnbondingRunnableArray.Clear();
// Bluetooth scan mode is NONE by default
@ -194,97 +196,6 @@ public:
/**
* Static callback functions
*/
static void
ClassToIcon(uint32_t aClass, nsAString& aRetIcon)
{
switch ((aClass & 0x1f00) >> 8) {
case 0x01:
aRetIcon.AssignLiteral("computer");
break;
case 0x02:
switch ((aClass & 0xfc) >> 2) {
case 0x01:
case 0x02:
case 0x03:
case 0x05:
aRetIcon.AssignLiteral("phone");
break;
case 0x04:
aRetIcon.AssignLiteral("modem");
break;
}
break;
case 0x03:
aRetIcon.AssignLiteral("network-wireless");
break;
case 0x04:
switch ((aClass & 0xfc) >> 2) {
case 0x01:
case 0x02:
case 0x06:
aRetIcon.AssignLiteral("audio-card");
break;
case 0x0b:
case 0x0c:
case 0x0d:
aRetIcon.AssignLiteral("camera-video");
break;
default:
aRetIcon.AssignLiteral("audio-card");
break;
}
break;
case 0x05:
switch ((aClass & 0xc0) >> 6) {
case 0x00:
switch ((aClass && 0x1e) >> 2) {
case 0x01:
case 0x02:
aRetIcon.AssignLiteral("input-gaming");
break;
}
break;
case 0x01:
aRetIcon.AssignLiteral("input-keyboard");
break;
case 0x02:
switch ((aClass && 0x1e) >> 2) {
case 0x05:
aRetIcon.AssignLiteral("input-tablet");
break;
default:
aRetIcon.AssignLiteral("input-mouse");
break;
}
}
break;
case 0x06:
if (aClass & 0x80) {
aRetIcon.AssignLiteral("printer");
break;
}
if (aClass & 0x20) {
aRetIcon.AssignLiteral("camera-photo");
break;
}
break;
}
if (aRetIcon.IsEmpty()) {
if (HAS_AUDIO(aClass)) {
/**
* Property 'Icon' may be missed due to CoD of major class is TOY(0x08).
* But we need to assign Icon as audio-card if service class is 'Audio'.
* This is for PTS test case TC_AG_COD_BV_02_I. As HFP specification
* defines that service class is 'Audio' can be considered as HFP HF.
*/
aRetIcon.AssignLiteral("audio-card");
} else {
BT_LOGR("No icon to match class: %x", aClass);
}
}
}
static ControlPlayStatus
PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus)
{
@ -476,11 +387,6 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
if (sRequestedDeviceCountArray.IsEmpty()) {
// This is possible because the callback would be called after turning
// Bluetooth on.
return NS_OK;
}
// Update to registered BluetoothDevice objects
BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
@ -492,6 +398,24 @@ public:
return NS_OK;
}
// FetchUuids task
if (!sFetchUuidsRunnableArray.IsEmpty()) {
// mProps contains Address and Uuids only
DispatchBluetoothReply(sFetchUuidsRunnableArray[0],
mProps[1].value() /* Uuids */,
EmptyString());
sFetchUuidsRunnableArray.RemoveElementAt(0);
return NS_OK;
}
// GetDevices task
if (sRequestedDeviceCountArray.IsEmpty()) {
// This is possible because the callback would be called after turning
// Bluetooth on.
return NS_OK;
}
// Use address as the index
sRemoteDevicesPack.AppendElement(
BluetoothNamedValue(mRemoteDeviceBdAddress, mProps));
@ -515,6 +439,7 @@ public:
* RemoteDevicePropertiesCallback will be called, as the following conditions:
* 1. When BT is turning on, bluedroid automatically execute this callback
* 2. When get_remote_device_properties()
* 3. When get_remote_services()
*/
static void
RemoteDevicePropertiesCallback(bt_status_t aStatus, bt_bdaddr_t *aBdAddress,
@ -536,11 +461,22 @@ RemoteDevicePropertiesCallback(bt_status_t aStatus, bt_bdaddr_t *aBdAddress,
BT_APPEND_NAMED_VALUE(props, "Name", propertyValue);
} else if (p.type == BT_PROPERTY_CLASS_OF_DEVICE) {
uint32_t cod = *(uint32_t*)p.val;
BT_APPEND_NAMED_VALUE(props, "Class", cod);
BT_APPEND_NAMED_VALUE(props, "Cod", cod);
} else if (p.type == BT_PROPERTY_UUIDS) {
nsTArray<nsString> uuids;
nsString icon;
ClassToIcon(cod, icon);
BT_APPEND_NAMED_VALUE(props, "Icon", icon);
// Construct a sorted uuid set
for (uint32_t j = 0; j < p.len / sizeof(bt_uuid_t); j++) {
nsAutoString uuid;
bt_uuid_t* pUuid = (bt_uuid_t*)p.val + j;
UuidToString(pUuid, uuid);
if (!uuids.Contains(uuid)) { // filter out duplicate uuids
uuids.InsertElementSorted(uuid);
}
}
BT_APPEND_NAMED_VALUE(props, "UUIDs", uuids);
} else {
BT_LOGD("Other non-handled device properties. Type: %d", p.type);
}
@ -574,12 +510,7 @@ DeviceFoundCallback(int aNumProperties, bt_property_t *aProperties)
} else if (p.type == BT_PROPERTY_CLASS_OF_DEVICE) {
uint32_t cod = *(uint32_t*)p.val;
propertyValue = cod;
BT_APPEND_NAMED_VALUE(propertiesArray, "Class", propertyValue);
nsString icon;
ClassToIcon(cod, icon);
propertyValue = icon;
BT_APPEND_NAMED_VALUE(propertiesArray, "Icon", propertyValue);
BT_APPEND_NAMED_VALUE(propertiesArray, "Cod", cod);
} else {
BT_LOGD("Not handled remote device property: %d", p.type);
}
@ -1251,6 +1182,51 @@ BluetoothServiceBluedroid::StopDiscoveryInternal(
return NS_OK;
}
class GetRemoteServicesResultHandler MOZ_FINAL : public BluetoothResultHandler
{
public:
GetRemoteServicesResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{ }
void OnError(int aStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
sFetchUuidsRunnableArray.RemoveElement(mRunnable);
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("FetchUuids"));
}
private:
BluetoothReplyRunnable* mRunnable;
};
nsresult
BluetoothServiceBluedroid::FetchUuidsInternal(
const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
/*
* get_remote_services request will not be performed by bluedroid
* if it is currently discovering nearby remote devices.
*/
if (sAdapterDiscovering) {
sBtInterface->CancelDiscovery(new CancelDiscoveryResultHandler(aRunnable));
}
bt_bdaddr_t addressType;
StringToBdAddressType(aDeviceAddress, &addressType);
sFetchUuidsRunnableArray.AppendElement(aRunnable);
sBtInterface->GetRemoteServices(&addressType,
new GetRemoteServicesResultHandler(aRunnable));
return NS_OK;
}
class SetAdapterPropertyResultHandler MOZ_FINAL : public BluetoothResultHandler
{
public:
@ -1766,4 +1742,3 @@ void
BluetoothServiceBluedroid::ToggleCalls(BluetoothReplyRunnable* aRunnable)
{
}

View File

@ -36,6 +36,10 @@ public:
GetPairedDevicePropertiesInternal(const nsTArray<nsString>& aDeviceAddress,
BluetoothReplyRunnable* aRunnable);
virtual nsresult
FetchUuidsInternal(const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual nsresult StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable);
virtual nsresult StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable);

View File

@ -49,6 +49,29 @@ BdAddressTypeToString(bt_bdaddr_t* aBdAddressType, nsAString& aRetBdAddress)
aRetBdAddress = NS_ConvertUTF8toUTF16(bdstr);
}
void
UuidToString(bt_uuid_t* aUuid, nsAString& aString) {
char uuidStr[37];
uint32_t uuid0, uuid4;
uint16_t uuid1, uuid2, uuid3, uuid5;
memcpy(&uuid0, &(aUuid->uu[0]), sizeof(uint32_t));
memcpy(&uuid1, &(aUuid->uu[4]), sizeof(uint16_t));
memcpy(&uuid2, &(aUuid->uu[6]), sizeof(uint16_t));
memcpy(&uuid3, &(aUuid->uu[8]), sizeof(uint16_t));
memcpy(&uuid4, &(aUuid->uu[10]), sizeof(uint32_t));
memcpy(&uuid5, &(aUuid->uu[14]), sizeof(uint16_t));
sprintf(uuidStr, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
ntohl(uuid0), ntohs(uuid1),
ntohs(uuid2), ntohs(uuid3),
ntohl(uuid4), ntohs(uuid5));
aString.Truncate();
aString.AssignLiteral(uuidStr);
}
bool
SetJsObject(JSContext* aContext,
const BluetoothValue& aValue,

View File

@ -26,6 +26,9 @@ void
BdAddressTypeToString(bt_bdaddr_t* aBdAddressType,
nsAString& aRetBdAddress);
void
UuidToString(bt_uuid_t* aUuid, nsAString& aString);
bool
SetJsObject(JSContext* aContext,
const BluetoothValue& aValue,

View File

@ -2814,6 +2814,13 @@ BluetoothDBusService::GetPairedDevicePropertiesInternal(
return NS_OK;
}
nsresult
FetchUuidsInternal(const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable)
{
return NS_OK;
}
class SetPropertyTask : public Task
{
public:

View File

@ -62,6 +62,10 @@ public:
GetPairedDevicePropertiesInternal(const nsTArray<nsString>& aDeviceAddresses,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual nsresult
FetchUuidsInternal(const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual nsresult StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual nsresult StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;

View File

@ -210,6 +210,8 @@ BluetoothParent::RecvPBluetoothRequestConstructor(
return actor->DoRequest(aRequest.get_PairedDevicePropertiesRequest());
case Request::TConnectedDevicePropertiesRequest:
return actor->DoRequest(aRequest.get_ConnectedDevicePropertiesRequest());
case Request::TFetchUuidsRequest:
return actor->DoRequest(aRequest.get_FetchUuidsRequest());
case Request::TSetPinCodeRequest:
return actor->DoRequest(aRequest.get_SetPinCodeRequest());
case Request::TSetPasskeyRequest:
@ -432,7 +434,8 @@ BluetoothRequestParent::DoRequest(const PairedDevicePropertiesRequest& aRequest)
}
bool
BluetoothRequestParent::DoRequest(const ConnectedDevicePropertiesRequest& aRequest)
BluetoothRequestParent::DoRequest(
const ConnectedDevicePropertiesRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TConnectedDevicePropertiesRequest);
@ -444,6 +447,19 @@ BluetoothRequestParent::DoRequest(const ConnectedDevicePropertiesRequest& aReque
return true;
}
bool
BluetoothRequestParent::DoRequest(const FetchUuidsRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TFetchUuidsRequest);
nsresult rv =
mService->FetchUuidsInternal(aRequest.address(), mReplyRunnable.get());
NS_ENSURE_SUCCESS(rv, false);
return true;
}
bool
BluetoothRequestParent::DoRequest(const SetPinCodeRequest& aRequest)
{

View File

@ -154,9 +154,13 @@ protected:
bool
DoRequest(const PairedDevicePropertiesRequest& aRequest);
bool
DoRequest(const ConnectedDevicePropertiesRequest& aRequest);
bool
DoRequest(const FetchUuidsRequest& aRequest);
bool
DoRequest(const SetPinCodeRequest& aRequest);

View File

@ -125,6 +125,7 @@ BluetoothServiceChildProcess::GetConnectedDevicePropertiesInternal(
SendRequest(aRunnable, ConnectedDevicePropertiesRequest(aServiceUuid));
return NS_OK;
}
nsresult
BluetoothServiceChildProcess::GetPairedDevicePropertiesInternal(
const nsTArray<nsString>& aDeviceAddresses,
@ -137,6 +138,14 @@ BluetoothServiceChildProcess::GetPairedDevicePropertiesInternal(
return NS_OK;
}
nsresult
BluetoothServiceChildProcess::FetchUuidsInternal(
const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable, FetchUuidsRequest(nsString(aDeviceAddress)));
return NS_OK;
}
nsresult
BluetoothServiceChildProcess::StopDiscoveryInternal(
BluetoothReplyRunnable* aRunnable)

View File

@ -62,6 +62,10 @@ public:
BluetoothReplyRunnable* aRunnable)
MOZ_OVERRIDE;
virtual nsresult
FetchUuidsInternal(const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual nsresult
StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual nsresult

View File

@ -96,6 +96,11 @@ struct ConnectedDevicePropertiesRequest
uint16_t serviceUuid;
};
struct FetchUuidsRequest
{
nsString address;
};
struct ConnectRequest
{
nsString address;
@ -188,6 +193,7 @@ union Request
DenyPairingConfirmationRequest;
ConnectedDevicePropertiesRequest;
PairedDevicePropertiesRequest;
FetchUuidsRequest;
ConnectRequest;
DisconnectRequest;
SendFileRequest;

View File

@ -22,6 +22,11 @@ StaticAutoPtr<nsCString> CameraPreferences::sPrefGonkParameters;
nsresult CameraPreferences::sPrefCameraControlMethodErrorOverride = NS_OK;
nsresult CameraPreferences::sPrefCameraControlAsyncErrorOverride = NS_OK;
uint32_t CameraPreferences::sPrefCameraControlLowMemoryThresholdMB = 0;
bool CameraPreferences::sPrefCameraParametersIsLowMemory = false;
#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
/* static */
nsresult
CameraPreferences::UpdatePref(const char* aPref, nsresult& aVal)
@ -33,6 +38,19 @@ CameraPreferences::UpdatePref(const char* aPref, nsresult& aVal)
}
return rv;
}
#endif
/* static */
nsresult
CameraPreferences::UpdatePref(const char* aPref, uint32_t& aVal)
{
uint32_t val;
nsresult rv = Preferences::GetUint(aPref, &val);
if (NS_SUCCEEDED(rv)) {
aVal = val;
}
return rv;
}
/* static */
nsresult
@ -46,6 +64,18 @@ CameraPreferences::UpdatePref(const char* aPref, nsACString& aVal)
return rv;
}
/* static */
nsresult
CameraPreferences::UpdatePref(const char* aPref, bool& aVal)
{
bool val;
nsresult rv = Preferences::GetBool(aPref, &val);
if (NS_SUCCEEDED(rv)) {
aVal = val;
}
return rv;
}
/* static */
CameraPreferences::Pref CameraPreferences::sPrefs[] = {
{
@ -67,14 +97,24 @@ CameraPreferences::Pref CameraPreferences::sPrefs[] = {
#endif
{
"camera.control.test.method.error",
kPrefValueIsNSResult,
kPrefValueIsNsResult,
{ &sPrefCameraControlMethodErrorOverride }
},
{
"camera.control.test.async.error",
kPrefValueIsNSResult,
kPrefValueIsNsResult,
{ &sPrefCameraControlAsyncErrorOverride }
},
{
"camera.control.test.is_low_memory",
kPrefValueIsBoolean,
{ &sPrefCameraParametersIsLowMemory }
},
{
"camera.control.low_memory_thresholdMB",
kPrefValueIsUint32,
{ &sPrefCameraControlLowMemoryThresholdMB }
},
};
/* static */
@ -104,7 +144,8 @@ CameraPreferences::PreferenceChanged(const char* aPref, void* aClosure)
Pref& p = sPrefs[i];
nsresult rv;
switch (p.mValueType) {
case kPrefValueIsNSResult:
case kPrefValueIsNsResult:
#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
{
nsresult& v = *p.mValue.mAsNsResult;
rv = UpdatePref(aPref, v);
@ -113,6 +154,17 @@ CameraPreferences::PreferenceChanged(const char* aPref, void* aClosure)
}
}
break;
#endif
case kPrefValueIsUint32:
{
uint32_t& v = *p.mValue.mAsUint32;
rv = UpdatePref(aPref, v);
if (NS_SUCCEEDED(rv)) {
DOM_CAMERA_LOGI("Preference '%s' has changed, %u\n", aPref, v);
}
}
break;
case kPrefValueIsCString:
{
@ -124,6 +176,17 @@ CameraPreferences::PreferenceChanged(const char* aPref, void* aClosure)
}
break;
case kPrefValueIsBoolean:
{
bool& v = *p.mValue.mAsBoolean;
rv = UpdatePref(aPref, v);
if (NS_SUCCEEDED(rv)) {
DOM_CAMERA_LOGI("Preference '%s' has changed, %s\n",
aPref, v ? "true" : "false");
}
}
break;
default:
MOZ_ASSERT_UNREACHABLE("Unhandled preference value type!");
return;
@ -211,6 +274,7 @@ CameraPreferences::GetPref(const char* aPref, nsACString& aVal)
return true;
}
#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
/* static */
bool
CameraPreferences::GetPref(const char* aPref, nsresult& aVal)
@ -223,14 +287,14 @@ CameraPreferences::GetPref(const char* aPref, nsresult& aVal)
DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
return false;
}
if (sPrefs[i].mValueType != kPrefValueIsNSResult) {
if (sPrefs[i].mValueType != kPrefValueIsNsResult) {
DOM_CAMERA_LOGW("Preference '%s' is not an nsresult type\n", aPref);
return false;
}
nsresult v = *sPrefs[i].mValue.mAsNsResult;
if (v == NS_OK) {
DOM_CAMERA_LOGI("Preference '%s' is not set\n", aPref);
DOM_CAMERA_LOGW("Preference '%s' is not set\n", aPref);
return false;
}
@ -238,3 +302,50 @@ CameraPreferences::GetPref(const char* aPref, nsresult& aVal)
aVal = v;
return true;
}
#endif
/* static */
bool
CameraPreferences::GetPref(const char* aPref, uint32_t& aVal)
{
MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
MonitorAutoLock mon(*sPrefMonitor);
uint32_t i = PrefToIndex(aPref);
if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
return false;
}
if (sPrefs[i].mValueType != kPrefValueIsUint32) {
DOM_CAMERA_LOGW("Preference '%s' is not a uint32_t type\n", aPref);
return false;
}
uint32_t v = *sPrefs[i].mValue.mAsUint32;
DOM_CAMERA_LOGI("Preference '%s', got %u\n", aPref, v);
aVal = v;
return true;
}
/* static */
bool
CameraPreferences::GetPref(const char* aPref, bool& aVal)
{
MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
MonitorAutoLock mon(*sPrefMonitor);
uint32_t i = PrefToIndex(aPref);
if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
return false;
}
if (sPrefs[i].mValueType != kPrefValueIsBoolean) {
DOM_CAMERA_LOGW("Preference '%s' is not a boolean type\n", aPref);
return false;
}
bool v = *sPrefs[i].mValue.mAsBoolean;
DOM_CAMERA_LOGI("Preference '%s', got %s\n", aPref, v ? "true" : "false");
aVal = v;
return true;
}

View File

@ -8,6 +8,13 @@
#include "nsString.h"
#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) || defined(MOZ_HAVE_CXX11_ENUM_TYPE)
// Older compilers that don't support strongly-typed enums
// just typedef uint32_t to nsresult, which results in conflicting
// overloaded members in CameraPreferences.
#define CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
#endif
namespace mozilla {
template<class T> class StaticAutoPtr;
@ -19,19 +26,29 @@ public:
static void Shutdown();
static bool GetPref(const char* aPref, nsACString& aVal);
#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
static bool GetPref(const char* aPref, nsresult& aVal);
#endif
static bool GetPref(const char* aPref, uint32_t& aVal);
static bool GetPref(const char* aPref, bool& aVal);
protected:
static const uint32_t kPrefNotFound = UINT32_MAX;
static uint32_t PrefToIndex(const char* aPref);
static void PreferenceChanged(const char* aPref, void* aClosure);
#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
static nsresult UpdatePref(const char* aPref, nsresult& aVar);
#endif
static nsresult UpdatePref(const char* aPref, uint32_t& aVar);
static nsresult UpdatePref(const char* aPref, nsACString& aVar);
static nsresult UpdatePref(const char* aPref, bool& aVar);
enum PrefValueType {
kPrefValueIsNSResult,
kPrefValueIsCString
kPrefValueIsNsResult,
kPrefValueIsUint32,
kPrefValueIsCString,
kPrefValueIsBoolean
};
struct Pref {
const char* const mPref;
@ -45,6 +62,8 @@ protected:
void* mAsVoid;
StaticAutoPtr<nsCString>* mAsCString;
nsresult* mAsNsResult;
uint32_t* mAsUint32;
bool* mAsBoolean;
} mValue;
};
static Pref sPrefs[];
@ -56,6 +75,10 @@ protected:
static nsresult sPrefCameraControlMethodErrorOverride;
static nsresult sPrefCameraControlAsyncErrorOverride;
static uint32_t sPrefCameraControlLowMemoryThresholdMB;
static bool sPrefCameraParametersIsLowMemory;
private:
// static class only
CameraPreferences();

View File

@ -16,12 +16,40 @@
#include "GonkCameraParameters.h"
#include "camera/CameraParameters.h"
#include "CameraPreferences.h"
#include "ICameraControl.h"
#include "CameraCommon.h"
#include "mozilla/Hal.h"
using namespace mozilla;
using namespace android;
/* static */ bool
GonkCameraParameters::IsLowMemoryPlatform()
{
bool testIsLowMem = false;
CameraPreferences::GetPref("camera.control.test.is_low_memory", testIsLowMem);
if (testIsLowMem) {
NS_WARNING("Forcing low-memory platform camera preferences");
return true;
}
uint32_t lowMemoryThresholdBytes = 0;
CameraPreferences::GetPref("camera.control.low_memory_thresholdMB",
lowMemoryThresholdBytes);
lowMemoryThresholdBytes *= 1024 * 1024;
if (lowMemoryThresholdBytes) {
uint32_t totalMemoryBytes = hal::GetTotalSystemMemory();
if (totalMemoryBytes < lowMemoryThresholdBytes) {
DOM_CAMERA_LOGI("Low-memory platform with %d bytes of RAM (threshold: <%d bytes)\n",
totalMemoryBytes, lowMemoryThresholdBytes);
return true;
}
}
return false;
}
/* static */ const char*
GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
{
@ -246,7 +274,7 @@ GonkCameraParameters::Initialize()
nsString s;
nsTArray<nsCString> isoModes;
GetListAsArray(CAMERA_PARAM_SUPPORTED_ISOMODES, isoModes);
for (uint32_t i = 0; i < isoModes.Length(); ++i) {
for (nsTArray<nsCString>::size_type i = 0; i < isoModes.Length(); ++i) {
rv = MapIsoFromGonk(isoModes[i].get(), s);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGW("Unrecognized ISO mode value '%s'\n", isoModes[i].get());
@ -256,6 +284,17 @@ GonkCameraParameters::Initialize()
mIsoModeMap.Put(s, new nsCString(isoModes[i]));
}
GetListAsArray(CAMERA_PARAM_SUPPORTED_SCENEMODES, mSceneModes);
if (IsLowMemoryPlatform()) {
bool hdrRemoved = false;
while (mSceneModes.RemoveElement(NS_LITERAL_STRING("hdr"))) {
hdrRemoved = true;
}
if (hdrRemoved) {
DOM_CAMERA_LOGI("Disabling HDR support due to low memory\n");
}
}
mInitialized = true;
return NS_OK;
}
@ -264,16 +303,26 @@ GonkCameraParameters::Initialize()
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue)
{
if (aKey == CAMERA_PARAM_ISOMODE) {
nsAutoCString v;
nsresult rv = MapIsoToGonk(aValue, v);
if (NS_FAILED(rv)) {
return rv;
}
return SetImpl(aKey, v.get());
}
switch (aKey) {
case CAMERA_PARAM_ISOMODE:
{
nsAutoCString v;
nsresult rv = MapIsoToGonk(aValue, v);
if (NS_FAILED(rv)) {
return rv;
}
return SetImpl(aKey, v.get());
}
return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
case CAMERA_PARAM_SCENEMODE:
if (mSceneModes.IndexOf(aValue) == nsTArray<nsString>::NoIndex) {
return NS_ERROR_INVALID_ARG;
}
// fallthrough
default:
return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
}
}
nsresult
@ -620,7 +669,7 @@ GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue)
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, double& aValue)
{
double val;
double val = 0.0; // initialize to keep the compiler happy [-Wmaybe-uninitialized]
int index = 0;
double focusDistance[3];
const char* s;
@ -840,12 +889,18 @@ GonkCameraParameters::GetListAsArray(uint32_t aKey, nsTArray<T>& aArray)
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues)
{
if (aKey == CAMERA_PARAM_SUPPORTED_ISOMODES) {
aValues = mIsoModes;
return NS_OK;
}
switch (aKey) {
case CAMERA_PARAM_SUPPORTED_ISOMODES:
aValues = mIsoModes;
return NS_OK;
return GetListAsArray(aKey, aValues);
case CAMERA_PARAM_SUPPORTED_SCENEMODES:
aValues = mSceneModes;
return NS_OK;
default:
return GetListAsArray(aKey, aValues);
}
}
nsresult

View File

@ -101,6 +101,7 @@ protected:
int32_t mExposureCompensationMaxIndex;
nsTArray<int> mZoomRatios;
nsTArray<nsString> mIsoModes;
nsTArray<nsString> mSceneModes;
nsClassHashtable<nsStringHashKey, nsCString> mIsoModeMap;
// This subclass of android::CameraParameters just gives
@ -224,6 +225,10 @@ protected:
// Call once to initialize local cached values used in translating other
// arguments between Gecko and Gonk. Always returns NS_OK.
nsresult Initialize();
// Returns true if we're a memory-constrained platform that requires
// certain features to be disabled; returns false otherwise.
static bool IsLowMemoryPlatform();
};
} // namespace mozilla

View File

@ -35,6 +35,7 @@ var CameraTest = (function() {
const PREF_TEST_ENABLED = "camera.control.test.enabled";
const PREF_TEST_HARDWARE = "camera.control.test.hardware";
const PREF_TEST_EXTRA_PARAMETERS = "camera.control.test.hardware.gonk.parameters";
const PREF_TEST_FAKE_LOW_MEMORY = "camera.control.test.is_low_memory";
var oldTestEnabled;
var oldTestHw;
var testMode;
@ -53,6 +54,20 @@ var CameraTest = (function() {
SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_EXTRA_PARAMETERS]]}, callback);
}
function testHardwareSetFakeLowMemoryPlatform(callback) {
SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_FAKE_LOW_MEMORY, true]]}, function() {
var setParams = SpecialPowers.getBoolPref(PREF_TEST_FAKE_LOW_MEMORY);
ise(setParams, true, "Fake low memory platform");
if (callback) {
callback(setParams);
}
});
}
function testHardwareClearFakeLowMemoryPlatform(callback) {
SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_FAKE_LOW_MEMORY]]}, callback);
}
function testHardwareSet(test, callback) {
SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_HARDWARE, test]]}, function() {
var setTest = SpecialPowers.getCharPref(PREF_TEST_HARDWARE);
@ -88,6 +103,8 @@ var CameraTest = (function() {
set: testHardwareSet,
setFakeParameters: testHardwareSetFakeParameters,
clearFakeParameters: testHardwareClearFakeParameters,
setFakeLowMemoryPlatform: testHardwareSetFakeLowMemoryPlatform,
clearFakeLowMemoryPlatform: testHardwareClearFakeLowMemoryPlatform,
done: testHardwareDone
};
if (callback) {
@ -122,8 +139,16 @@ var CameraTest = (function() {
next();
}
}
function cleanUpExtraParameters() {
function cleanUpLowMemoryPlatform() {
var next = cleanUpTest;
if (testMode) {
testMode.clearFakeLowMemoryPlatform(next);
} else {
next();
}
}
function cleanUpExtraParameters() {
var next = cleanUpLowMemoryPlatform;
if (testMode) {
testMode.clearFakeParameters(next);
} else {

View File

@ -114,6 +114,61 @@ var tests = [
next();
}
},
{
key: "fake-high-memory-platform",
prep: function setupFakeHighMemoryPlatform(test) {
test.setFakeParameters("scene-mode-values=none,snow,beach,hdr,nothdr", function () {
run();
});
},
test: function testFakeHighMemoryPlatform(cam, cap) {
ok(cap.sceneModes.length == 5, "scene modes length = " + cap.zoomRatios.length);
// make sure expected values are present and can be set
[ "none", "snow", "beach", "hdr", "nothdr" ].forEach(function(mode) {
ok(cap.sceneModes.indexOf(mode) != -1, "Scene mode '" + mode + "' is present");
cam.sceneMode = mode;
ok(cam.sceneMode == mode, "Scene mode '" + cam.sceneMode + "' is set");
});
next();
}
},
{
key: "fake-low-memory-platform",
prep: function setupFakeLowMemoryPlatform(test) {
test.setFakeLowMemoryPlatform(function() {
test.setFakeParameters("scene-mode-values=none,hdr,snow,beach,hdr,nothdr", function () {
run();
});
});
},
test: function testFakeLowMemoryPlatform(cam, cap) {
ok(cap.sceneModes.length == 4, "scene modes length = " + cap.zoomRatios.length);
// make sure expected values are present and can be set
[ "none", "snow", "beach", "nothdr" ].forEach(function(mode) {
ok(cap.sceneModes.indexOf(mode) != -1, "Scene mode '" + mode + "' is present");
cam.sceneMode = mode;
ok(cam.sceneMode == mode, "Scene mode '" + cam.sceneMode + "' is set");
});
// make sure unsupported values have been removed, and can't be set
var sceneMode = cam.sceneMode;
[ "hdr" ].forEach(function(mode) {
ok(cap.sceneModes.indexOf(mode) == -1, "Scene mode '" + mode + "' is not present");
try {
cam.sceneMode = mode;
} catch(e) {
}
ok(cam.sceneMode != mode, "Scene mode '" + cam.sceneMode + "' is still set, '"
+ mode + "' rejected");
});
ok(cam.sceneMode == sceneMode, "Scene mode '" + cam.sceneMode + "' is still set");
next();
}
},
{
key: "fake-iso",
prep: function setupFakeIso(test) {
@ -129,20 +184,14 @@ var tests = [
ok(cap.isoModes.length == 7, "ISO modes length = " + cap.isoModes.length);
// make sure we're not leaking any unexpected values formats
ok(cap.isoModes.indexOf("ISO_HJR") == -1, "ISO mode 'ISO_HJR' does not appear");
ok(cap.isoModes.indexOf("_HJR") == -1, "ISO mode '_HJR' does not appear");
ok(cap.isoModes.indexOf("HJR") == -1, "ISO mode 'HJR' does not appear");
ok(cap.isoModes.indexOf("ISO100") == -1, "ISO mode 'ISO100' does not appear");
ok(cap.isoModes.indexOf("ISO200") == -1, "ISO mode 'ISO200' does not appear");
ok(cap.isoModes.indexOf("ISO800") == -1, "ISO mode 'ISO800' does not appear");
[ "ISO_HJR", "_HJR", "HJR", "ISO100", "ISO200", "ISO800" ].forEach(function(iso) {
ok(cap.isoModes.indexOf(iso) == -1, "ISO mode '" + iso + "' does not appear");
});
// make sure any weird values are dropped entirely
ok(cap.isoModes.indexOf("foo") == -1, "Unknown ISO mode 'foo' is ignored");
ok(cap.isoModes.indexOf("ISObar") == -1, "Unknown ISO mode 'ISObar' is ignored");
ok(cap.isoModes.indexOf("bar") == -1, "Unknown ISO mode 'bar' is ignored");
ok(cap.isoModes.indexOf("ISO150moz") == -1, "Unknown ISO mode 'ISO150moz' is ignored");
ok(cap.isoModes.indexOf("150moz") == -1, "Unknown ISO mode '150moz' is ignored");
ok(cap.isoModes.indexOf("150") == -1, "Unknown ISO mode '150' is ignored");
[ "foo", "ISObar", "bar", "ISO150moz", "150moz", "150" ].forEach(function(iso) {
ok(cap.isoModes.indexOf(iso) == -1, "Unknown ISO mode '" + iso + "' is ignored");
});
// make sure expected values are present
[ "auto", "hjr", "100", "200", "400", "800", "1600" ].forEach(function(iso) {

View File

@ -4,19 +4,42 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
interface BluetoothDevice : EventTarget {
readonly attribute DOMString address;
readonly attribute DOMString name;
readonly attribute DOMString icon;
readonly attribute boolean connected;
readonly attribute boolean paired;
readonly attribute unsigned long class;
// array of type DOMString[]
[Throws]
readonly attribute any uuids;
// array of type DOMString[]
[Throws]
readonly attribute any services;
/*
* Set of attributes that might be changed and reported by attributechanged
* event.
* Address is not included since it should not be changed once BluetoothDevice
* is created.
*/
enum BluetoothDeviceAttribute
{
"unknown",
"cod",
"name",
"paired",
"uuids"
};
[CheckPermissions="bluetooth"]
interface BluetoothDevice : EventTarget
{
readonly attribute DOMString address;
readonly attribute BluetoothClassOfDevice cod;
readonly attribute DOMString name;
readonly attribute boolean paired;
[Cached, Pure]
readonly attribute sequence<DOMString> uuids;
attribute EventHandler onattributechanged;
/**
* Fetch the up-to-date UUID list of each bluetooth service that the device
* provides and refresh the cache value of attribute uuids if it is updated.
*
* If the operation succeeds, the promise will be resolved with up-to-date
* UUID list which is identical to attribute uuids.
*/
// Promise<sequence<DOMString>>
[NewObject, Throws]
Promise fetchUuids();
};

View File

@ -240,6 +240,9 @@ this.WifiCommand = function(aControlMessage, aInterface, aSdkVersion) {
});
};
let infoKeys = [{regexp: /RSSI=/i, prop: 'rssi'},
{regexp: /LINKSPEED=/i, prop: 'linkspeed'}];
command.getConnectionInfoICS = function (callback) {
doStringCommand("SIGNAL_POLL", function(reply) {
if (!reply) {
@ -247,19 +250,21 @@ this.WifiCommand = function(aControlMessage, aInterface, aSdkVersion) {
return;
}
// Find any values matching |infoKeys|. This gets executed frequently
// enough that we want to avoid creating intermediate strings as much as
// possible.
let rval = {};
var lines = reply.split("\n");
for (let i = 0; i < lines.length; ++i) {
let [key, value] = lines[i].split("=");
switch (key.toUpperCase()) {
case "RSSI":
rval.rssi = value | 0;
break;
case "LINKSPEED":
rval.linkspeed = value | 0;
break;
default:
// Ignore.
for (let i = 0; i < infoKeys.length; i++) {
let re = infoKeys[i].regexp;
let iKeyStart = reply.search(re);
if (iKeyStart !== -1) {
let prop = infoKeys[i].prop;
let iValueStart = reply.indexOf('=', iKeyStart) + 1;
let iNewlineAfterValue = reply.indexOf('\n', iValueStart);
let iValueEnd = iNewlineAfterValue !== -1
? iNewlineAfterValue
: reply.length;
rval[prop] = reply.substring(iValueStart, iValueEnd) | 0;
}
}

View File

@ -4273,3 +4273,11 @@ pref("beacon.enabled", true);
// Camera prefs
pref("camera.control.autofocus_moving_callback.enabled", true);
pref("camera.control.face_detection.enabled", true);
#ifdef MOZ_WIDGET_GONK
// Empirically, this is the value returned by hal::GetTotalSystemMemory()
// when Flame's memory is limited to 512MiB. If the camera stack determines
// it is running on a low memory platform, features that can be reliably
// supported will be disabled. This threshold can be adjusted to suit other
// platforms; and set to 0 to disable the low-memory check altogether.
pref("camera.control.low_memory_thresholdMB", 404);
#endif