Merge birch to m-c. IGNORE BAD COMMIT MESSAGES

This commit is contained in:
Ryan VanderMeulen 2013-05-10 14:56:24 -04:00
commit 7f00594f07
82 changed files with 2722 additions and 933 deletions

View File

@ -564,23 +564,8 @@ pref("dom.ipc.processPriorityManager.enabled", true);
pref("dom.ipc.processPriorityManager.backgroundGracePeriodMS", 1000);
pref("dom.ipc.processPriorityManager.temporaryPriorityLockMS", 5000);
// Kernel parameters for how processes are killed on low-memory.
pref("gonk.systemMemoryPressureRecoveryPollMS", 5000);
pref("hal.processPriorityManager.gonk.masterOomScoreAdjust", 0);
pref("hal.processPriorityManager.gonk.masterKillUnderMB", 4);
pref("hal.processPriorityManager.gonk.foregroundHighOomScoreAdjust", 67);
pref("hal.processPriorityManager.gonk.foregroundHighKillUnderMB", 5);
pref("hal.processPriorityManager.gonk.foregroundOomScoreAdjust", 134);
pref("hal.processPriorityManager.gonk.foregroundKillUnderMB", 6);
pref("hal.processPriorityManager.gonk.backgroundPerceivableOomScoreAdjust", 200);
pref("hal.processPriorityManager.gonk.backgroundPerceivableKillUnderMB", 7);
pref("hal.processPriorityManager.gonk.backgroundHomescreenOomScoreAdjust", 267);
pref("hal.processPriorityManager.gonk.backgroundHomescreenKillUnderMB", 8);
pref("hal.processPriorityManager.gonk.backgroundOomScoreAdjust", 400);
pref("hal.processPriorityManager.gonk.backgroundKillUnderMB", 20);
pref("hal.processPriorityManager.gonk.notifyLowMemUnderMB", 10);
// Niceness values (i.e., CPU priorities) for B2G processes.
// Kernel parameters for process priorities. These affect how processes are
// killed on low-memory and their relative CPU priorities.
//
// Note: The maximum nice value on Linux is 19, but the max value you should
// use here is 18. NSPR adds 1 to some threads' nice values, to mark
@ -589,12 +574,47 @@ pref("hal.processPriorityManager.gonk.notifyLowMemUnderMB", 10);
// niceness. Then when we reniced the process to (say) 10, all threads would
// /still/ have the same niceness; we'd effectively have erased NSPR's thread
// priorities.
pref("hal.processPriorityManager.gonk.masterNice", 0);
pref("hal.processPriorityManager.gonk.foregroundHighNice", 0);
pref("hal.processPriorityManager.gonk.foregroundNice", 1);
pref("hal.processPriorityManager.gonk.backgroundPerceivableNice", 10);
pref("hal.processPriorityManager.gonk.backgroundHomescreenNice", 18);
pref("hal.processPriorityManager.gonk.backgroundNice", 18);
pref("hal.processPriorityManager.gonk.MASTER.OomScoreAdjust", 0);
pref("hal.processPriorityManager.gonk.MASTER.KillUnderMB", 4);
pref("hal.processPriorityManager.gonk.MASTER.Nice", 0);
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.OomScoreAdjust", 67);
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.KillUnderMB", 5);
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.Nice", 0);
pref("hal.processPriorityManager.gonk.FOREGROUND.OomScoreAdjust", 134);
pref("hal.processPriorityManager.gonk.FOREGROUND.KillUnderMB", 6);
pref("hal.processPriorityManager.gonk.FOREGROUND.Nice", 1);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.OomScoreAdjust", 200);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderMB", 7);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.Nice", 10);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.OomScoreAdjust", 267);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.KillUnderMB", 8);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.Nice", 18);
pref("hal.processPriorityManager.gonk.BACKGROUND.OomScoreAdjust", 400);
pref("hal.processPriorityManager.gonk.BACKGROUND.KillUnderMB", 20);
pref("hal.processPriorityManager.gonk.BACKGROUND.Nice", 18);
// Processes get this niceness when they have low CPU priority.
pref("hal.processPriorityManager.gonk.LowCPUNice", 18);
// Fire a memory pressure event when the system has less than Xmb of memory
// remaining. You should probably set this just above Y.KillUnderMB for
// the highest priority class Y that you want to make an effort to keep alive.
// (For example, we want BACKGROUND_PERCEIVABLE to stay alive.) If you set
// this too high, then we'll send out a memory pressure event every Z seconds
// (see below), even while we have processes that we would happily kill in
// order to free up memory.
pref("hal.processPriorityManager.gonk.notifyLowMemUnderMB", 10);
// We wait this long before polling the memory-pressure fd after seeing one
// memory pressure event. (When we're not under memory pressure, we sit
// blocked on a poll(), and this pref has no effect.)
pref("gonk.systemMemoryPressureRecoveryPollMS", 5000);
#ifndef DEBUG
// Enable pre-launching content processes for improved startup time
@ -697,3 +717,6 @@ pref("captivedetect.canonicalContent", "success\n");
// The url of the manifest we use for ADU pings.
pref("ping.manifestURL", "https://marketplace.firefox.com/packaged.webapp");
// Enable the disk space watcher
pref("disk_space_watcher.enabled", true);

View File

@ -155,6 +155,7 @@
@BINPATH@/components/content_xslt.xpt
@BINPATH@/components/cookie.xpt
@BINPATH@/components/directory.xpt
@BINPATH@/components/diskspacewatcher.xpt
@BINPATH@/components/docshell.xpt
@BINPATH@/components/dom.xpt
@BINPATH@/components/dom_activities.xpt

View File

@ -2554,6 +2554,10 @@ nsFrameLoader::ResetPermissionManagerStatus()
/* [infallible] */ NS_IMETHODIMP
nsFrameLoader::SetVisible(bool aVisible)
{
if (mVisible == aVisible) {
return NS_OK;
}
mVisible = aVisible;
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {

View File

@ -121,7 +121,7 @@ public:
* Call this to reevaluate whether we should start/stop due to our owner
* document being active, inactive, visible or hidden.
*/
void NotifyOwnerDocumentActivityChanged();
virtual void NotifyOwnerDocumentActivityChanged();
// Called by the video decoder object, on the main thread,
// when it has read the metadata containing video dimensions,
@ -536,10 +536,16 @@ protected:
bool mValue;
bool mCanPlay;
HTMLMediaElement* mOuter;
nsCOMPtr<nsIDOMMozWakeLock> mWakeLock;
};
/**
* These two methods are called by the WakeLockBoolWrapper when the wakelock
* has to be created or released.
*/
virtual void WakeLockCreate();
virtual void WakeLockRelease();
nsCOMPtr<nsIDOMMozWakeLock> mWakeLock;
/**
* Logs a warning message to the web console to report various failures.
* aMsg is the localized message identifier, aParams is the parameters to

View File

@ -108,9 +108,17 @@ public:
bool MozHasAudio() const;
void NotifyOwnerDocumentActivityChanged() MOZ_OVERRIDE;
protected:
virtual JSObject* WrapNode(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
virtual void WakeLockCreate();
virtual void WakeLockRelease();
void WakeLockUpdate();
nsCOMPtr<nsIDOMMozWakeLock> mScreenWakeLock;
};
} // namespace dom

View File

@ -2117,21 +2117,35 @@ HTMLMediaElement::WakeLockBoolWrapper::UpdateWakeLock()
{
if (!mOuter) {
return;
}
bool playing = (!mValue && mCanPlay);
if (playing) {
mOuter->WakeLockCreate();
} else {
mOuter->WakeLockRelease();
}
}
void
HTMLMediaElement::WakeLockCreate()
{
if (!mWakeLock) {
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
NS_ENSURE_TRUE_VOID(pmService);
if (!mWakeLock) {
pmService->NewWakeLock(NS_LITERAL_STRING("cpu"),
mOuter->OwnerDoc()->GetWindow(),
getter_AddRefs(mWakeLock));
}
} else if (mWakeLock) {
pmService->NewWakeLock(NS_LITERAL_STRING("cpu"),
OwnerDoc()->GetWindow(),
getter_AddRefs(mWakeLock));
}
}
void
HTMLMediaElement::WakeLockRelease()
{
if (mWakeLock) {
mWakeLock->Unlock();
mWakeLock = nullptr;
}

View File

@ -30,6 +30,7 @@
#include "nsEventDispatcher.h"
#include "nsIDOMProgressEvent.h"
#include "nsIPowerManagerService.h"
#include "MediaError.h"
#include "MediaDecoder.h"
@ -235,5 +236,51 @@ HTMLVideoElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
return HTMLVideoElementBinding::Wrap(aCx, aScope, this);
}
void
HTMLVideoElement::NotifyOwnerDocumentActivityChanged()
{
HTMLMediaElement::NotifyOwnerDocumentActivityChanged();
WakeLockUpdate();
}
void
HTMLVideoElement::WakeLockCreate()
{
WakeLockUpdate();
}
void
HTMLVideoElement::WakeLockRelease()
{
WakeLockUpdate();
}
void
HTMLVideoElement::WakeLockUpdate()
{
bool hidden = true;
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(OwnerDoc());
if (domDoc) {
domDoc->GetHidden(&hidden);
}
if (mScreenWakeLock && (mPaused || hidden)) {
mScreenWakeLock->Unlock();
mScreenWakeLock = nullptr;
return;
}
if (!mScreenWakeLock && !mPaused && !hidden) {
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
NS_ENSURE_TRUE_VOID(pmService);
pmService->NewWakeLock(NS_LITERAL_STRING("screen"),
OwnerDoc()->GetWindow(),
getter_AddRefs(mScreenWakeLock));
}
}
} // namespace dom
} // namespace mozilla

View File

@ -352,7 +352,9 @@ MOCHITEST_FILES = \
test_element_prototype.html \
test_formData.html \
test_audio_wakelock.html \
test_video_wakelock.html \
wakelock.ogg \
wakelock.ogv \
test_bug869040.html \
$(NULL)

View File

@ -0,0 +1,105 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=868943
-->
<head>
<title>Test for Bug 868943</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=868943">Mozilla Bug 868943</a>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 868943 **/
SpecialPowers.addPermission("power", true, document);
function testVideoPlayPause() {
var lockState = true;
var count = 0;
var content = document.getElementById('content');
var video = document.createElement('video');
video.src = "wakelock.ogv";
content.appendChild(video);
navigator.mozPower.addWakeLockListener(function testVideoPlayPauseListener(topic, state) {
is(topic, "screen", "Video element locked the target == screen");
var locked = state == "locked-foreground" ||
state == "locked-background";
is(locked, lockState, "Video element locked the screen - paused");
count++;
if (count == 1) {
// The next step is to unlock the resource.
lockState = false;
video.pause();
} else if (count == 2) {
content.removeChild(video);
navigator.mozPower.removeWakeLockListener(testVideoPlayPauseListener);
runTests();
}
});
video.play();
}
function testVideoPlay() {
var lockState = true;
var count = 0;
var content = document.getElementById('content');
var video = document.createElement('video');
video.src = "wakelock.ogv";
content.appendChild(video);
navigator.mozPower.addWakeLockListener(function testVideoPlayListener(topic, state) {
is(topic, "screen", "Video element locked the target == screen");
var locked = state == "locked-foreground" ||
state == "locked-background";
is(locked, lockState, "Video element locked the screen - no paused");
count++;
if (count == 1) {
// The next step is to unlock the resource.
lockState = false;
} else if (count == 2) {
content.removeChild(video);
navigator.mozPower.removeWakeLockListener(testVideoPlayListener);
runTests();
}
});
video.play();
}
var tests = [ testVideoPlayPause, testVideoPlay ];
function runTests() {
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.pop();
test();
};
SimpleTest.waitForExplicitFinish();
runTests();
</script>
</pre>
</body>
</html>

Binary file not shown.

View File

@ -124,6 +124,37 @@ private:
nsRefPtr<BluetoothAdapter> mAdapterPtr;
};
class GetScoConnectionStatusTask : public BluetoothReplyRunnable
{
public:
GetScoConnectionStatusTask(nsIDOMDOMRequest* aReq) :
BluetoothReplyRunnable(aReq)
{
MOZ_ASSERT(aReq);
}
virtual bool ParseSuccessfulReply(JS::Value* aValue)
{
*aValue = JSVAL_VOID;
const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
if (v.type() != BluetoothValue::Tbool) {
NS_WARNING("Not a boolean!");
SetError(NS_LITERAL_STRING("BluetoothReplyTypeError"));
return false;
}
aValue->setBoolean(v.get_bool());
return true;
}
void
ReleaseMembers()
{
BluetoothReplyRunnable::ReleaseMembers();
}
};
static int kCreatePairedDeviceTimeout = 50000; // unit: msec
nsresult
@ -743,4 +774,58 @@ BluetoothAdapter::ConfirmReceivingFile(const nsAString& aDeviceAddress,
return NS_OK;
}
NS_IMETHODIMP
BluetoothAdapter::ConnectSco(nsIDOMDOMRequest** aRequest)
{
nsCOMPtr<nsIDOMDOMRequest> req;
nsresult rv = PrepareDOMRequest(GetOwner(), getter_AddRefs(req));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
nsRefPtr<BluetoothVoidReplyRunnable> results =
new BluetoothVoidReplyRunnable(req);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
bs->ConnectSco(results);
req.forget(aRequest);
return NS_OK;
}
NS_IMETHODIMP
BluetoothAdapter::DisconnectSco(nsIDOMDOMRequest** aRequest)
{
nsCOMPtr<nsIDOMDOMRequest> req;
nsresult rv = PrepareDOMRequest(GetOwner(), getter_AddRefs(req));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
nsRefPtr<BluetoothVoidReplyRunnable> results =
new BluetoothVoidReplyRunnable(req);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
bs->DisconnectSco(results);
req.forget(aRequest);
return NS_OK;
}
NS_IMETHODIMP
BluetoothAdapter::IsScoConnected(nsIDOMDOMRequest** aRequest)
{
nsCOMPtr<nsIDOMDOMRequest> req;
nsresult rv = PrepareDOMRequest(GetOwner(), getter_AddRefs(req));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
nsRefPtr<BluetoothReplyRunnable> results =
new GetScoConnectionStatusTask(req);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
bs->IsScoConnected(results);
req.forget(aRequest);
return NS_OK;
}
NS_IMPL_EVENT_HANDLER(BluetoothAdapter, devicefound)

View File

@ -9,7 +9,6 @@
#include "BluetoothHfpManager.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothScoManager.h"
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
@ -31,6 +30,7 @@
#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
#define MOBILE_CONNECTION_ICCINFO_CHANGED "mobile-connection-iccinfo-changed"
#define MOBILE_CONNECTION_VOICE_CHANGED "mobile-connection-voice-changed"
#define BLUETOOTH_SCO_STATUS_CHANGED "bluetooth-sco-status-changed"
/**
* These constants are used in result code such as +CLIP and +CCWA. The value
@ -125,7 +125,7 @@ class mozilla::dom::bluetooth::Call {
public:
Call(uint16_t aState = nsITelephonyProvider::CALL_STATE_DISCONNECTED,
bool aDirection = false,
const nsAString& aNumber = NS_LITERAL_STRING(""),
const nsAString& aNumber = EmptyString(),
int aType = TOA_UNKNOWN)
: mState(aState), mDirection(aDirection), mNumber(aNumber), mType(aType)
{
@ -333,35 +333,6 @@ private:
int mType;
};
void
OpenScoSocket(const nsAString& aDeviceAddress)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothScoManager* sco = BluetoothScoManager::Get();
if (!sco) {
NS_WARNING("BluetoothScoManager is not available!");
return;
}
if (!sco->Connect(aDeviceAddress)) {
NS_WARNING("Failed to create a sco socket!");
}
}
void
CloseScoSocket()
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothScoManager* sco = BluetoothScoManager::Get();
if (!sco) {
NS_WARNING("BluetoothScoManager is not available!");
return;
}
sco->Disconnect();
}
static bool
IsValidDtmf(const char aChar) {
// Valid DTMF: [*#0-9ABCD]
@ -439,6 +410,12 @@ BluetoothHfpManager::Init()
Listen();
mScoSocket = new BluetoothSocket(this,
BluetoothSocketType::SCO,
true,
false);
mScoSocketStatus = mScoSocket->GetConnectionStatus();
ListenSco();
return true;
}
@ -497,7 +474,7 @@ BluetoothHfpManager::NotifySettings()
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("address");
v = mDevicePath;
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
if (!BroadcastSystemMessage(type, parameters)) {
@ -524,6 +501,22 @@ BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
}
}
void
BluetoothHfpManager::NotifyAudioManager(const nsAString& aAddress)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs =
do_GetService("@mozilla.org/observer-service;1");
NS_ENSURE_TRUE_VOID(obs);
if (NS_FAILED(obs->NotifyObservers(nullptr,
BLUETOOTH_SCO_STATUS_CHANGED,
aAddress.BeginReading()))) {
NS_WARNING("Failed to notify bluetooth-sco-status-changed observsers!");
}
}
nsresult
BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
{
@ -682,6 +675,7 @@ BluetoothHfpManager::HandleShutdown()
MOZ_ASSERT(NS_IsMainThread());
gInShutdown = true;
Disconnect();
DisconnectSco();
gBluetoothHfpManager = nullptr;
return NS_OK;
}
@ -766,7 +760,6 @@ BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket,
message.Append(NS_ConvertUTF16toUTF8(mOperatorName));
message.AppendLiteral("\"");
SendLine(message.get());
return;
} else if (msg.Find("AT+VTS=") != -1) {
ParseAtCommand(msg, 7, atCommandValues);
@ -917,12 +910,6 @@ BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket,
mCCWA = atCommandValues[0].EqualsLiteral("1");
} else if (msg.Find("AT+CKPD") != -1) {
BluetoothScoManager* sco = BluetoothScoManager::Get();
if (!sco) {
NS_WARNING("Couldn't get BluetoothScoManager instance");
goto respond_with_ok;
}
if (!sStopSendingRingFlag) {
// Bluetooth HSP spec 4.2.2
// There is an incoming call, notify Dialer to pick up the phone call
@ -930,12 +917,10 @@ BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket,
// indicating the call is answered successfully.
NotifyDialer(NS_LITERAL_STRING("ATA"));
} else {
if (!sco->IsConnected()) {
if (!IsScoConnected()) {
// Bluetooth HSP spec 4.3
// If there's no SCO, set up a SCO link.
nsAutoString address;
mSocket->GetAddress(address);
sco->Connect(address);
ConnectSco();
} else if (!mFirstCKPD) {
// Bluetooth HSP spec 4.5
// There are two ways to release SCO: sending CHUP to dialer or closing
@ -944,7 +929,7 @@ BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket,
if (mCurrentCallArray.Length() > 1) {
NotifyDialer(NS_LITERAL_STRING("CHUP"));
} else {
sco->Disconnect();
DisconnectSco();
}
} else {
// Three conditions have to be matched to come in here:
@ -982,21 +967,31 @@ respond_with_ok:
SendLine("OK");
}
bool
void
BluetoothHfpManager::Connect(const nsAString& aDevicePath,
const bool aIsHandsfree,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
if (gInShutdown) {
NS_WARNING("Connect called while in shutdown!");
return false;
NS_ENSURE_FALSE_VOID(gInShutdown);
NS_ENSURE_FALSE_VOID(mSocket);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
nsString uuid;
if (aIsHandsfree) {
BluetoothUuidHelper::GetString(BluetoothServiceClass::HANDSFREE, uuid);
} else {
BluetoothUuidHelper::GetString(BluetoothServiceClass::HEADSET, uuid);
}
if (mSocket) {
NS_WARNING("BluetoothHfpManager has been already connected");
return false;
if (NS_FAILED(bs->GetServiceChannel(aDevicePath, uuid, this))) {
BluetoothValue v;
DispatchBluetoothReply(aRunnable, v,
NS_LITERAL_STRING("GetServiceChannelError"));
return;
}
// Stop listening because currently we only support one connection at a time.
@ -1010,29 +1005,9 @@ BluetoothHfpManager::Connect(const nsAString& aDevicePath,
mHeadsetSocket = nullptr;
}
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, false);
nsString uuid;
if (aIsHandsfree) {
BluetoothUuidHelper::GetString(BluetoothServiceClass::HANDSFREE, uuid);
} else {
BluetoothUuidHelper::GetString(BluetoothServiceClass::HEADSET, uuid);
}
mRunnable = aRunnable;
mSocket =
new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
nsresult rv = bs->GetSocketViaService(aDevicePath,
uuid,
BluetoothSocketType::RFCOMM,
true,
true,
mSocket,
mRunnable);
return NS_FAILED(rv) ? false : true;
}
bool
@ -1041,7 +1016,7 @@ BluetoothHfpManager::Listen()
MOZ_ASSERT(NS_IsMainThread());
if (gInShutdown) {
MOZ_ASSERT(false, "Listen called while in shutdown!");
NS_WARNING("Listen called while in shutdown!");
return false;
}
@ -1219,6 +1194,7 @@ void
BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
uint16_t aCallState,
const nsAString& aNumber,
const bool aIsOutgoing,
bool aSend)
{
if (!IsConnected()) {
@ -1233,6 +1209,7 @@ BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
uint16_t prevCallState = mCurrentCallArray[aCallIndex].mState;
mCurrentCallArray[aCallIndex].mState = aCallState;
mCurrentCallArray[aCallIndex].mDirection = !aIsOutgoing;
// Same logic as implementation in ril_worker.js
if (aNumber.Length() && aNumber[0] == '+') {
@ -1251,7 +1228,6 @@ BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
SendCommand("+CIEV: ", CINDType::CALLHELD);
break;
case nsITelephonyProvider::CALL_STATE_INCOMING:
mCurrentCallArray[aCallIndex].mDirection = true;
if (mCurrentCallIndex) {
if (mCCWA) {
@ -1285,20 +1261,15 @@ BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
mBLDNProcessed = true;
}
mCurrentCallArray[aCallIndex].mDirection = false;
UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING, aSend);
mSocket->GetAddress(address);
OpenScoSocket(address);
ConnectSco();
break;
case nsITelephonyProvider::CALL_STATE_ALERTING:
mCurrentCallArray[aCallIndex].mDirection = false;
UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING_ALERTING, aSend);
// If there's an ongoing call when the headset is just connected, we have
// to open a sco socket here.
mSocket->GetAddress(address);
OpenScoSocket(address);
ConnectSco();
break;
case nsITelephonyProvider::CALL_STATE_CONNECTED:
mCurrentCallIndex = aCallIndex;
@ -1307,9 +1278,7 @@ BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
// Incoming call, no break
sStopSendingRingFlag = true;
mSocket->GetAddress(address);
OpenScoSocket(address);
ConnectSco();
case nsITelephonyProvider::CALL_STATE_ALERTING:
// Outgoing call
UpdateCIND(CINDType::CALL, CallState::IN_PROGRESS, aSend);
@ -1379,7 +1348,7 @@ BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
// There is no call, close Sco and clear mCurrentCallArray
if (index == callArrayLength) {
CloseScoSocket();
DisconnectSco();
ResetCallArray();
}
}
@ -1395,6 +1364,12 @@ BluetoothHfpManager::OnConnectSuccess(BluetoothSocket* aSocket)
{
MOZ_ASSERT(aSocket);
// Success to create a SCO socket
if (aSocket == mScoSocket) {
OnScoConnectSuccess();
return;
}
/**
* If the created connection is an inbound connection, close another server
* socket because currently only one SLC is allowed. After that, we need to
@ -1427,29 +1402,35 @@ BluetoothHfpManager::OnConnectSuccess(BluetoothSocket* aSocket)
nsString errorStr;
DispatchBluetoothReply(mRunnable, v, errorStr);
mRunnable.forget();
mRunnable = nullptr;
}
mFirstCKPD = true;
// Cache device path for NotifySettings() since we can't get socket address
// when a headset disconnect with us
mSocket->GetAddress(mDevicePath);
mSocket->GetAddress(mDeviceAddress);
NotifySettings();
ListenSco();
}
void
BluetoothHfpManager::OnConnectError(BluetoothSocket* aSocket)
{
// Failed to create a SCO socket
if (aSocket == mScoSocket) {
OnScoConnectError();
return;
}
// For active connection request, we need to reply the DOMRequest
if (mRunnable) {
BluetoothValue v;
nsString errorStr;
errorStr.AssignLiteral("Failed to connect with a bluetooth headset!");
DispatchBluetoothReply(mRunnable, v, errorStr);
NS_NAMED_LITERAL_STRING(replyError,
"Failed to connect with a bluetooth headset!");
DispatchBluetoothReply(mRunnable, BluetoothValue(), replyError);
mRunnable.forget();
mRunnable = nullptr;
}
mSocket = nullptr;
@ -1465,19 +1446,89 @@ BluetoothHfpManager::OnDisconnect(BluetoothSocket* aSocket)
{
MOZ_ASSERT(aSocket);
if (aSocket == mScoSocket) {
// SCO socket is closed
OnScoDisconnect();
return;
}
if (aSocket != mSocket) {
// Do nothing when a listening server socket is closed.
return;
}
mSocket = nullptr;
CloseScoSocket();
DisconnectSco();
Listen();
NotifySettings();
Reset();
}
void
BluetoothHfpManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
int aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mRunnable);
BluetoothValue v;
if (aChannel < 0) {
DispatchBluetoothReply(mRunnable, v,
NS_LITERAL_STRING("DeviceChannelRetrievalError"));
mSocket = nullptr;
Listen();
return;
}
if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) {
DispatchBluetoothReply(mRunnable, v,
NS_LITERAL_STRING("SocketConnectionError"));
mSocket = nullptr;
Listen();
return;
}
}
void
BluetoothHfpManager::OnScoConnectSuccess()
{
// For active connection request, we need to reply the DOMRequest
if (mScoRunnable) {
DispatchBluetoothReply(mScoRunnable,
BluetoothValue(true), EmptyString());
mScoRunnable = nullptr;
}
NotifyAudioManager(mDeviceAddress);
mScoSocketStatus = mScoSocket->GetConnectionStatus();
}
void
BluetoothHfpManager::OnScoConnectError()
{
if (mScoRunnable) {
NS_NAMED_LITERAL_STRING(replyError, "Failed to create SCO socket!");
DispatchBluetoothReply(mScoRunnable, BluetoothValue(), replyError);
mScoRunnable = nullptr;
}
ListenSco();
}
void
BluetoothHfpManager::OnScoDisconnect()
{
if (mScoSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
ListenSco();
NotifyAudioManager(EmptyString());
}
}
bool
BluetoothHfpManager::IsConnected()
{
@ -1494,3 +1545,86 @@ BluetoothHfpManager::GetAddress(nsAString& aDeviceAddress)
{
return mSocket->GetAddress(aDeviceAddress);
}
bool
BluetoothHfpManager::ConnectSco(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
if (gInShutdown) {
NS_WARNING("ConnecteSco called while in shutdown!");
return false;
}
if (!IsConnected()) {
NS_WARNING("BluetoothHfpManager is not connected");
return false;
}
SocketConnectionStatus status = mScoSocket->GetConnectionStatus();
if (status == SocketConnectionStatus::SOCKET_CONNECTED ||
status == SocketConnectionStatus::SOCKET_CONNECTING ||
(mScoRunnable && (mScoRunnable != aRunnable))) {
NS_WARNING("SCO connection exists or is being established");
return false;
}
mScoSocket->Disconnect();
mScoRunnable = aRunnable;
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, false);
nsresult rv = bs->GetScoSocket(mDeviceAddress, true, false, mScoSocket);
return NS_SUCCEEDED(rv);
}
bool
BluetoothHfpManager::DisconnectSco()
{
if (!mScoSocket) {
NS_WARNING("BluetoothHfpManager is not connected");
return false;
}
mScoSocket->Disconnect();
return true;
}
bool
BluetoothHfpManager::ListenSco()
{
MOZ_ASSERT(NS_IsMainThread());
if (gInShutdown) {
NS_WARNING("ListenSco called while in shutdown!");
return false;
}
if (mScoSocket->GetConnectionStatus() ==
SocketConnectionStatus::SOCKET_LISTENING) {
NS_WARNING("SCO socket has been already listening");
return false;
}
mScoSocket->Disconnect();
if (!mScoSocket->Listen(-1)) {
NS_WARNING("Can't listen on SCO socket!");
return false;
}
mScoSocketStatus = mScoSocket->GetConnectionStatus();
return true;
}
bool
BluetoothHfpManager::IsScoConnected()
{
if (mScoSocket) {
return mScoSocket->GetConnectionStatus() ==
SocketConnectionStatus::SOCKET_CONNECTED;
}
return false;
}

View File

@ -8,6 +8,7 @@
#define mozilla_dom_bluetooth_bluetoothhfpmanager_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
#include "BluetoothSocketObserver.h"
#include "BluetoothTelephonyListener.h"
#include "mozilla/ipc/UnixSocket.h"
@ -51,6 +52,7 @@ enum BluetoothCmeError {
};
class BluetoothHfpManager : public BluetoothSocketObserver
, public BluetoothProfileManagerBase
{
public:
static BluetoothHfpManager* Get();
@ -62,19 +64,28 @@ public:
virtual void OnConnectSuccess(BluetoothSocket* aSocket) MOZ_OVERRIDE;
virtual void OnConnectError(BluetoothSocket* aSocket) MOZ_OVERRIDE;
virtual void OnDisconnect(BluetoothSocket* aSocket) MOZ_OVERRIDE;
virtual void OnGetServiceChannel(const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
int aChannel) MOZ_OVERRIDE;
bool Connect(const nsAString& aDeviceObjectPath,
void Connect(const nsAString& aDeviceObjectPath,
const bool aIsHandsfree,
BluetoothReplyRunnable* aRunnable);
void Disconnect();
bool Listen();
bool ConnectSco(BluetoothReplyRunnable* aRunnable = nullptr);
bool DisconnectSco();
bool ListenSco();
/**
* @param aSend A boolean indicates whether we need to notify headset or not
*/
void HandleCallStateChanged(uint32_t aCallIndex, uint16_t aCallState,
const nsAString& aNumber, bool aSend);
const nsAString& aNumber, const bool aIsOutgoing,
bool aSend);
bool IsConnected();
bool IsScoConnected();
void GetAddress(nsAString& aDeviceAddress);
private:
@ -100,10 +111,14 @@ private:
void NotifyDialer(const nsAString& aCommand);
void NotifySettings();
void NotifyAudioManager(const nsAString& aAddress);
bool SendCommand(const char* aCommand, uint8_t aValue = 0);
bool SendLine(const char* aMessage);
void UpdateCIND(uint8_t aType, uint8_t aValue, bool aSend);
void OnScoConnectSuccess();
void OnScoConnectError();
void OnScoDisconnect();
int mCurrentVgs;
int mCurrentVgm;
@ -116,13 +131,14 @@ private:
int mNetworkSelectionMode;
bool mReceiveVgsFlag;
bool mBLDNProcessed;
nsString mDevicePath;
nsString mDeviceAddress;
nsString mMsisdn;
nsString mOperatorName;
nsTArray<Call> mCurrentCallArray;
nsAutoPtr<BluetoothTelephonyListener> mListener;
nsRefPtr<BluetoothReplyRunnable> mRunnable;
nsRefPtr<BluetoothReplyRunnable> mScoRunnable;
// If a connection has been established, mSocket will be the socket
// communicating with the remote socket. We maintain the invariant that if
@ -135,6 +151,8 @@ private:
// is called.
nsRefPtr<BluetoothSocket> mHandsfreeSocket;
nsRefPtr<BluetoothSocket> mHeadsetSocket;
nsRefPtr<BluetoothSocket> mScoSocket;
SocketConnectionStatus mScoSocketStatus;
};
END_BLUETOOTH_NAMESPACE

View File

@ -88,7 +88,6 @@ static const uint32_t kUpdateProgressBase = 50 * 1024;
static const uint32_t kPutRequestHeaderSize = 6;
StaticAutoPtr<BluetoothOppManager> sInstance;
StaticRefPtr<BluetoothOppManagerObserver> sOppObserver;
/*
* FIXME / Bug 806749
@ -253,15 +252,25 @@ BluetoothOppManager::Get()
return sInstance;
}
bool
void
BluetoothOppManager::Connect(const nsAString& aDeviceObjectPath,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
if (mSocket) {
NS_WARNING("BluetoothOppManager has been already connected");
return false;
NS_ENSURE_FALSE_VOID(mSocket);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
nsString uuid;
BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
if (NS_FAILED(bs->GetServiceChannel(aDeviceObjectPath, uuid, this))) {
BluetoothValue v;
DispatchBluetoothReply(aRunnable, v,
NS_LITERAL_STRING("GetServiceChannelError"));
return;
}
// Stop listening because currently we only support one connection at a time.
@ -275,25 +284,9 @@ BluetoothOppManager::Connect(const nsAString& aDeviceObjectPath,
mL2capSocket = nullptr;
}
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, false);
nsString uuid;
BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
mRunnable = aRunnable;
mSocket =
new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
nsresult rv = bs->GetSocketViaService(aDeviceObjectPath,
uuid,
BluetoothSocketType::RFCOMM,
true,
true,
mSocket,
mRunnable);
return NS_FAILED(rv) ? false : true;
}
void
@ -1436,13 +1429,9 @@ void
BluetoothOppManager::OnConnectError(BluetoothSocket* aSocket)
{
if (mRunnable) {
nsString errorStr;
errorStr.AssignLiteral("Failed to connect with a bluetooth opp manager!");
BluetoothReply* reply = new BluetoothReply(BluetoothReplyError(errorStr));
mRunnable->SetReply(reply);
if (NS_FAILED(NS_DispatchToMainThread(mRunnable))) {
NS_WARNING("Failed to dispatch to main thread!");
}
BluetoothValue v;
DispatchBluetoothReply(mRunnable, v,
NS_LITERAL_STRING("OnConnectError:no runnable"));
mRunnable.forget();
}
@ -1484,3 +1473,31 @@ BluetoothOppManager::OnDisconnect(BluetoothSocket* aSocket)
mSocket = nullptr;
Listen();
}
void
BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
int aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mRunnable);
BluetoothValue v;
if (aChannel < 0) {
DispatchBluetoothReply(mRunnable, v,
NS_LITERAL_STRING("DeviceChannelRetrievalError"));
mSocket = nullptr;
Listen();
return;
}
if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) {
DispatchBluetoothReply(mRunnable, v,
NS_LITERAL_STRING("SocketConnectionError"));
mSocket = nullptr;
Listen();
return;
}
}

View File

@ -8,6 +8,7 @@
#define mozilla_dom_bluetooth_bluetoothoppmanager_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
#include "BluetoothSocketObserver.h"
#include "DeviceStorage.h"
#include "mozilla/dom/ipc/Blob.h"
@ -24,6 +25,7 @@ class BluetoothSocket;
class ObexHeaderSet;
class BluetoothOppManager : public BluetoothSocketObserver
, public BluetoothProfileManagerBase
{
public:
/*
@ -50,7 +52,7 @@ public:
* either call Disconnect() to close RFCOMM connection or start another
* file-sending thread via calling SendFile() again.
*/
bool Connect(const nsAString& aDeviceObjectPath,
void Connect(const nsAString& aDeviceObjectPath,
BluetoothReplyRunnable* aRunnable);
void Disconnect();
bool Listen();
@ -83,9 +85,9 @@ public:
virtual void OnConnectSuccess(BluetoothSocket* aSocket) MOZ_OVERRIDE;
virtual void OnConnectError(BluetoothSocket* aSocket) MOZ_OVERRIDE;
virtual void OnDisconnect(BluetoothSocket* aSocket) MOZ_OVERRIDE;
void OnConnectSuccess() MOZ_OVERRIDE;
void OnConnectError() MOZ_OVERRIDE;
void OnDisconnect() MOZ_OVERRIDE;
virtual void OnGetServiceChannel(const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
int aChannel) MOZ_OVERRIDE;
private:
BluetoothOppManager();

View File

@ -0,0 +1,24 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef mozilla_dom_bluetooth_bluetoothprofilemanagerbase_h__
#define mozilla_dom_bluetooth_bluetoothprofilemanagerbase_h__
#include "BluetoothCommon.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothProfileManagerBase
{
public:
virtual void OnGetServiceChannel(const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
int aChannel) = 0;
};
END_BLUETOOTH_NAMESPACE
#endif //#ifndef mozilla_dom_bluetooth_bluetoothprofilemanagerbase_h__

View File

@ -1,293 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "BluetoothScoManager.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "nsContentUtils.h"
#include "nsIAudioManager.h"
#include "nsIObserverService.h"
#define BLUETOOTH_SCO_STATUS_CHANGED "bluetooth-sco-status-changed"
using namespace mozilla;
using namespace mozilla::ipc;
USING_BLUETOOTH_NAMESPACE
class mozilla::dom::bluetooth::BluetoothScoManagerObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
BluetoothScoManagerObserver()
{
}
bool Init()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
MOZ_ASSERT(obs);
if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
NS_WARNING("Failed to add shutdown observer!");
return false;
}
return true;
}
bool Shutdown()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs ||
(NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)))) {
NS_WARNING("Can't unregister observers!");
return false;
}
return true;
}
~BluetoothScoManagerObserver()
{
Shutdown();
}
};
void
BluetoothScoManager::NotifyAudioManager(const nsAString& aAddress)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs =
do_GetService("@mozilla.org/observer-service;1");
NS_ENSURE_TRUE_VOID(obs);
const PRUnichar* addr =
aAddress.IsEmpty() ? nullptr : aAddress.BeginReading();
if (NS_FAILED(obs->NotifyObservers(nullptr,
BLUETOOTH_SCO_STATUS_CHANGED,
addr))) {
NS_WARNING("Failed to notify bluetooth-sco-status-changed observsers!");
return;
}
}
NS_IMPL_ISUPPORTS1(BluetoothScoManagerObserver, nsIObserver)
namespace {
StaticAutoPtr<BluetoothScoManager> gBluetoothScoManager;
StaticRefPtr<BluetoothScoManagerObserver> sScoObserver;
bool gInShutdown = false;
} // anonymous namespace
NS_IMETHODIMP
BluetoothScoManagerObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
MOZ_ASSERT(gBluetoothScoManager);
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
return gBluetoothScoManager->HandleShutdown();
}
MOZ_ASSERT(false, "BluetoothScoManager got unexpected topic!");
return NS_ERROR_UNEXPECTED;
}
BluetoothScoManager::BluetoothScoManager()
{
}
bool
BluetoothScoManager::Init()
{
mSocket = new BluetoothSocket(this,
BluetoothSocketType::SCO,
true,
false);
mPrevSocketStatus = mSocket->GetConnectionStatus();
sScoObserver = new BluetoothScoManagerObserver();
if (!sScoObserver->Init()) {
NS_WARNING("Cannot set up SCO observers!");
}
return true;
}
BluetoothScoManager::~BluetoothScoManager()
{
Cleanup();
}
void
BluetoothScoManager::Cleanup()
{
sScoObserver->Shutdown();
sScoObserver = nullptr;
}
//static
BluetoothScoManager*
BluetoothScoManager::Get()
{
MOZ_ASSERT(NS_IsMainThread());
// If we already exist, exit early
if (gBluetoothScoManager) {
return gBluetoothScoManager;
}
// If we're in shutdown, don't create a new instance
if (gInShutdown) {
NS_WARNING("BluetoothScoManager can't be created during shutdown");
return nullptr;
}
// Create new instance, register, return
BluetoothScoManager* manager = new BluetoothScoManager();
NS_ENSURE_TRUE(manager->Init(), nullptr);
gBluetoothScoManager = manager;
return gBluetoothScoManager;
}
// Virtual function of class SocketConsumer
void
BluetoothScoManager::ReceiveSocketData(BluetoothSocket* aSocket,
nsAutoPtr<UnixSocketRawData>& aMessage)
{
// SCO socket do nothing here
MOZ_NOT_REACHED("This should never be called!");
}
nsresult
BluetoothScoManager::HandleShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
gInShutdown = true;
mSocket->Disconnect();
gBluetoothScoManager = nullptr;
return NS_OK;
}
bool
BluetoothScoManager::Connect(const nsAString& aDeviceAddress)
{
MOZ_ASSERT(NS_IsMainThread());
if (gInShutdown) {
MOZ_ASSERT(false, "Connect called while in shutdown!");
return false;
}
SocketConnectionStatus status = mSocket->GetConnectionStatus();
if (status == SocketConnectionStatus::SOCKET_CONNECTED ||
status == SocketConnectionStatus::SOCKET_CONNECTING) {
NS_WARNING("SCO connection exists or is being established");
return false;
}
mSocket->Disconnect();
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, false);
nsresult rv = bs->GetScoSocket(aDeviceAddress,
true,
false,
mSocket);
return NS_FAILED(rv) ? false : true;
}
bool
BluetoothScoManager::Listen()
{
MOZ_ASSERT(NS_IsMainThread());
if (gInShutdown) {
MOZ_ASSERT(false, "Listen called while in shutdown!");
return false;
}
if (mSocket->GetConnectionStatus() ==
SocketConnectionStatus::SOCKET_LISTENING) {
NS_WARNING("BluetoothScoManager has been already listening");
return true;
}
mSocket->Disconnect();
if (!mSocket->Listen(-1)) {
NS_WARNING("[SCO] Can't listen on socket!");
return false;
}
mPrevSocketStatus = mSocket->GetConnectionStatus();
return true;
}
void
BluetoothScoManager::Disconnect()
{
mSocket->Disconnect();
}
void
BluetoothScoManager::OnConnectSuccess(BluetoothSocket* aSocket)
{
MOZ_ASSERT(aSocket == mSocket);
nsString address;
mSocket->GetAddress(address);
NotifyAudioManager(address);
mPrevSocketStatus = mSocket->GetConnectionStatus();
}
void
BluetoothScoManager::OnConnectError(BluetoothSocket* aSocket)
{
MOZ_ASSERT(aSocket == mSocket);
mSocket->Disconnect();
mPrevSocketStatus = mSocket->GetConnectionStatus();
Listen();
}
void
BluetoothScoManager::OnDisconnect(BluetoothSocket* aSocket)
{
MOZ_ASSERT(aSocket == mSocket);
if (mPrevSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
Listen();
nsString address = NS_LITERAL_STRING("");
NotifyAudioManager(address);
}
}
bool
BluetoothScoManager::IsConnected()
{
if (mSocket) {
return mSocket->GetConnectionStatus() ==
SocketConnectionStatus::SOCKET_CONNECTED;
}
return false;
}

View File

@ -1,52 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef mozilla_dom_bluetooth_bluetoothscomanager_h__
#define mozilla_dom_bluetooth_bluetoothscomanager_h__
#include "BluetoothCommon.h"
#include "BluetoothSocketObserver.h"
#include "nsIObserver.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothReplyRunnable;
class BluetoothScoManagerObserver;
class BluetoothSocket;
class BluetoothScoManager : public BluetoothSocketObserver
{
public:
static BluetoothScoManager* Get();
~BluetoothScoManager();
virtual void ReceiveSocketData(
BluetoothSocket* aSocket,
nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage) MOZ_OVERRIDE;
virtual void OnConnectSuccess(BluetoothSocket* aSocket) MOZ_OVERRIDE;
virtual void OnConnectError(BluetoothSocket* aSocket) MOZ_OVERRIDE;
virtual void OnDisconnect(BluetoothSocket* aSocket) MOZ_OVERRIDE;
bool Connect(const nsAString& aDeviceObjectPath);
void Disconnect();
bool Listen();
bool IsConnected();
private:
friend class BluetoothScoManagerObserver;
BluetoothScoManager();
bool Init();
void Cleanup();
nsresult HandleShutdown();
void NotifyAudioManager(const nsAString& aAddress);
SocketConnectionStatus mPrevSocketStatus;
nsRefPtr<BluetoothSocket> mSocket;
};
END_BLUETOOTH_NAMESPACE
#endif

View File

@ -8,6 +8,7 @@
#define mozilla_dom_bluetooth_bluetootheventservice_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
#include "mozilla/dom/ipc/Blob.h"
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
@ -181,7 +182,7 @@ public:
const BluetoothNamedValue& aValue,
BluetoothReplyRunnable* aRunnable) = 0;
/**
/**
* Get the path of a device
*
* @param aAdapterPath Path to the Adapter that's communicating with the device
@ -210,14 +211,20 @@ public:
bool aEncrypt,
mozilla::ipc::UnixSocketConsumer* aConsumer) = 0;
/**
* Get corresponding service channel of specific service on remote device.
* It's usually the very first step of establishing an outbound connection.
*
* @param aObjectPath Object path of remote device
* @param aServiceUuid UUID of the target service
* @param aManager Instance which has callback function OnGetServiceChannel()
*
* @return NS_OK if the task begins, NS_ERROR_FAILURE otherwise
*/
virtual nsresult
GetSocketViaService(const nsAString& aObjectPath,
const nsAString& aService,
BluetoothSocketType aType,
bool aAuth,
bool aEncrypt,
mozilla::ipc::UnixSocketConsumer* aSocketConsumer,
BluetoothReplyRunnable* aRunnable) = 0;
GetServiceChannel(const nsAString& aObjectPath,
const nsAString& aServiceUuid,
BluetoothProfileManagerBase* aManager) = 0;
virtual bool
SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode,
@ -263,6 +270,15 @@ public:
ConfirmReceivingFile(const nsAString& aDeviceAddress, bool aConfirm,
BluetoothReplyRunnable* aRunnable) = 0;
virtual void
ConnectSco(BluetoothReplyRunnable* aRunnable) = 0;
virtual void
DisconnectSco(BluetoothReplyRunnable* aRunnable) = 0;
virtual void
IsScoConnected(BluetoothReplyRunnable* aRunnable) = 0;
bool
IsEnabled() const
{

View File

@ -30,10 +30,12 @@ NS_IMETHODIMP
TelephonyListener::CallStateChanged(uint32_t aCallIndex,
uint16_t aCallState,
const nsAString& aNumber,
bool aIsActive)
bool aIsActive,
bool aIsOutgoing)
{
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
hfp->HandleCallStateChanged(aCallIndex, aCallState, aNumber, true);
hfp->HandleCallStateChanged(aCallIndex, aCallState, aNumber,
aIsOutgoing, true);
return NS_OK;
}
@ -43,10 +45,12 @@ TelephonyListener::EnumerateCallState(uint32_t aCallIndex,
uint16_t aCallState,
const nsAString_internal& aNumber,
bool aIsActive,
bool aIsOutgoing,
bool* aResult)
{
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
hfp->HandleCallStateChanged(aCallIndex, aCallState, aNumber, false);
hfp->HandleCallStateChanged(aCallIndex, aCallState, aNumber,
aIsOutgoing, false);
*aResult = true;
return NS_OK;
}

View File

@ -44,7 +44,6 @@ CPPSRCS += \
BluetoothHfpManager.cpp \
BluetoothOppManager.cpp \
ObexBase.cpp \
BluetoothScoManager.cpp \
BluetoothUuid.cpp \
BluetoothSocket.cpp \
$(NULL)

View File

@ -225,6 +225,12 @@ BluetoothParent::RecvPBluetoothRequestConstructor(
return actor->DoRequest(aRequest.get_ConfirmReceivingFileRequest());
case Request::TDenyReceivingFileRequest:
return actor->DoRequest(aRequest.get_DenyReceivingFileRequest());
case Request::TConnectScoRequest:
return actor->DoRequest(aRequest.get_ConnectScoRequest());
case Request::TDisconnectScoRequest:
return actor->DoRequest(aRequest.get_DisconnectScoRequest());
case Request::TIsScoConnectedRequest:
return actor->DoRequest(aRequest.get_IsScoConnectedRequest());
default:
MOZ_NOT_REACHED("Unknown type!");
return false;
@ -570,3 +576,33 @@ BluetoothRequestParent::DoRequest(const DenyReceivingFileRequest& aRequest)
mReplyRunnable.get());
return true;
}
bool
BluetoothRequestParent::DoRequest(const ConnectScoRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TConnectScoRequest);
mService->ConnectSco(mReplyRunnable.get());
return true;
}
bool
BluetoothRequestParent::DoRequest(const DisconnectScoRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TDisconnectScoRequest);
mService->DisconnectSco(mReplyRunnable.get());
return true;
}
bool
BluetoothRequestParent::DoRequest(const IsScoConnectedRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TIsScoConnectedRequest);
mService->IsScoConnected(mReplyRunnable.get());
return true;
}

View File

@ -185,6 +185,15 @@ protected:
bool
DoRequest(const DenyReceivingFileRequest& aRequest);
bool
DoRequest(const ConnectScoRequest& aRequest);
bool
DoRequest(const DisconnectScoRequest& aRequest);
bool
DoRequest(const IsScoConnectedRequest& aRequest);
};
END_BLUETOOTH_NAMESPACE

View File

@ -207,14 +207,9 @@ BluetoothServiceChildProcess::GetScoSocket(
}
nsresult
BluetoothServiceChildProcess::GetSocketViaService(
const nsAString& aObjectPath,
const nsAString& aService,
BluetoothSocketType aType,
bool aAuth,
bool aEncrypt,
mozilla::ipc::UnixSocketConsumer* aConsumer,
BluetoothReplyRunnable* aRunnable)
BluetoothServiceChildProcess::GetServiceChannel(const nsAString& aObjectPath,
const nsAString& aServiceUuid,
BluetoothProfileManagerBase* aManager)
{
MOZ_NOT_REACHED("This should never be called!");
return NS_ERROR_FAILURE;
@ -336,6 +331,24 @@ BluetoothServiceChildProcess::ConfirmReceivingFile(
DenyReceivingFileRequest(nsString(aDeviceAddress)));
}
void
BluetoothServiceChildProcess::ConnectSco(BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable, ConnectScoRequest());
}
void
BluetoothServiceChildProcess::DisconnectSco(BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable, DisconnectScoRequest());
}
void
BluetoothServiceChildProcess::IsScoConnected(BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable, IsScoConnectedRequest());
}
nsresult
BluetoothServiceChildProcess::HandleStartup()
{

View File

@ -87,13 +87,9 @@ public:
mozilla::ipc::UnixSocketConsumer* aConsumer) MOZ_OVERRIDE;
virtual nsresult
GetSocketViaService(const nsAString& aObjectPath,
const nsAString& aService,
BluetoothSocketType aType,
bool aAuth,
bool aEncrypt,
mozilla::ipc::UnixSocketConsumer* aConsumer,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
GetServiceChannel(const nsAString& aObjectPath,
const nsAString& aServiceUuid,
BluetoothProfileManagerBase* aManager) MOZ_OVERRIDE;
virtual bool
SetPinCodeInternal(const nsAString& aDeviceAddress,
@ -142,6 +138,16 @@ public:
ConfirmReceivingFile(const nsAString& aDeviceAddress,
bool aConfirm,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual void
ConnectSco(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual void
DisconnectSco(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual void
IsScoConnected(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
protected:
BluetoothServiceChildProcess();
virtual ~BluetoothServiceChildProcess();

View File

@ -129,6 +129,18 @@ struct DenyReceivingFileRequest
nsString devicePath;
};
struct ConnectScoRequest
{
};
struct DisconnectScoRequest
{
};
struct IsScoConnectedRequest
{
};
union Request
{
DefaultAdapterPathRequest;
@ -152,6 +164,9 @@ union Request
StopSendingFileRequest;
ConfirmReceivingFileRequest;
DenyReceivingFileRequest;
ConnectScoRequest;
DisconnectScoRequest;
IsScoConnectedRequest;
};
protocol PBluetooth

View File

@ -21,7 +21,6 @@
#include "BluetoothHfpManager.h"
#include "BluetoothOppManager.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothScoManager.h"
#include "BluetoothUnixSocketConnector.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
@ -838,12 +837,6 @@ public:
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!");
@ -854,31 +847,6 @@ public:
}
};
class ShutdownProfileManagersRunnable : public nsRunnable
{
public:
NS_IMETHOD
Run()
{
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
if (hfp) {
hfp->Disconnect();
}
BluetoothOppManager* opp = BluetoothOppManager::Get();
if (opp) {
opp->Disconnect();
}
BluetoothScoManager* sco = BluetoothScoManager::Get();
if (sco) {
sco->Disconnect();
}
return NS_OK;
}
};
class PrepareAdapterRunnable : public nsRunnable
{
public:
@ -1977,6 +1945,27 @@ public:
v.get_ArrayOfBluetoothNamedValue().AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Path"), objectPath)
);
const InfallibleTArray<BluetoothNamedValue>& deviceProperties =
v.get_ArrayOfBluetoothNamedValue();
uint32_t length = deviceProperties.Length();
// It is possible that property Icon missed due to CoD of major
// class is TOY but service class is "Audio", we need to assign
// Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I.
// As HFP specification defined that
// service class is "Audio" can be considered as HFP AG.
if (!ContainsIcon(deviceProperties)) {
for (uint32_t p = 0; p < length; ++p) {
if (deviceProperties[p].name().EqualsLiteral("Class")) {
if (HasAudioService(deviceProperties[p].value().get_uint32_t())) {
v.get_ArrayOfBluetoothNamedValue()
.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Icon"),
NS_LITERAL_STRING("audio-card")));
}
break;
}
}
}
if (mFilterFunc(v)) {
values.get_ArrayOfBluetoothNamedValue().AppendElement(
@ -2528,37 +2517,23 @@ BluetoothDBusService::Connect(const nsAString& aDeviceAddress,
const uint16_t aProfileId,
BluetoothReplyRunnable* aRunnable)
{
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
MOZ_ASSERT(NS_IsMainThread());
BluetoothValue v;
nsAutoString errorStr;
if (aProfileId == BluetoothServiceClass::HANDSFREE) {
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
if (!hfp->Connect(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress),
true, aRunnable)) {
errorStr.AssignLiteral("BluetoothHfpManager has connected/is connecting \
to a headset!");
DispatchBluetoothReply(aRunnable, v, errorStr);
}
hfp->Connect(
GetObjectPathFromAddress(sAdapterPath, aDeviceAddress), true, aRunnable);
} else if (aProfileId == BluetoothServiceClass::HEADSET) {
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
if (!hfp->Connect(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress),
false, aRunnable)) {
errorStr.AssignLiteral("BluetoothHfpManager has connected/is connecting \
to a headset!");
DispatchBluetoothReply(aRunnable, v, errorStr);
}
hfp->Connect(
GetObjectPathFromAddress(sAdapterPath, aDeviceAddress), false, aRunnable);
} else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
BluetoothOppManager* opp = BluetoothOppManager::Get();
if (!opp->Connect(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress),
aRunnable)) {
errorStr.AssignLiteral("BluetoothOppManager has been connected/is \
connecting!");
DispatchBluetoothReply(aRunnable, v, errorStr);
}
opp->Connect(
GetObjectPathFromAddress(sAdapterPath, aDeviceAddress), aRunnable);
} else {
errorStr.AssignLiteral("Unknown profile");
DispatchBluetoothReply(aRunnable, v, errorStr);
BluetoothValue v;
DispatchBluetoothReply(aRunnable, v, NS_LITERAL_STRING("UnknownProfileError"));
}
}
@ -2654,24 +2629,54 @@ private:
int mChannel;
};
class GetDeviceChannelForConnectRunnable : public nsRunnable
class OnGetServiceChannelRunnable : 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)
OnGetServiceChannelRunnable(const nsAString& aObjectPath,
const nsAString& aServiceUuid,
int aChannel,
BluetoothProfileManagerBase* aManager)
: mServiceUuid(aServiceUuid),
mChannel(aChannel),
mManager(aManager)
{
MOZ_ASSERT(!aObjectPath.IsEmpty());
MOZ_ASSERT(!aServiceUuid.IsEmpty());
MOZ_ASSERT(aManager);
mDeviceAddress = GetAddressFromObjectPath(aObjectPath);
}
nsresult
Run()
{
MOZ_ASSERT(NS_IsMainThread());
mManager->OnGetServiceChannel(mDeviceAddress, mServiceUuid, mChannel);
return NS_OK;
}
private:
nsString mDeviceAddress;
nsString mServiceUuid;
int mChannel;
BluetoothProfileManagerBase* mManager;
};
class GetServiceChannelRunnable : public nsRunnable
{
public:
GetServiceChannelRunnable(const nsAString& aObjectPath,
const nsAString& aServiceUuid,
BluetoothProfileManagerBase* aManager)
: mObjectPath(aObjectPath),
mServiceUuid(aServiceUuid),
mManager(aManager)
{
MOZ_ASSERT(!aObjectPath.IsEmpty());
MOZ_ASSERT(!aServiceUuid.IsEmpty());
MOZ_ASSERT(aManager);
}
nsresult
@ -2679,23 +2684,12 @@ public:
{
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!");
int channel = GetDeviceServiceChannel(mObjectPath, mServiceUuid, 0x0004);
nsRefPtr<nsRunnable> r(new OnGetServiceChannelRunnable(mObjectPath,
mServiceUuid,
channel,
mManager));
if (NS_FAILED(NS_DispatchToMainThread(r))) {
return NS_ERROR_FAILURE;
}
@ -2703,48 +2697,27 @@ public:
}
private:
nsRefPtr<BluetoothReplyRunnable> mRunnable;
nsRefPtr<UnixSocketConsumer> mConsumer;
nsString mObjectPath;
nsString mServiceUUID;
BluetoothSocketType mType;
bool mAuth;
bool mEncrypt;
nsString mServiceUuid;
BluetoothProfileManagerBase* mManager;
};
nsresult
BluetoothDBusService::GetSocketViaService(
const nsAString& aObjectPath,
const nsAString& aService,
BluetoothSocketType aType,
bool aAuth,
bool aEncrypt,
mozilla::ipc::UnixSocketConsumer* aConsumer,
BluetoothReplyRunnable* aRunnable)
BluetoothDBusService::GetServiceChannel(const nsAString& aObjectPath,
const nsAString& aServiceUuid,
BluetoothProfileManagerBase* aManager)
{
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
if (!IsReady()) {
BluetoothValue v;
nsAutoString errorStr;
errorStr.AssignLiteral("Bluetooth service is not ready yet!");
DispatchBluetoothReply(aRunnable, v, errorStr);
return NS_OK;
}
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBluetoothCommandThread);
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
nsRefPtr<nsRunnable> r(new GetServiceChannelRunnable(aObjectPath,
aServiceUuid,
aManager));
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!");
if (NS_FAILED(mBluetoothCommandThread->Dispatch(r, NS_DISPATCH_NORMAL))) {
return NS_ERROR_FAILURE;
}
runnable.forget();
return NS_OK;
}
@ -2840,3 +2813,46 @@ BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress,
DispatchBluetoothReply(aRunnable, v, errorStr);
}
void
BluetoothDBusService::ConnectSco(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
NS_ENSURE_TRUE_VOID(hfp);
if(!hfp->ConnectSco(aRunnable)) {
NS_NAMED_LITERAL_STRING(replyError,
"SCO socket exists or HFP is not connected");
DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError);
}
}
void
BluetoothDBusService::DisconnectSco(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
NS_ENSURE_TRUE_VOID(hfp);
if (hfp->DisconnectSco()) {
DispatchBluetoothReply(aRunnable,
BluetoothValue(true), NS_LITERAL_STRING(""));
return;
}
NS_NAMED_LITERAL_STRING(replyError,
"SCO socket doesn't exist or HFP is not connected");
DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError);
}
void
BluetoothDBusService::IsScoConnected(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
NS_ENSURE_TRUE_VOID(hfp);
DispatchBluetoothReply(aRunnable,
hfp->IsScoConnected(), EmptyString());
}

View File

@ -85,13 +85,9 @@ public:
mozilla::ipc::UnixSocketConsumer* aConsumer);
virtual nsresult
GetSocketViaService(const nsAString& aObjectPath,
const nsAString& aService,
BluetoothSocketType aType,
bool aAuth,
bool aEncrypt,
mozilla::ipc::UnixSocketConsumer* aConsumer,
BluetoothReplyRunnable* aRunnable);
GetServiceChannel(const nsAString& aObjectPath,
const nsAString& aServiceUuid,
BluetoothProfileManagerBase* aManager);
virtual nsresult
CreatePairedDeviceInternal(const nsAString& aDeviceAddress,
@ -146,6 +142,15 @@ public:
ConfirmReceivingFile(const nsAString& aDeviceAddress, bool aConfirm,
BluetoothReplyRunnable* aRunnable);
virtual void
ConnectSco(BluetoothReplyRunnable* aRunnable);
virtual void
DisconnectSco(BluetoothReplyRunnable* aRunnable);
virtual void
IsScoConnected(BluetoothReplyRunnable* aRunnable);
private:
nsresult SendGetPropertyMessage(const nsAString& aPath,
const char* aInterface,

View File

@ -10,7 +10,7 @@ interface nsIDOMDOMRequest;
interface nsIDOMBlob;
interface nsIDOMBluetoothDevice;
[scriptable, builtinclass, uuid(88a5638f-f55a-4d67-8437-392d0a9a87c7)]
[scriptable, builtinclass, uuid(7058d214-3575-4913-99ad-0980296f617a)]
interface nsIDOMBluetoothAdapter : nsIDOMEventTarget
{
readonly attribute DOMString address;
@ -58,6 +58,11 @@ interface nsIDOMBluetoothAdapter : nsIDOMEventTarget
nsIDOMDOMRequest stopSendingFile(in DOMString aDeviceAddress);
nsIDOMDOMRequest confirmReceivingFile(in DOMString aDeviceAddress, in bool aConfirmation);
// Connect/Disconnect SCO (audio) connection
nsIDOMDOMRequest connectSco();
nsIDOMDOMRequest disconnectSco();
nsIDOMDOMRequest isScoConnected();
// Fired when discoverying and any device is discovered.
[implicit_jscontext] attribute jsval ondevicefound;
};

View File

@ -143,9 +143,15 @@ function expectOnlyOneProcessCreated() {
}
// Returns a promise which is resolved or rejected the next time the process
// childID changes its priority. We resolve if the priority matches
// expectedPriority, and we reject otherwise.
function expectPriorityChange(childID, expectedPriority) {
// childID changes its priority. We resolve if the (priority, CPU priority)
// tuple matches (expectedPriority, expectedCPUPriority) and we reject
// otherwise.
//
// expectedCPUPriority is an optional argument; if it's not specified, we
// resolve if priority matches expectedPriority.
function expectPriorityChange(childID, expectedPriority,
/* optional */ expectedCPUPriority) {
var deferred = Promise.defer();
var observed = false;
@ -156,7 +162,7 @@ function expectPriorityChange(childID, expectedPriority) {
return;
}
[id, priority] = data.split(":");
[id, priority, cpuPriority] = data.split(":");
if (id != childID) {
return;
}
@ -169,10 +175,17 @@ function expectPriorityChange(childID, expectedPriority) {
'Expected priority of childID ' + childID +
' to change to ' + expectedPriority);
if (priority == expectedPriority) {
deferred.resolve(priority);
if (expectedCPUPriority) {
is(cpuPriority, expectedCPUPriority,
'Expected CPU priority of childID ' + childID +
' to change to ' + expectedCPUPriority);
}
if (priority == expectedPriority &&
(!expectedCPUPriority || expectedCPUPriority == cpuPriority)) {
deferred.resolve();
} else {
deferred.reject(priority);
deferred.reject();
}
}
);

View File

@ -0,0 +1,15 @@
A word to the wise:
You must ensure that if your test finishes successfully, no processes have
priority FOREGROUND_HIGH.
If you don't do this, expect to see tests randomly fail with mysterious
FOREGROUND --> FOREGROUND priority transitions.
What's happening in this case is that your FOREGROUND_HIGH process lives until
the beginning of the next test. This causes the process started by the next
test to have low CPU priority. Then the FOREGROUND_HIGH process dies, because
its iframe gets GC'ed, and we transition the new test's process from low CPU
priority to regular CPU priority.
Ouch.

View File

@ -28,6 +28,8 @@ MOCHITEST_FILES = \
test_Visibility.html \
test_HighPriority.html \
file_HighPriority.html \
test_HighPriorityDowngrade.html \
test_HighPriorityDowngrade2.html \
test_Background.html \
test_Audio.html \
file_Audio.html \

View File

@ -0,0 +1,69 @@
<!DOCTYPE HTML>
<html>
<!--
Test that high-priority processes downgrade the CPU priority of regular
processes.
-->
<head>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7">
"use strict";
SimpleTest.waitForExplicitFinish();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
SpecialPowers.addPermission("embed-apps", true, document);
var iframe = null;
var childID = null;
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
iframe.src = browserElementTestHelpers.emptyPage1;
var highPriorityIframe = null;
var childID = null;
expectProcessCreated().then(function(chid) {
childID = chid;
return expectPriorityChange(childID, 'FOREGROUND', 'CPU_NORMAL');
}).then(function() {
// Create a new, high-priority iframe.
highPriorityIframe = document.createElement('iframe');
highPriorityIframe.setAttribute('mozbrowser', true);
highPriorityIframe.setAttribute('expecting-system-message', true);
highPriorityIframe.setAttribute('mozapptype', 'critical');
highPriorityIframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
highPriorityIframe.src = browserElementTestHelpers.emptyPage2;
var p = expectPriorityChange(childID, 'FOREGROUND', 'CPU_LOW');
document.body.appendChild(highPriorityIframe);
return p;
}).then(function() {
return expectPriorityChange(childID, 'FOREGROUND', 'CPU_NORMAL');
}).then(SimpleTest.finish);
document.body.appendChild(iframe);
}
addEventListener('testready', function() {
// Cause the CPU wake lock taken on behalf of the high-priority process to
// time out after 1s.
SpecialPowers.pushPrefEnv(
{set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1]]},
runTest);
});
</script>
</body>
</html>

View File

@ -0,0 +1,77 @@
<!DOCTYPE HTML>
<html>
<!--
Test that high-priority processes downgrade the CPU priority of regular
processes.
This is just like test_HighPriorityDowngrade, except instead of waiting for the
high-priority process's wake lock to expire, we kill the process by removing
its iframe from the DOM.
-->
<head>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7">
"use strict";
SimpleTest.waitForExplicitFinish();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
SpecialPowers.addPermission("embed-apps", true, document);
function runTest() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
iframe.src = browserElementTestHelpers.emptyPage1;
var highPriorityIframe = null;
var childID = null;
expectProcessCreated().then(function(chid) {
childID = chid;
return expectPriorityChange(childID, 'FOREGROUND', 'CPU_NORMAL');
}).then(function() {
// Create a new, high-priority iframe.
highPriorityIframe = document.createElement('iframe');
highPriorityIframe.setAttribute('mozbrowser', true);
highPriorityIframe.setAttribute('expecting-system-message', true);
highPriorityIframe.setAttribute('mozapptype', 'critical');
highPriorityIframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
highPriorityIframe.src = browserElementTestHelpers.emptyPage2;
var p = Promise.all(
[expectPriorityChange(childID, 'FOREGROUND', 'CPU_LOW'),
expectMozbrowserEvent(highPriorityIframe, 'loadend')]
);
document.body.appendChild(highPriorityIframe);
return p;
}).then(function() {
// Killing the high-priority iframe should cause our CPU priority to go back
// up to regular.
var p = expectPriorityChange(childID, 'FOREGROUND', 'CPU_NORMAL');
document.body.removeChild(highPriorityIframe);
return p;
}).then(SimpleTest.finish);
document.body.appendChild(iframe);
}
addEventListener('testready', function() {
// Cause the CPU wake lock taken on behalf of the high-priority process never
// to time out during this test.
SpecialPowers.pushPrefEnv(
{set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1000]]},
runTest);
});
</script>
</body>
</html>

View File

@ -1046,6 +1046,7 @@ nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType,
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->AddObserver(this, "file-watcher-update", false);
obs->AddObserver(this, "disk-space-watcher", false);
mRootDirectory = f;
mStorageType = aType;
}
@ -2169,6 +2170,7 @@ nsDOMDeviceStorage::Shutdown()
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->RemoveObserver(this, "file-watcher-update");
obs->RemoveObserver(this, "disk-space-watcher");
}
void
@ -2622,6 +2624,18 @@ nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRU
DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
return NS_OK;
} else if (!strcmp(aTopic, "disk-space-watcher")) {
// 'disk-space-watcher' notifications are sent when there is a modification
// of a file in a specific location while a low device storage situation
// exists or after recovery of a low storage situation. For Firefox OS,
// these notifications are specific for apps storage.
nsRefPtr<DeviceStorageFile> file = new DeviceStorageFile(mStorageType);
if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "full")) {
Notify("low-disk-space", file);
} else if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "free")) {
Notify("available-disk-space", file);
}
return NS_OK;
}
#ifdef MOZ_WIDGET_GONK

View File

@ -1362,7 +1362,7 @@ ContentParent::RecvFirstIdle()
// use as an indicator that it's a good time to prelaunch another process.
// If we prelaunch any sooner than this, then we'll be competing with the
// child process and slowing it down.
PreallocatedProcessManager::AllocateOnIdle();
PreallocatedProcessManager::AllocateAfterDelay();
return true;
}

View File

@ -143,6 +143,21 @@ public:
void FireTestOnlyObserverNotification(const char* aTopic,
const nsACString& aData = EmptyCString());
/**
* Does some process, other than the one handled by aParticularManager, have
* priority FOREGROUND_HIGH?
*/
bool OtherProcessHasHighPriority(
ParticularProcessPriorityManager* aParticularManager);
/**
* This must be called by a ParticularProcessPriorityManager when it changes
* its priority.
*/
void NotifyProcessPriorityChanged(
ParticularProcessPriorityManager* aParticularManager,
hal::ProcessPriority aOldPriority);
private:
static bool sPrefListenersRegistered;
static bool sInitialized;
@ -164,6 +179,8 @@ private:
nsDataHashtable<nsUint64HashKey, nsRefPtr<ParticularProcessPriorityManager> >
mParticularManagers;
nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs;
};
/**
@ -202,6 +219,7 @@ class ParticularProcessPriorityManager MOZ_FINAL
: public WakeLockObserver
, public nsIObserver
, public nsITimerCallback
, public nsSupportsWeakReference
{
public:
ParticularProcessPriorityManager(ContentParent* aContentParent);
@ -237,13 +255,24 @@ public:
void OnFrameloaderVisibleChanged(nsISupports* aSubject);
void OnChannelConnected(nsISupports* aSubject);
ProcessPriority CurrentPriority();
ProcessPriority ComputePriority();
ProcessCPUPriority ComputeCPUPriority();
void ScheduleResetPriority(const char* aTimeoutPref);
void ResetPriority();
void ResetPriorityNow();
void ResetCPUPriorityNow();
/**
* This overload is equivalent to SetPriorityNow(aPriority,
* ComputeCPUPriority()).
*/
void SetPriorityNow(ProcessPriority aPriority);
void SetPriorityNow(ProcessPriority aPriority,
ProcessCPUPriority aCPUPriority);
void ShutDown();
private:
@ -258,6 +287,7 @@ private:
ContentParent* mContentParent;
uint64_t mChildID;
ProcessPriority mPriority;
ProcessCPUPriority mCPUPriority;
bool mHoldsCPUWakeLock;
bool mHoldsHighPriorityWakeLock;
@ -342,6 +372,7 @@ ProcessPriorityManagerImpl::ProcessPriorityManagerImpl()
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
mParticularManagers.Init();
mHighPriorityChildIDs.Init();
}
void
@ -352,7 +383,8 @@ ProcessPriorityManagerImpl::Init()
// The master process's priority never changes; set it here and then forget
// about it. We'll manage only subprocesses' priorities using the process
// priority manager.
hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER);
hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER,
PROCESS_CPU_PRIORITY_NORMAL);
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
@ -418,6 +450,18 @@ ProcessPriorityManagerImpl::ObserveContentParentCreated(
GetParticularProcessPriorityManager(static_cast<ContentParent*>(cp.get()));
}
static PLDHashOperator
EnumerateParticularProcessPriorityManagers(
const uint64_t& aKey,
nsRefPtr<ParticularProcessPriorityManager> aValue,
void* aUserData)
{
nsTArray<nsRefPtr<ParticularProcessPriorityManager> >* aArray =
static_cast<nsTArray<nsRefPtr<ParticularProcessPriorityManager> >*>(aUserData);
aArray->AppendElement(aValue);
return PL_DHASH_NEXT;
}
void
ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject)
{
@ -436,17 +480,77 @@ ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject)
}
mParticularManagers.Remove(childID);
if (mHighPriorityChildIDs.Contains(childID)) {
mHighPriorityChildIDs.RemoveEntry(childID);
// We just lost a high-priority process; reset everyone's CPU priorities.
nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
mParticularManagers.EnumerateRead(
&EnumerateParticularProcessPriorityManagers,
&pppms);
for (uint32_t i = 0; i < pppms.Length(); i++) {
pppms[i]->ResetCPUPriorityNow();
}
}
}
NS_IMPL_ISUPPORTS2(ParticularProcessPriorityManager,
bool
ProcessPriorityManagerImpl::OtherProcessHasHighPriority(
ParticularProcessPriorityManager* aParticularManager)
{
if (mHighPriorityChildIDs.Contains(aParticularManager->ChildID())) {
return mHighPriorityChildIDs.Count() > 1;
}
return mHighPriorityChildIDs.Count() > 0;
}
void
ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
ParticularProcessPriorityManager* aParticularManager,
ProcessPriority aOldPriority)
{
// This priority change can only affect other processes' priorities if we're
// changing to/from FOREGROUND_HIGH.
if (aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
aParticularManager->CurrentPriority() <
PROCESS_PRIORITY_FOREGROUND_HIGH) {
return;
}
if (aParticularManager->CurrentPriority() >=
PROCESS_PRIORITY_FOREGROUND_HIGH) {
mHighPriorityChildIDs.PutEntry(aParticularManager->ChildID());
} else {
mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID());
}
nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
mParticularManagers.EnumerateRead(
&EnumerateParticularProcessPriorityManagers,
&pppms);
for (uint32_t i = 0; i < pppms.Length(); i++) {
if (pppms[i] != aParticularManager) {
pppms[i]->ResetCPUPriorityNow();
}
}
}
NS_IMPL_ISUPPORTS3(ParticularProcessPriorityManager,
nsIObserver,
nsITimerCallback);
nsITimerCallback,
nsISupportsWeakReference);
ParticularProcessPriorityManager::ParticularProcessPriorityManager(
ContentParent* aContentParent)
: mContentParent(aContentParent)
, mChildID(aContentParent->ChildID())
, mPriority(PROCESS_PRIORITY_UNKNOWN)
, mCPUPriority(PROCESS_CPU_PRIORITY_NORMAL)
, mHoldsCPUWakeLock(false)
, mHoldsHighPriorityWakeLock(false)
{
@ -461,10 +565,10 @@ ParticularProcessPriorityManager::Init()
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ false);
os->AddObserver(this, "remote-browser-frame-shown", /* ownsWeak */ false);
os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ false);
os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ false);
os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true);
os->AddObserver(this, "remote-browser-frame-shown", /* ownsWeak */ true);
os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true);
}
// This process may already hold the CPU lock; for example, our parent may
@ -738,6 +842,12 @@ ParticularProcessPriorityManager::IsExpectingSystemMessage()
return false;
}
ProcessPriority
ParticularProcessPriorityManager::CurrentPriority()
{
return mPriority;
}
ProcessPriority
ParticularProcessPriorityManager::ComputePriority()
{
@ -775,15 +885,43 @@ ParticularProcessPriorityManager::ComputePriority()
PROCESS_PRIORITY_BACKGROUND;
}
ProcessCPUPriority
ParticularProcessPriorityManager::ComputeCPUPriority()
{
if (mPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) {
return PROCESS_CPU_PRIORITY_NORMAL;
}
return ProcessPriorityManagerImpl::GetSingleton()->
OtherProcessHasHighPriority(this) ?
PROCESS_CPU_PRIORITY_LOW :
PROCESS_CPU_PRIORITY_NORMAL;
}
void
ParticularProcessPriorityManager::ResetCPUPriorityNow()
{
SetPriorityNow(mPriority);
}
void
ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority)
{
SetPriorityNow(aPriority, ComputeCPUPriority());
}
void
ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
ProcessCPUPriority aCPUPriority)
{
if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
MOZ_ASSERT(false);
return;
}
if (mPriority == aPriority) {
if (!mContentParent ||
!ProcessPriorityManagerImpl::PrefsEnabled() ||
(mPriority == aPriority && mCPUPriority == aCPUPriority)) {
return;
}
@ -796,12 +934,18 @@ ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority)
}
LOGP("Changing priority from %s to %s.",
ProcessPriorityToString(mPriority),
ProcessPriorityToString(aPriority));
mPriority = aPriority;
hal::SetProcessPriority(Pid(), mPriority);
ProcessPriorityToString(mPriority, mCPUPriority),
ProcessPriorityToString(aPriority, aCPUPriority));
unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
ProcessPriority oldPriority = mPriority;
mPriority = aPriority;
mCPUPriority = aCPUPriority;
hal::SetProcessPriority(Pid(), mPriority, mCPUPriority);
if (oldPriority != mPriority) {
unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
}
if (aPriority >= PROCESS_PRIORITY_FOREGROUND) {
unused << mContentParent->SendCancelMinimizeMemoryUsage();
@ -810,7 +954,12 @@ ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority)
}
FireTestOnlyObserverNotification("process-priority-set",
ProcessPriorityToString(mPriority));
ProcessPriorityToString(mPriority, mCPUPriority));
if (oldPriority != mPriority) {
ProcessPriorityManagerImpl::GetSingleton()->
NotifyProcessPriorityChanged(this, oldPriority);
}
}
void

View File

@ -13,7 +13,7 @@ dictionary SmsThreadListItem
unsigned long long unreadCount;
};
[scriptable, builtinclass, uuid(5e993cfc-fb34-46a8-bb14-3df5c09ff748)]
[scriptable, builtinclass, uuid(6e20c451-8bae-4b36-be3c-da166fdd10ba)]
interface nsIMobileMessageCallback : nsISupports
{
/**
@ -39,7 +39,8 @@ interface nsIMobileMessageCallback : nsISupports
void notifyMessageGot(in nsISupports message);
void notifyGetMessageFailed(in long error);
void notifyMessageDeleted(in boolean deleted);
void notifyMessageDeleted([array, size_is(count)] in boolean deleted,
in uint32_t count);
void notifyDeleteMessageFailed(in long error);
void notifyMessageMarkedRead(in boolean read);

View File

@ -16,14 +16,15 @@ interface nsIDOMMozSmsFilter;
interface nsIMobileMessageCallback;
interface nsIMobileMessageCursorCallback;
[scriptable, uuid(ec1ca45f-e621-4c67-9c50-74c16842e780)]
[scriptable, uuid(ea6f49ae-3a4c-47eb-a489-15578e634100)]
interface nsIMobileMessageDatabaseService : nsISupports
{
[binaryname(GetMessageMoz)]
void getMessage(in long messageId,
in nsIMobileMessageCallback request);
void deleteMessage(in long messageId,
void deleteMessage([array, size_is(count)] in long messageIds,
in uint32_t count,
in nsIMobileMessageCallback request);
nsICursorContinueCallback createMessageCursor(in nsIDOMMozSmsFilter filter,

View File

@ -13,6 +13,7 @@
#include "jsapi.h"
#include "xpcpublic.h"
#include "nsServiceManagerUtils.h"
#include "nsTArrayHelpers.h"
namespace mozilla {
namespace dom {
@ -117,9 +118,29 @@ MobileMessageCallback::NotifyGetMessageFailed(int32_t aError)
}
NS_IMETHODIMP
MobileMessageCallback::NotifyMessageDeleted(bool aDeleted)
MobileMessageCallback::NotifyMessageDeleted(bool *aDeleted, uint32_t aSize)
{
return NotifySuccess(aDeleted ? JSVAL_TRUE : JSVAL_FALSE);
if (aSize == 1) {
return NotifySuccess(aDeleted[0] ? JSVAL_TRUE : JSVAL_FALSE);
}
nsresult rv;
nsIScriptContext* sc = mDOMRequest->GetContextForEventHandlers(&rv);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(sc, NS_ERROR_FAILURE);
AutoPushJSContext cx(sc->GetNativeContext());
NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
JSObject *deleteArrayObj = JS_NewArrayObject(cx, aSize, NULL);
JS::Value jsValTrue = BOOLEAN_TO_JSVAL(1);
JS::Value jsValFalse = BOOLEAN_TO_JSVAL(0);
for (uint32_t i = 0; i < aSize; i++) {
JS_SetElement(cx, deleteArrayObj, i,
aDeleted[i] ? &jsValTrue : &jsValFalse);
}
return NotifySuccess(OBJECT_TO_JSVAL(deleteArrayObj));
}
NS_IMETHODIMP

View File

@ -213,29 +213,29 @@ MobileMessageManager::GetMessageMoz(int32_t aId, nsIDOMDOMRequest** aRequest)
}
nsresult
MobileMessageManager::Delete(int32_t aId, nsIDOMDOMRequest** aRequest)
MobileMessageManager::GetMessageId(AutoPushJSContext &aCx,
const JS::Value &aMessage, int32_t &aId)
{
nsCOMPtr<nsIMobileMessageDatabaseService> mobileMessageDBService =
do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMMozSmsMessage> smsMessage =
do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, &aMessage.toObject()));
if (smsMessage) {
return smsMessage->GetId(&aId);
}
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsIMobileMessageCallback> msgCallback = new MobileMessageCallback(request);
nsresult rv = mobileMessageDBService->DeleteMessage(aId, msgCallback);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMMozMmsMessage> mmsMessage =
do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, &aMessage.toObject()));
if (mmsMessage) {
return mmsMessage->GetId(&aId);
}
request.forget(aRequest);
return NS_OK;
return NS_ERROR_INVALID_ARG;
}
NS_IMETHODIMP
MobileMessageManager::Delete(const JS::Value& aParam, nsIDOMDOMRequest** aRequest)
{
if (aParam.isInt32()) {
return Delete(aParam.toInt32(), aRequest);
}
if (!aParam.isObject()) {
// We expect Int32, SmsMessage, MmsMessage, Int32[], SmsMessage[], MmsMessage[]
if (!aParam.isObject() && !aParam.isInt32()) {
return NS_ERROR_INVALID_ARG;
}
@ -244,22 +244,61 @@ MobileMessageManager::Delete(const JS::Value& aParam, nsIDOMDOMRequest** aReques
AutoPushJSContext cx(sc->GetNativeContext());
NS_ENSURE_STATE(sc);
int32_t id;
nsCOMPtr<nsIDOMMozSmsMessage> smsMessage =
do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, &aParam.toObject()));
if (smsMessage) {
smsMessage->GetId(&id);
int32_t id, *idArray;
uint32_t size;
if (aParam.isInt32()) {
// Single Integer Message ID
id = aParam.toInt32();
size = 1;
idArray = &id;
} else if (!JS_IsArrayObject(cx, &aParam.toObject())) {
// Single SmsMessage/MmsMessage object
rv = GetMessageId(cx, aParam, id);
NS_ENSURE_SUCCESS(rv, rv);
size = 1;
idArray = &id;
} else {
nsCOMPtr<nsIDOMMozMmsMessage> mmsMessage =
do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, &aParam.toObject()));
if (mmsMessage) {
mmsMessage->GetId(&id);
} else {
return NS_ERROR_INVALID_ARG;
// Int32[], SmsMessage[], or MmsMessage[]
JSObject& ids = aParam.toObject();
JS_ALWAYS_TRUE(JS_GetArrayLength(cx, &ids, &size));
nsAutoArrayPtr<int32_t> idAutoArray(new int32_t[size]);
JS::Value idJsValue;
for (uint32_t i = 0; i < size; i++) {
if (!JS_GetElement(cx, &ids, i, &idJsValue)) {
return NS_ERROR_INVALID_ARG;
}
if (idJsValue.isInt32()) {
idAutoArray[i] = idJsValue.toInt32();
} else if (idJsValue.isObject()) {
rv = GetMessageId(cx, idJsValue, id);
NS_ENSURE_SUCCESS(rv, rv);
idAutoArray[i] = id;
}
}
idArray = idAutoArray.forget();
}
return Delete(id, aRequest);
nsCOMPtr<nsIMobileMessageDatabaseService> dbService =
do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(dbService, NS_ERROR_FAILURE);
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsIMobileMessageCallback> msgCallback =
new MobileMessageCallback(request);
rv = dbService->DeleteMessage(idArray, size, msgCallback);
NS_ENSURE_SUCCESS(rv, rv);
request.forget(aRequest);
return NS_OK;
}
NS_IMETHODIMP

View File

@ -37,14 +37,15 @@ private:
nsresult Send(JSContext* aCx, JSObject* aGlobal, JSString* aNumber,
const nsAString& aMessage, JS::Value* aRequest);
/**
* Internal Delete() method used to delete a message.
*/
nsresult Delete(int32_t aId, nsIDOMDOMRequest** aRequest);
nsresult DispatchTrustedSmsEventToSelf(const char* aTopic,
const nsAString& aEventName,
nsISupports* aMsg);
/**
* Helper to get message ID from SMS/MMS Message object
*/
nsresult GetMessageId(AutoPushJSContext &aCx, const JS::Value &aMessage,
int32_t &aId);
};
} // namespace dom

View File

@ -254,8 +254,71 @@ SmsManager::GetMessageMoz(int32_t aId, nsIDOMDOMRequest** aRequest)
}
nsresult
SmsManager::Delete(int32_t aId, nsIDOMDOMRequest** aRequest)
SmsManager::GetSmsMessageId(AutoPushJSContext &aCx,
const JS::Value &aSmsMessage, int32_t &aId)
{
nsCOMPtr<nsIDOMMozSmsMessage> message =
do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, &aSmsMessage.toObject()));
NS_ENSURE_TRUE(message, NS_ERROR_INVALID_ARG);
return message->GetId(&aId);
}
NS_IMETHODIMP
SmsManager::Delete(const JS::Value& aParam, nsIDOMDOMRequest** aRequest)
{
// We expect Int32, SmsMessage, Int32[], SmsMessage[]
if (!aParam.isObject() && !aParam.isInt32()) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
AutoPushJSContext cx(sc->GetNativeContext());
NS_ENSURE_STATE(sc);
int32_t id, *idArray;
uint32_t size;
if (aParam.isInt32()) {
// Single Integer Message ID
id = aParam.toInt32();
size = 1;
idArray = &id;
} else if (!JS_IsArrayObject(cx, &aParam.toObject())) {
// Single SmsMessage object
rv = GetSmsMessageId(cx, aParam, id);
NS_ENSURE_SUCCESS(rv, rv);
size = 1;
idArray = &id;
} else {
// Int32[] or SmsMessage[]
JSObject& ids = aParam.toObject();
JS_ALWAYS_TRUE(JS_GetArrayLength(cx, &ids, &size));
nsAutoArrayPtr<int32_t> idAutoArray(new int32_t[size]);
JS::Value idJsValue;
for (uint32_t i = 0; i < size; i++) {
if (!JS_GetElement(cx, &ids, i, &idJsValue)) {
return NS_ERROR_INVALID_ARG;
}
if (idJsValue.isInt32()) {
idAutoArray[i] = idJsValue.toInt32();
} else if (idJsValue.isObject()) {
rv = GetSmsMessageId(cx, idJsValue, id);
NS_ENSURE_SUCCESS(rv, rv);
idAutoArray[i] = id;
}
}
idArray = idAutoArray.forget();
}
nsCOMPtr<nsIMobileMessageDatabaseService> dbService =
do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(dbService, NS_ERROR_FAILURE);
@ -264,38 +327,13 @@ SmsManager::Delete(int32_t aId, nsIDOMDOMRequest** aRequest)
nsCOMPtr<nsIMobileMessageCallback> msgCallback =
new MobileMessageCallback(request);
nsresult rv = dbService->DeleteMessage(aId, msgCallback);
rv = dbService->DeleteMessage(idArray, size, msgCallback);
NS_ENSURE_SUCCESS(rv, rv);
request.forget(aRequest);
return NS_OK;
}
NS_IMETHODIMP
SmsManager::Delete(const JS::Value& aParam, nsIDOMDOMRequest** aRequest)
{
if (aParam.isInt32()) {
return Delete(aParam.toInt32(), aRequest);
}
if (!aParam.isObject()) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
AutoPushJSContext cx(sc->GetNativeContext());
NS_ENSURE_STATE(sc);
nsCOMPtr<nsIDOMMozSmsMessage> message =
do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, &aParam.toObject()));
NS_ENSURE_TRUE(message, NS_ERROR_INVALID_ARG);
int32_t id;
message->GetId(&id);
return Delete(id, aRequest);
}
NS_IMETHODIMP
SmsManager::GetMessages(nsIDOMMozSmsFilter* aFilter,
bool aReverse,

View File

@ -39,13 +39,14 @@ private:
nsresult Send(JSContext* aCx, JSObject* aGlobal, JS::Handle<JSString*> aNumber,
const nsAString& aMessage, JS::Value* aRequest);
/**
* Internal Delete() method used to delete a message.
*/
nsresult Delete(int32_t aId, nsIDOMDOMRequest** aRequest);
nsresult DispatchTrustedSmsEventToSelf(const nsAString& aEventName,
nsIDOMMozSmsMessage* aMessage);
/**
* Helper to get message ID from SMS Message object
*/
nsresult GetSmsMessageId(AutoPushJSContext &aCx, const JS::Value &aSmsMessage,
int32_t &aId);
};
} // namespace dom

View File

@ -26,14 +26,23 @@ MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId,
}
NS_IMETHODIMP
MobileMessageDatabaseService::DeleteMessage(int32_t aMessageId,
MobileMessageDatabaseService::DeleteMessage(int32_t *aMessageIds,
uint32_t aLength,
nsIMobileMessageCallback* aRequest)
{
if (!AndroidBridge::Bridge()) {
return NS_OK;
}
AndroidBridge::Bridge()->DeleteMessage(aMessageId, aRequest);
if (!aMessageIds) {
return NS_OK;
}
if (aLength != 1) {
return NS_ERROR_FAILURE;
}
AndroidBridge::Bridge()->DeleteMessage(aMessageIds[0], aRequest);
return NS_OK;
}

View File

@ -20,7 +20,8 @@ MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId,
}
NS_IMETHODIMP
MobileMessageDatabaseService::DeleteMessage(int32_t aMessageId,
MobileMessageDatabaseService::DeleteMessage(int32_t *aMessageIds,
uint32_t aLength,
nsIMobileMessageCallback* aRequest)
{
NS_ERROR("We should not be here!");

View File

@ -46,7 +46,7 @@ struct GetMessageRequest
struct DeleteMessageRequest
{
int32_t messageId;
int32_t[] messageIds;
};
struct CreateMessageCursorRequest

View File

@ -48,7 +48,7 @@ struct ReplyGetMessageFail
struct ReplyMessageDelete
{
bool deleted;
bool[] deleted;
};
struct ReplyMessageDeleteFail

View File

@ -177,11 +177,14 @@ SmsRequestChild::Recv__delete__(const MessageReply& aReply)
case MessageReply::TReplyGetMessageFail:
mReplyRequest->NotifyGetMessageFailed(aReply.get_ReplyGetMessageFail().error());
break;
case MessageReply::TReplyMessageDelete:
mReplyRequest->NotifyMessageDeleted(aReply.get_ReplyMessageDelete().deleted());
case MessageReply::TReplyMessageDelete: {
const InfallibleTArray<bool>& deletedResult = aReply.get_ReplyMessageDelete().deleted();
mReplyRequest->NotifyMessageDeleted(const_cast<bool *>(deletedResult.Elements()),
deletedResult.Length());
}
break;
case MessageReply::TReplyMessageDeleteFail:
mReplyRequest->NotifyMessageDeleted(aReply.get_ReplyMessageDeleteFail().error());
mReplyRequest->NotifyDeleteMessageFailed(aReply.get_ReplyMessageDeleteFail().error());
break;
case MessageReply::TReplyMarkeMessageRead:
mReplyRequest->NotifyMessageMarkedRead(aReply.get_ReplyMarkeMessageRead().read());

View File

@ -129,10 +129,12 @@ SmsIPCService::GetMessageMoz(int32_t aMessageId,
}
NS_IMETHODIMP
SmsIPCService::DeleteMessage(int32_t aMessageId,
SmsIPCService::DeleteMessage(int32_t *aMessageIds, uint32_t aSize,
nsIMobileMessageCallback* aRequest)
{
return SendRequest(DeleteMessageRequest(aMessageId), aRequest);
DeleteMessageRequest data;
data.messageIds().AppendElements(aMessageIds, aSize);
return SendRequest(data, aRequest);
}
NS_IMETHODIMP

View File

@ -482,7 +482,9 @@ SmsRequestParent::DoRequest(const DeleteMessageRequest& aRequest)
nsCOMPtr<nsIMobileMessageDatabaseService> dbService =
do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
if (dbService) {
rv = dbService->DeleteMessage(aRequest.messageId(), this);
const InfallibleTArray<int32_t>& messageIds = aRequest.messageIds();
rv = dbService->DeleteMessage(const_cast<int32_t *>(messageIds.Elements()),
messageIds.Length(), this);
}
if (NS_FAILED(rv)) {
@ -583,9 +585,11 @@ SmsRequestParent::NotifyGetMessageFailed(int32_t aError)
}
NS_IMETHODIMP
SmsRequestParent::NotifyMessageDeleted(bool aDeleted)
SmsRequestParent::NotifyMessageDeleted(bool *aDeleted, uint32_t aSize)
{
return SendReply(ReplyMessageDelete(aDeleted));
ReplyMessageDelete data;
data.deleted().AppendElements(aDeleted, aSize);
return SendReply(data);
}
NS_IMETHODIMP

View File

@ -1417,9 +1417,9 @@ MobileMessageDatabaseService.prototype = {
};
},
deleteMessage: function deleteMessage(messageId, aRequest) {
if (DEBUG) debug("deleteMessage: message id " + messageId);
let deleted = false;
deleteMessage: function deleteMessage(messageIds, length, aRequest) {
if (DEBUG) debug("deleteMessage: message ids " + JSON.stringify(messageIds));
let deleted = [];
let self = this;
this.newTxn(READ_WRITE, function (error, txn, stores) {
if (error) {
@ -1436,34 +1436,37 @@ MobileMessageDatabaseService.prototype = {
const messageStore = stores[0];
const threadStore = stores[1];
let deleted = false;
txn.oncomplete = function oncomplete(event) {
if (DEBUG) debug("Transaction " + txn + " completed.");
aRequest.notifyMessageDeleted(deleted);
aRequest.notifyMessageDeleted(deleted, length);
};
messageStore.get(messageId).onsuccess = function(event) {
let messageRecord = event.target.result;
if (messageRecord) {
if (DEBUG) debug("Deleting message id " + messageId);
for (let i = 0; i < length; i++) {
let messageId = messageIds[i];
deleted[i] = false;
messageStore.get(messageId).onsuccess = function(messageIndex, event) {
let messageRecord = event.target.result;
let messageId = messageIds[messageIndex];
if (messageRecord) {
if (DEBUG) debug("Deleting message id " + messageId);
// First actually delete the message.
messageStore.delete(messageId).onsuccess = function(event) {
if (DEBUG) debug("Message id " + messageId + " deleted");
deleted = true;
// First actually delete the message.
messageStore.delete(messageId).onsuccess = function(event) {
if (DEBUG) debug("Message id " + messageId + " deleted");
deleted[messageIndex] = true;
// Then update unread count and most recent message.
self.updateThreadByMessageChange(messageStore,
threadStore,
messageRecord.threadId,
messageId,
messageRecord.read);
};
} else if (DEBUG) {
debug("Message id " + messageId + " does not exist");
}
};
// Then update unread count and most recent message.
self.updateThreadByMessageChange(messageStore,
threadStore,
messageRecord.threadId,
messageId,
messageRecord.read);
};
} else if (DEBUG) {
debug("Message id " + messageId + " does not exist");
}
}.bind(null, i);
}
}, [MESSAGE_STORE_NAME, THREAD_STORE_NAME]);
},

View File

@ -31,3 +31,4 @@ qemu = true
[test_incoming_max_segments.js]
[test_outgoing_max_segments.js]
[test_update_thread_record_in_delete.js]
[test_massive_incoming_delete.js]

View File

@ -0,0 +1,216 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
SpecialPowers.setBoolPref("dom.sms.enabled", true);
SpecialPowers.addPermission("sms", true, document);
const SENDER = "5555552368"; // the remote number
const RECEIVER = "15555215554"; // the emulator's number
let sms = window.navigator.mozMobileMessage;
let MSG_TEXT = "Mozilla Firefox OS!";
let SMS_NUMBER = 100;
let SmsList = [];
let checkDone = true;
let emulatorReady = true;
let pendingEmulatorCmdCount = 0;
function sendSmsToEmulator(from, text) {
++pendingEmulatorCmdCount;
let cmd = "sms send " + from + " " + text;
runEmulatorCmd(cmd, function (result) {
--pendingEmulatorCmdCount;
is(result[0], "OK", "Emulator response");
});
}
let tasks = {
// List of test fuctions. Each of them should call |tasks.next()| when
// completed or |tasks.finish()| to jump to the last one.
_tasks: [],
_nextTaskIndex: 0,
push: function push(func) {
this._tasks.push(func);
},
next: function next() {
let index = this._nextTaskIndex++;
let task = this._tasks[index];
try {
task();
} catch (ex) {
ok(false, "test task[" + index + "] throws: " + ex);
// Run last task as clean up if possible.
if (index != this._tasks.length - 1) {
this.finish();
}
}
},
finish: function finish() {
this._tasks[this._tasks.length - 1]();
},
run: function run() {
this.next();
}
};
function taskNextWrapper() {
tasks.next();
}
function verifySmsExists(incomingSms) {
log("Getting SMS (id: " + incomingSms.id + ").");
let requestRet = sms.getMessage(incomingSms.id);
ok(requestRet, "smsrequest obj returned");
requestRet.onsuccess = function(event) {
log("Received 'onsuccess' smsrequest event.");
ok(event.target.result, "smsrequest event.target.result");
let foundSms = event.target.result;
is(foundSms.id, incomingSms.id, "found SMS id matches");
is(foundSms.threadId, incomingSms.threadId, "found SMS thread id matches");
is(foundSms.body, MSG_TEXT, "found SMS msg text matches");
is(foundSms.delivery, "received", "delivery");
is(foundSms.deliveryStatus, "success", "deliveryStatus");
is(foundSms.read, false, "read");
is(foundSms.receiver, RECEIVER, "receiver");
is(foundSms.sender, SENDER, "sender");
is(foundSms.messageClass, "normal", "messageClass");
log("Got SMS (id: " + foundSms.id + ") as expected.");
SmsList.push(incomingSms);
};
requestRet.onerror = function(event) {
log("Received 'onerror' smsrequest event.");
ok(event.target.error, "domerror obj");
is(event.target.error.name, "NotFoundError", "error returned");
log("Could not get SMS (id: " + incomingSms.id + ") but should have.");
ok(false,"SMS was not found");
tasks.finish();
};
}
let verifDeletedCount = 0;
function verifySmsDeleted(smsId) {
log("Getting SMS (id: " + smsId + ").");
let requestRet = sms.getMessage(smsId);
ok(requestRet, "smsrequest obj returned");
requestRet.onsuccess = function(event) {
log("Received 'onsuccess' smsrequest event.");
ok(event.target.result, "smsrequest event.target.result");
let foundSms = event.target.result;
is(foundSms.id, smsId, "found SMS id matches");
is(foundSms.body, MSG_TEXT, "found SMS msg text matches");
log("Got SMS (id: " + foundSms.id + ") but should not have.");
ok(false, "SMS was not deleted");
tasks.finish();
};
requestRet.onerror = function(event) {
log("Received 'onerror' smsrequest event.");
ok(event.target.error, "domerror obj");
is(event.target.error.name, "NotFoundError", "error returned");
log("Could not get SMS (id: " + smsId + ") as expected.");
verifDeletedCount++;
};
}
tasks.push(function init() {
log("Initialize test object.");
ok(sms, "mozSms");
// Callback for incoming sms
sms.onreceived = function onreceived(event) {
log("Received 'onreceived' smsmanager event.");
let incomingSms = event.message;
ok(incomingSms, "incoming sms");
ok(incomingSms.id, "sms id");
log("Received SMS (id: " + incomingSms.id + ").");
ok(incomingSms.threadId, "thread id");
is(incomingSms.body, MSG_TEXT, "msg body");
is(incomingSms.delivery, "received", "delivery");
is(incomingSms.deliveryStatus, "success", "deliveryStatus");
is(incomingSms.read, false, "read");
is(incomingSms.receiver, RECEIVER, "receiver");
is(incomingSms.sender, SENDER, "sender");
is(incomingSms.messageClass, "normal", "messageClass");
ok(incomingSms.timestamp instanceof Date, "timestamp is istanceof date");
verifySmsExists(incomingSms);
};
tasks.next();
});
tasks.push(function sendAllSms() {
log("Send " + SMS_NUMBER + " SMS");
for (let i = 0; i < SMS_NUMBER; i++) {
sendSmsToEmulator(SENDER, MSG_TEXT);
}
waitFor(taskNextWrapper, function() {
return (pendingEmulatorCmdCount === 0) && (SmsList.length === SMS_NUMBER);
});
});
tasks.push(function deleteAllSms() {
log("Deleting SMS using smsmsg obj array parameter.");
let deleteStart = Date.now();
log("deleteStart: " + deleteStart);
log("SmsList: " + JSON.stringify(SmsList));
let requestRet = sms.delete(SmsList);
ok(requestRet,"smsrequest obj returned");
requestRet.onsuccess = function(event) {
let deleteDone = Date.now();
log("Delete " + SMS_NUMBER + " SMS takes " + (deleteDone - deleteStart) + " ms.");
log("Received 'onsuccess' smsrequest event.");
if (event.target.result) {
for (let i = 0; i < SmsList.length; i++) {
verifySmsDeleted(SmsList[i].id);
}
} else {
log("smsrequest returned false for sms.delete");
ok(false, "SMS delete failed");
}
};
requestRet.onerror = function(event) {
log("Received 'onerror' smsrequest event.");
ok(event.target.error, "domerror obj");
ok(false, "sms.delete request returned unexpected error: "
+ event.target.error.name);
tasks.finish();
};
waitFor(taskNextWrapper, function() {
return verifDeletedCount === SMS_NUMBER;
});
});
// WARNING: All tasks should be pushed before this!!!
tasks.push(function cleanUp() {
if (pendingEmulatorCmdCount) {
window.setTimeout(cleanUp, 100);
return;
}
sms.onreceived = null;
SpecialPowers.removePermission("sms", document);
SpecialPowers.setBoolPref("dom.sms.enabled", false);
log("Finish!!!");
finish();
});
// Start the test
tasks.run();

View File

@ -1089,7 +1089,8 @@ RILContentHelper.prototype = {
this._deliverEvent("_telephonyListeners",
"callStateChanged",
[msg.json.callIndex, msg.json.state,
msg.json.number, msg.json.isActive]);
msg.json.number, msg.json.isActive,
msg.json.isOutgoing]);
break;
case "RIL:CallError":
this._deliverEvent("_telephonyListeners",
@ -1198,7 +1199,7 @@ RILContentHelper.prototype = {
try {
keepGoing =
callback.enumerateCallState(call.callIndex, call.state, call.number,
call.isActive);
call.isActive, call.isOutgoing);
} catch (e) {
debug("callback handler for 'enumerateCallState' threw an " +
" exception: " + e);

View File

@ -1362,7 +1362,7 @@ RadioInterfaceLayer.prototype = {
let data = {
number: call.number,
duration: duration,
direction: call.direction
direction: call.isOutgoing ? "outgoing" : "incoming"
};
gSystemMessenger.broadcastMessage("telephony-call-ended", data);
this.updateCallAudioState(call);

View File

@ -3259,9 +3259,9 @@ let RIL = {
newCall.number = "+" + newCall.number;
}
if (newCall.state == CALL_STATE_INCOMING) {
newCall.direction = 'incoming';
newCall.isOutgoing = false;
} else if (newCall.state == CALL_STATE_DIALING) {
newCall.direction = 'outgoing';
newCall.isOutgoing = true;
}
// Add to our map.
this.currentCalls[newCall.callIndex] = newCall;

View File

@ -366,7 +366,8 @@ NS_IMPL_EVENT_HANDLER(Telephony, callschanged)
NS_IMETHODIMP
Telephony::CallStateChanged(uint32_t aCallIndex, uint16_t aCallState,
const nsAString& aNumber, bool aIsActive)
const nsAString& aNumber, bool aIsActive,
bool aIsOutgoing)
{
NS_ASSERTION(aCallIndex != kOutgoingPlaceholderCallIndex,
"This should never happen!");
@ -441,7 +442,7 @@ Telephony::CallStateChanged(uint32_t aCallIndex, uint16_t aCallState,
NS_IMETHODIMP
Telephony::EnumerateCallState(uint32_t aCallIndex, uint16_t aCallState,
const nsAString& aNumber, bool aIsActive,
bool* aContinue)
bool aIsOutgoing, bool* aContinue)
{
// Make sure we don't somehow add duplicates.
for (uint32_t index = 0; index < mCalls.Length(); index++) {

View File

@ -4,7 +4,7 @@
#include "nsISupports.idl"
[scriptable, uuid(2ab9abfe-09fb-4fea-985f-acf29fc7376a)]
[scriptable, uuid(45c5a67b-3780-4c41-a670-8abacffb4851)]
interface nsITelephonyListener : nsISupports
{
/**
@ -18,11 +18,14 @@ interface nsITelephonyListener : nsISupports
* Number of the other party.
* @param isActive
* Indicates whether this call is the currently active one.
* @param isOutgoing
* Indicates whether this call is outgoing or incoming.
*/
void callStateChanged(in unsigned long callIndex,
in unsigned short callState,
in AString number,
in boolean isActive);
in boolean isActive,
in boolean isOutgoing);
/**
* Called when nsITelephonyProvider is asked to enumerate the current
@ -37,13 +40,16 @@ interface nsITelephonyListener : nsISupports
* Number of the other party.
* @param isActive
* Indicates whether this call is the active one.
* @param isOutgoing
* Indicates whether this call is outgoing or incoming.
*
* @return true to continue enumeration or false to cancel.
*/
boolean enumerateCallState(in unsigned long callIndex,
in unsigned short callState,
in AString number,
in boolean isActive);
in boolean isActive,
in boolean isOutgoing);
/**
* Called when RIL error occurs.

View File

@ -34,8 +34,10 @@
#include <dxgi.h>
#endif
// This header is available in the June 2010 SDK and in the Win8 SDK
#include <d3dcommon.h>
// Win 8.0 SDK types we'll need when building using older sdks.
#if MOZ_WINSDK_TARGETVER <= 0x06010000
#if !defined(D3D_FEATURE_LEVEL_11_1) // defined in the 8.0 SDK only
#define D3D_FEATURE_LEVEL_11_1 static_cast<D3D_FEATURE_LEVEL>(0xb100)
#define D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION 2048
#define D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION 4096

View File

@ -846,9 +846,13 @@ SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
}
void
SetProcessPriority(int aPid, ProcessPriority aPriority)
SetProcessPriority(int aPid,
ProcessPriority aPriority,
ProcessCPUPriority aCPUPriority)
{
PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority));
// n.b. The sandboxed implementation crashes; SetProcessPriority works only
// from the main process.
PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority, aCPUPriority));
}
// From HalTypes.h.
@ -876,6 +880,75 @@ ProcessPriorityToString(ProcessPriority aPriority)
}
}
// From HalTypes.h.
const char*
ProcessPriorityToString(ProcessPriority aPriority,
ProcessCPUPriority aCPUPriority)
{
// Sorry this is ugly. At least it's all in one place.
//
// We intentionally fall through if aCPUPriority is invalid; we won't hit any
// of the if statements further down, so it's OK.
switch (aPriority) {
case PROCESS_PRIORITY_MASTER:
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
return "MASTER:CPU_NORMAL";
}
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
return "MASTER:CPU_LOW";
}
case PROCESS_PRIORITY_FOREGROUND_HIGH:
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
return "FOREGROUND_HIGH:CPU_NORMAL";
}
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
return "FOREGROUND_HIGH:CPU_LOW";
}
case PROCESS_PRIORITY_FOREGROUND:
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
return "FOREGROUND:CPU_NORMAL";
}
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
return "FOREGROUND:CPU_LOW";
}
case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
return "BACKGROUND_PERCEIVABLE:CPU_NORMAL";
}
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
return "BACKGROUND_PERCEIVABLE:CPU_LOW";
}
case PROCESS_PRIORITY_BACKGROUND_HOMESCREEN:
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
return "BACKGROUND_HOMESCREEN:CPU_NORMAL";
}
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
return "BACKGROUND_HOMESCREEN:CPU_LOW";
}
case PROCESS_PRIORITY_BACKGROUND:
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
return "BACKGROUND:CPU_NORMAL";
}
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
return "BACKGROUND:CPU_LOW";
}
case PROCESS_PRIORITY_UNKNOWN:
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
return "UNKNOWN:CPU_NORMAL";
}
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
return "UNKNOWN:CPU_LOW";
}
default:
// Fall through. (|default| is here to silence warnings.)
break;
}
MOZ_ASSERT(false);
return "???";
}
static StaticAutoPtr<ObserverList<FMRadioOperationInformation> > sFMRadioObservers;
static void
@ -1085,5 +1158,21 @@ void FactoryReset()
PROXY_IF_SANDBOXED(FactoryReset());
}
void
StartDiskSpaceWatcher()
{
AssertMainProcess();
AssertMainThread();
PROXY_IF_SANDBOXED(StartDiskSpaceWatcher());
}
void
StopDiskSpaceWatcher()
{
AssertMainProcess();
AssertMainThread();
PROXY_IF_SANDBOXED(StopDiskSpaceWatcher());
}
} // namespace hal
} // namespace mozilla

View File

@ -249,7 +249,7 @@ void AdjustSystemClock(int64_t aDeltaMilliseconds);
/**
* Set timezone
* @param aTimezoneSpec The definition can be found in
* @param aTimezoneSpec The definition can be found in
* http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
*/
void SetTimezone(const nsCString& aTimezoneSpec);
@ -303,14 +303,14 @@ void NotifySystemTimezoneChange(
/**
* Reboot the device.
*
*
* This API is currently only allowed to be used from the main process.
*/
void Reboot();
/**
* Power off the device.
*
*
* This API is currently only allowed to be used from the main process.
*/
void PowerOff();
@ -423,7 +423,7 @@ void RegisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aSwi
void UnregisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aSwitchObserver);
/**
* Notify the state of the switch.
* Notify the state of the switch.
*
* This API is internal to hal; clients shouldn't call it directly.
*/
@ -470,13 +470,21 @@ void NotifyAlarmFired();
bool SetAlarm(int32_t aSeconds, int32_t aNanoseconds);
/**
* Set the priority of the given process.
* Set the priority of the given process. A process's priority is a two-tuple
* consisting of a hal::ProcessPriority value and a hal::ProcessCPUPriority
* value.
*
* Two processes with the same ProcessCPUPriority value don't necessarily have
* the same CPU priority; the CPU priority we assign to a process is a function
* of its ProcessPriority and ProcessCPUPriority.
*
* Exactly what this does will vary between platforms. On *nix we might give
* background processes higher nice values. On other platforms, we might
* ignore this call entirely.
*/
void SetProcessPriority(int aPid, hal::ProcessPriority aPriority);
void SetProcessPriority(int aPid,
hal::ProcessPriority aPriority,
hal::ProcessCPUPriority aCPUPriority);
/**
* Register an observer for the FM radio.
@ -549,7 +557,7 @@ hal::FMRadioSettings GetFMBandSettings(hal::FMRadioCountry aCountry);
* Start a watchdog to compulsively shutdown the system if it hangs.
* @param aMode Specify how to shutdown the system.
* @param aTimeoutSecs Specify the delayed seconds to shutdown the system.
*
*
* This API is currently only allowed to be used from the main process.
*/
void StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs);
@ -569,6 +577,20 @@ void StartMonitoringGamepadStatus();
*/
void StopMonitoringGamepadStatus();
/**
* Start monitoring disk space for low space situations.
*
* This API is currently only allowed to be used from the main process.
*/
void StartDiskSpaceWatcher();
/**
* Stop monitoring disk space for low space situations.
*
* This API is currently only allowed to be used from the main process.
*/
void StopDiskSpaceWatcher();
} // namespace MOZ_HAL_NAMESPACE
} // namespace mozilla

View File

@ -92,14 +92,25 @@ enum ProcessPriority {
NUM_PROCESS_PRIORITY
};
// Convert a ProcessPriority enum value to a string. The strings returned by
// this function are statically allocated; do not attempt to free one!
enum ProcessCPUPriority {
PROCESS_CPU_PRIORITY_LOW,
PROCESS_CPU_PRIORITY_NORMAL,
NUM_PROCESS_CPU_PRIORITY
};
// Convert a ProcessPriority enum value (with an optional ProcessCPUPriority)
// to a string. The strings returned by this function are statically
// allocated; do not attempt to free one!
//
// If you pass an unknown process priority (or NUM_PROCESS_PRIORITY), we
// fatally assert in debug builds and otherwise return "???".
const char*
ProcessPriorityToString(ProcessPriority aPriority);
const char*
ProcessPriorityToString(ProcessPriority aPriority,
ProcessCPUPriority aCPUPriority);
/**
* Used by ModifyWakeLock
*/

View File

@ -14,30 +14,11 @@
#include "nsIPropertyBag2.h"
#include "nsObserverService.h"
using namespace mozilla;
using namespace mozilla::hal;
namespace mozilla {
namespace hal {
WakeLockState
ComputeWakeLockState(int aNumLocks, int aNumHidden)
{
if (aNumLocks == 0) {
return WAKE_LOCK_STATE_UNLOCKED;
} else if (aNumLocks == aNumHidden) {
return WAKE_LOCK_STATE_HIDDEN;
} else {
return WAKE_LOCK_STATE_VISIBLE;
}
}
} // hal
} // mozilla
namespace mozilla {
namespace hal_impl {
namespace {
struct LockCount {
LockCount()
: numLocks(0)
@ -47,15 +28,33 @@ struct LockCount {
uint32_t numHidden;
nsTArray<uint64_t> processes;
};
typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
static int sActiveListeners = 0;
static StaticAutoPtr<LockTable> sLockTable;
static bool sInitialized = false;
static bool sIsShuttingDown = false;
int sActiveListeners = 0;
StaticAutoPtr<LockTable> sLockTable;
bool sInitialized = false;
bool sIsShuttingDown = false;
static PLDHashOperator
WakeLockInformation
WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
{
// TODO: Once we abandon b2g18, we can switch this to use the
// WakeLockInformation constructor, which is better because it doesn't let us
// forget to assign a param. For now we have to do it this way, because
// b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
// 819791).
WakeLockInformation info;
info.topic() = aTopic;
info.numLocks() = aLockCount.numLocks;
info.numHidden() = aLockCount.numHidden;
info.lockingProcesses().AppendElements(aLockCount.processes);
return info;
}
PLDHashOperator
CountWakeLocks(const uint64_t& aKey, LockCount aCount, void* aUserArg)
{
MOZ_ASSERT(aUserArg);
@ -80,19 +79,17 @@ RemoveChildFromList(const nsAString& aKey, nsAutoPtr<ProcessLockTable>& aTable,
PLDHashOperator op = PL_DHASH_NEXT;
uint64_t childID = *static_cast<uint64_t*>(aUserArg);
if (aTable->Get(childID, NULL)) {
if (aTable->Get(childID, nullptr)) {
aTable->Remove(childID);
LockCount totalCount;
aTable->EnumerateRead(CountWakeLocks, &totalCount);
if (!totalCount.numLocks) {
op = PL_DHASH_REMOVE;
}
if (sActiveListeners) {
LockCount totalCount;
WakeLockInformation info;
aTable->EnumerateRead(CountWakeLocks, &totalCount);
if (!totalCount.numLocks) {
op = PL_DHASH_REMOVE;
}
info.numLocks() = totalCount.numLocks;
info.numHidden() = totalCount.numHidden;
info.topic() = aKey;
NotifyWakeLockChange(info);
NotifyWakeLockChange(WakeLockInfoFromLockCount(aKey, totalCount));
}
}
@ -152,7 +149,7 @@ CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, con
return NS_OK;
}
static void
void
Init()
{
sLockTable = new LockTable();
@ -165,8 +162,29 @@ Init()
obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
}
}
} // anonymous namespace
namespace mozilla {
namespace hal {
WakeLockState
ComputeWakeLockState(int aNumLocks, int aNumHidden)
{
if (aNumLocks == 0) {
return WAKE_LOCK_STATE_UNLOCKED;
} else if (aNumLocks == aNumHidden) {
return WAKE_LOCK_STATE_HIDDEN;
} else {
return WAKE_LOCK_STATE_VISIBLE;
}
}
} // namespace hal
namespace hal_impl {
void
EnableWakeLockNotifications()
{
@ -215,6 +233,7 @@ ModifyWakeLock(const nsAString& aTopic,
MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
bool processWasLocked = processCount.numLocks > 0;
processCount.numLocks += aLockAdjust;
processCount.numHidden += aHiddenAdjust;
@ -231,9 +250,11 @@ ModifyWakeLock(const nsAString& aTopic,
sLockTable->Remove(aTopic);
}
WakeLockState newState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
if (sActiveListeners &&
(oldState != ComputeWakeLockState(totalCount.numLocks,
totalCount.numHidden) ||
processWasLocked != (processCount.numLocks > 0))) {
if (sActiveListeners && oldState != newState) {
WakeLockInformation info;
GetWakeLockInfo(aTopic, &info);
NotifyWakeLockChange(info);
@ -245,6 +266,7 @@ GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
{
if (sIsShuttingDown) {
NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
*aWakeLockInfo = WakeLockInformation();
return;
}
if (!sInitialized) {
@ -253,17 +275,12 @@ GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
ProcessLockTable* table = sLockTable->Get(aTopic);
if (!table) {
aWakeLockInfo->numLocks() = 0;
aWakeLockInfo->numHidden() = 0;
aWakeLockInfo->topic() = aTopic;
*aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
return;
}
LockCount totalCount;
table->EnumerateRead(CountWakeLocks, &totalCount);
aWakeLockInfo->numLocks() = totalCount.numLocks;
aWakeLockInfo->numHidden() = totalCount.numHidden;
aWakeLockInfo->lockingProcesses() = totalCount.processes;
aWakeLockInfo->topic() = aTopic;
*aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
}
} // hal_impl

View File

@ -60,6 +60,7 @@ CPPSRCS += \
UeventPoller.cpp \
GonkSwitch.cpp \
GonkFMRadio.cpp \
GonkDiskSpaceWatcher.cpp \
$(NULL)
else ifeq (Linux,$(OS_TARGET))
CPPSRCS += \
@ -134,6 +135,7 @@ CPPSRCS += \
FallbackProcessPriority.cpp \
FallbackFMRadio.cpp \
FallbackFactoryReset.cpp \
FallbackDiskSpaceWatcher.cpp \
$(NULL)
endif #}

View File

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
namespace mozilla {
namespace hal_impl {
void
StartDiskSpaceWatcher()
{
}
void
StopDiskSpaceWatcher()
{
}
} // namespace hal_impl
} // namespace mozilla

View File

@ -10,9 +10,12 @@ namespace mozilla {
namespace hal_impl {
void
SetProcessPriority(int aPid, ProcessPriority aPriority)
SetProcessPriority(int aPid,
ProcessPriority aPriority,
ProcessCPUPriority aCPUPriority)
{
HAL_LOG(("FallbackProcessPriority - SetProcessPriority(%d, %d)\n", aPid, aPriority));
HAL_LOG(("FallbackProcessPriority - SetProcessPriority(%d, %s)\n",
aPid, ProcessPriorityToString(aPriority, aCPUPriority)));
}
} // hal_impl

View File

@ -0,0 +1,308 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "Hal.h"
#include <sys/syscall.h>
#include <sys/vfs.h>
#include <fcntl.h>
#include <errno.h>
#include "nsIObserverService.h"
#include "nsIDiskSpaceWatcher.h"
#include "mozilla/ModuleUtils.h"
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "base/message_loop.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsXULAppAPI.h"
#include "fanotify.h"
#include "DiskSpaceWatcher.h"
using namespace mozilla;
namespace mozilla { namespace hal_impl { class GonkDiskSpaceWatcher; } }
using namespace mozilla::hal_impl;
template<>
struct RunnableMethodTraits<GonkDiskSpaceWatcher>
{
static void RetainCallee(GonkDiskSpaceWatcher* obj) { }
static void ReleaseCallee(GonkDiskSpaceWatcher* obj) { }
};
namespace mozilla {
namespace hal_impl {
// fanotify_init and fanotify_mark functions are syscalls.
// The user space bits are not part of bionic so we add them here
// as well as fanotify.h
int fanotify_init (unsigned int flags, unsigned int event_f_flags)
{
return syscall(367, flags, event_f_flags);
}
// Add, remove, or modify an fanotify mark on a filesystem object.
int fanotify_mark (int fanotify_fd, unsigned int flags,
uint64_t mask, int dfd, const char *pathname)
{
// On 32 bits platforms we have to convert the 64 bits mask into
// two 32 bits ints.
if (sizeof(void *) == 4) {
union {
uint64_t _64;
uint32_t _32[2];
} _mask;
_mask._64 = mask;
return syscall(368, fanotify_fd, flags, _mask._32[0], _mask._32[1],
dfd, pathname);
}
return syscall(368, fanotify_fd, flags, mask, dfd, pathname);
}
class GonkDiskSpaceWatcher MOZ_FINAL : public MessageLoopForIO::Watcher
{
public:
GonkDiskSpaceWatcher();
~GonkDiskSpaceWatcher() {};
virtual void OnFileCanReadWithoutBlocking(int aFd);
// We should never write to the fanotify fd.
virtual void OnFileCanWriteWithoutBlocking(int aFd)
{
MOZ_NOT_REACHED("Must not write to fanotify fd");
MOZ_CRASH();
}
void DoStart();
void DoStop();
private:
void NotifyUpdate();
uint64_t mLowThreshold;
uint64_t mHighThreshold;
TimeDuration mTimeout;
TimeStamp mLastTimestamp;
uint64_t mLastFreeSpace;
uint32_t mSizeDelta;
bool mIsDiskFull;
uint64_t mFreeSpace;
int mFd;
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
};
static GonkDiskSpaceWatcher* gHalDiskSpaceWatcher = nullptr;
#define WATCHER_PREF_LOW "disk_space_watcher.low_threshold"
#define WATCHER_PREF_HIGH "disk_space_watcher.high_threshold"
#define WATCHER_PREF_TIMEOUT "disk_space_watcher.timeout"
#define WATCHER_PREF_SIZE_DELTA "disk_space_watcher.size_delta"
static const char kWatchedPath[] = "/data";
// Helper class to dispatch calls to xpcom on the main thread.
class DiskSpaceNotifier : public nsRunnable
{
public:
DiskSpaceNotifier(const bool aIsDiskFull, const uint64_t aFreeSpace) :
mIsDiskFull(aIsDiskFull),
mFreeSpace(aFreeSpace) {}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
DiskSpaceWatcher::UpdateState(mIsDiskFull, mFreeSpace);
return NS_OK;
}
private:
bool mIsDiskFull;
uint64_t mFreeSpace;
};
// Helper runnable to delete the watcher on the main thread.
class DiskSpaceCleaner : public nsRunnable
{
public:
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
delete gHalDiskSpaceWatcher;
return NS_OK;
}
};
GonkDiskSpaceWatcher::GonkDiskSpaceWatcher() :
mLastFreeSpace(UINT64_MAX),
mIsDiskFull(false),
mFreeSpace(UINT64_MAX),
mFd(-1)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(gHalDiskSpaceWatcher == nullptr);
// Default values: 5MB for low threshold, 10MB for high threshold, and
// a timeout of 5 seconds.
mLowThreshold = Preferences::GetInt(WATCHER_PREF_LOW, 5) * 1024 * 1024;
mHighThreshold = Preferences::GetInt(WATCHER_PREF_HIGH, 10) * 1024 * 1024;
mTimeout = TimeDuration::FromSeconds(Preferences::GetInt(WATCHER_PREF_TIMEOUT, 5));
mSizeDelta = Preferences::GetInt(WATCHER_PREF_SIZE_DELTA, 1) * 1024 * 1024;
}
void
GonkDiskSpaceWatcher::DoStart()
{
NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
"Not on the correct message loop");
mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC);
if (mFd == -1) {
NS_WARNING("Error calling inotify_init()");
if (errno == ENOSYS) {
printf_stderr("Warning: No fanotify support in this device's kernel.\n");
}
return;
}
if (fanotify_mark(mFd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_CLOSE,
0, kWatchedPath) < 0) {
NS_WARNING("Error calling fanotify_mark");
close(mFd);
mFd = -1;
return;
}
if (!MessageLoopForIO::current()->WatchFileDescriptor(
mFd, /* persistent = */ true,
MessageLoopForIO::WATCH_READ,
&mReadWatcher, gHalDiskSpaceWatcher)) {
NS_WARNING("Unable to watch fanotify fd.");
close(mFd);
mFd = -1;
}
}
void
GonkDiskSpaceWatcher::DoStop()
{
NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
"Not on the correct message loop");
if (mFd != -1) {
mReadWatcher.StopWatchingFileDescriptor();
fanotify_mark(mFd, FAN_MARK_FLUSH, 0, 0, kWatchedPath);
close(mFd);
mFd = -1;
}
// Dispatch the cleanup to the main thread.
nsCOMPtr<nsIRunnable> runnable = new DiskSpaceCleaner();
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
}
// We are called off the main thread, so we proxy first to the main thread
// before calling the xpcom object.
void
GonkDiskSpaceWatcher::NotifyUpdate()
{
mLastTimestamp = TimeStamp::Now();
mLastFreeSpace = mFreeSpace;
nsCOMPtr<nsIRunnable> runnable =
new DiskSpaceNotifier(mIsDiskFull, mFreeSpace);
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
}
void
GonkDiskSpaceWatcher::OnFileCanReadWithoutBlocking(int aFd)
{
struct fanotify_event_metadata* fem = nullptr;
char buf[4096];
struct statfs sfs;
int32_t len, rc;
do {
len = read(aFd, buf, sizeof(buf));
} while(len == -1 && errno == EINTR);
// We should get an exact multiple of fanotify_event_metadata
if (len <= 0 || (len % sizeof(*fem) != 0)) {
printf_stderr("About to crash: fanotify_event_metadata read error.");
MOZ_CRASH();
}
fem = reinterpret_cast<fanotify_event_metadata *>(buf);
while (FAN_EVENT_OK(fem, len)) {
rc = fstatfs(fem->fd, &sfs);
if (rc < 0) {
NS_WARNING("Unable to stat fan_notify fd");
} else {
bool firstRun = mFreeSpace == UINT64_MAX;
mFreeSpace = sfs.f_bavail * sfs.f_bsize;
// We change from full <-> free depending on the free space and the
// low and high thresholds.
// Once we are in 'full' mode we send updates for all size changes with
// a minimum of time between messages or when we cross a size change
// threshold.
if (firstRun) {
mIsDiskFull = mFreeSpace <= mLowThreshold;
// Always notify the current state at first run.
NotifyUpdate();
} else if (!mIsDiskFull && (mFreeSpace <= mLowThreshold)) {
mIsDiskFull = true;
NotifyUpdate();
} else if (mIsDiskFull && (mFreeSpace > mHighThreshold)) {
mIsDiskFull = false;
NotifyUpdate();
} else if (mIsDiskFull) {
if (mTimeout < TimeStamp::Now() - mLastTimestamp ||
mSizeDelta < llabs(mFreeSpace - mLastFreeSpace)) {
NotifyUpdate();
}
}
}
close(fem->fd);
fem = FAN_EVENT_NEXT(fem, len);
}
}
void
StartDiskSpaceWatcher()
{
MOZ_ASSERT(NS_IsMainThread());
// Bail out if called several times.
if (gHalDiskSpaceWatcher != nullptr) {
return;
}
gHalDiskSpaceWatcher = new GonkDiskSpaceWatcher();
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE,
NewRunnableMethod(gHalDiskSpaceWatcher, &GonkDiskSpaceWatcher::DoStart));
}
void
StopDiskSpaceWatcher()
{
MOZ_ASSERT(NS_IsMainThread());
if (!gHalDiskSpaceWatcher) {
return;
}
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE,
NewRunnableMethod(gHalDiskSpaceWatcher, &GonkDiskSpaceWatcher::DoStop));
}
} // namespace hal_impl
} // namespace mozilla

View File

@ -1056,29 +1056,25 @@ EnsureKernelLowMemKillerParamsSet()
nsAutoCString adjParams;
nsAutoCString minfreeParams;
const char* priorityClasses[] = {
"master",
"foregroundHigh",
"foreground",
"backgroundPerceivable",
"backgroundHomescreen",
"background"
};
for (size_t i = 0; i < NS_ARRAY_LENGTH(priorityClasses); i++) {
for (int i = 0; i < NUM_PROCESS_PRIORITY; i++) {
// The system doesn't function correctly if we're missing these prefs, so
// crash loudly.
ProcessPriority priority = static_cast<ProcessPriority>(i);
int32_t oomScoreAdj;
if (!NS_SUCCEEDED(Preferences::GetInt(nsPrintfCString(
"hal.processPriorityManager.gonk.%sOomScoreAdjust",
priorityClasses[i]).get(), &oomScoreAdj))) {
if (!NS_SUCCEEDED(Preferences::GetInt(
nsPrintfCString("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
ProcessPriorityToString(priority)).get(),
&oomScoreAdj))) {
MOZ_CRASH();
}
int32_t killUnderMB;
if (!NS_SUCCEEDED(Preferences::GetInt(nsPrintfCString(
"hal.processPriorityManager.gonk.%sKillUnderMB",
priorityClasses[i]).get(), &killUnderMB))) {
if (!NS_SUCCEEDED(Preferences::GetInt(
nsPrintfCString("hal.processPriorityManager.gonk.%s.KillUnderMB",
ProcessPriorityToString(priority)).get(),
&killUnderMB))) {
MOZ_CRASH();
}
@ -1187,9 +1183,12 @@ SetNiceForPid(int aPid, int aNice)
}
void
SetProcessPriority(int aPid, ProcessPriority aPriority)
SetProcessPriority(int aPid,
ProcessPriority aPriority,
ProcessCPUPriority aCPUPriority)
{
HAL_LOG(("SetProcessPriority(pid=%d, priority=%d)", aPid, aPriority));
HAL_LOG(("SetProcessPriority(pid=%d, priority=%d, cpuPriority=%d)",
aPid, aPriority, aCPUPriority));
// If this is the first time SetProcessPriority was called, set the kernel's
// OOM parameters according to our prefs.
@ -1200,43 +1199,12 @@ SetProcessPriority(int aPid, ProcessPriority aPriority)
// SetProcessPriority being called early in startup.
EnsureKernelLowMemKillerParamsSet();
const char* priorityStr = NULL;
switch (aPriority) {
case PROCESS_PRIORITY_BACKGROUND:
priorityStr = "background";
break;
case PROCESS_PRIORITY_BACKGROUND_HOMESCREEN:
priorityStr = "backgroundHomescreen";
break;
case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
priorityStr = "backgroundPerceivable";
break;
case PROCESS_PRIORITY_FOREGROUND:
priorityStr = "foreground";
break;
case PROCESS_PRIORITY_FOREGROUND_HIGH:
priorityStr = "foregroundHigh";
break;
case PROCESS_PRIORITY_MASTER:
priorityStr = "master";
break;
default:
// PROCESS_PRIORITY_UNKNOWN ends up in this branch, along with invalid enum
// values.
NS_ERROR("Invalid process priority!");
return;
}
// Notice that you can disable oom_adj and renice by deleting the prefs
// hal.processPriorityManager{foreground,background,master}{OomAdjust,Nice}.
int32_t oomScoreAdj = 0;
nsresult rv = Preferences::GetInt(nsPrintfCString(
"hal.processPriorityManager.gonk.%sOomScoreAdjust",
priorityStr).get(), &oomScoreAdj);
"hal.processPriorityManager.gonk.%s.OomScoreAdjust",
ProcessPriorityToString(aPriority)).get(), &oomScoreAdj);
if (NS_SUCCEEDED(rv)) {
int clampedOomScoreAdj = clamped<int>(oomScoreAdj, OOM_SCORE_ADJ_MIN,
OOM_SCORE_ADJ_MAX);
if(clampedOomScoreAdj != oomScoreAdj) {
@ -1258,14 +1226,33 @@ SetProcessPriority(int aPid, ProcessPriority aPriority)
WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
nsPrintfCString("%d", oomAdj).get());
}
} else {
LOG("Unable to read oom_score_adj pref for priority %s; "
"are the prefs messed up?",
ProcessPriorityToString(aPriority));
MOZ_ASSERT(false);
}
int32_t nice = 0;
rv = Preferences::GetInt(nsPrintfCString(
"hal.processPriorityManager.gonk.%sNice", priorityStr).get(), &nice);
if (NS_SUCCEEDED(rv)) {
HAL_LOG(("Setting nice for pid %d to %d", aPid, nice));
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
rv = Preferences::GetInt(
nsPrintfCString("hal.processPriorityManager.gonk.%s.Nice",
ProcessPriorityToString(aPriority)).get(),
&nice);
} else if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
rv = Preferences::GetInt("hal.processPriorityManager.gonk.LowCPUNice",
&nice);
} else {
LOG("Unable to read niceness pref for priority %s; "
"are the prefs messed up?",
ProcessPriorityToString(aPriority));
MOZ_ASSERT(false);
rv = NS_ERROR_FAILURE;
}
if (NS_SUCCEEDED(rv)) {
LOG("Setting nice for pid %d to %d", aPid, nice);
SetNiceForPid(aPid, nice);
}
}

118
hal/gonk/fanotify.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef _LINUX_FANOTIFY_H
#define _LINUX_FANOTIFY_H
/* This is a Linux header generated by "make headers_install" */
#include <linux/types.h>
/* the following events that user-space can register for */
#define FAN_ACCESS 0x00000001 /* File was accessed */
#define FAN_MODIFY 0x00000002 /* File was modified */
#define FAN_CLOSE_WRITE 0x00000008 /* Writtable file closed */
#define FAN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
#define FAN_OPEN 0x00000020 /* File was opened */
#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
#define FAN_ONDIR 0x40000000 /* event occurred against dir */
#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */
/* helper events */
#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
/* flags used for fanotify_init() */
#define FAN_CLOEXEC 0x00000001
#define FAN_NONBLOCK 0x00000002
/* These are NOT bitwise flags. Both bits are used togther. */
#define FAN_CLASS_NOTIF 0x00000000
#define FAN_CLASS_CONTENT 0x00000004
#define FAN_CLASS_PRE_CONTENT 0x00000008
#define FAN_ALL_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
FAN_CLASS_PRE_CONTENT)
#define FAN_UNLIMITED_QUEUE 0x00000010
#define FAN_UNLIMITED_MARKS 0x00000020
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\
FAN_UNLIMITED_MARKS)
/* flags used for fanotify_modify_mark() */
#define FAN_MARK_ADD 0x00000001
#define FAN_MARK_REMOVE 0x00000002
#define FAN_MARK_DONT_FOLLOW 0x00000004
#define FAN_MARK_ONLYDIR 0x00000008
#define FAN_MARK_MOUNT 0x00000010
#define FAN_MARK_IGNORED_MASK 0x00000020
#define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040
#define FAN_MARK_FLUSH 0x00000080
#define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\
FAN_MARK_REMOVE |\
FAN_MARK_DONT_FOLLOW |\
FAN_MARK_ONLYDIR |\
FAN_MARK_MOUNT |\
FAN_MARK_IGNORED_MASK |\
FAN_MARK_IGNORED_SURV_MODIFY |\
FAN_MARK_FLUSH)
/*
* All of the events - we build the list by hand so that we can add flags in
* the future and not break backward compatibility. Apps will get only the
* events that they originally wanted. Be sure to add new events here!
*/
#define FAN_ALL_EVENTS (FAN_ACCESS |\
FAN_MODIFY |\
FAN_CLOSE |\
FAN_OPEN)
/*
* All events which require a permission response from userspace
*/
#define FAN_ALL_PERM_EVENTS (FAN_OPEN_PERM |\
FAN_ACCESS_PERM)
#define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\
FAN_ALL_PERM_EVENTS |\
FAN_Q_OVERFLOW)
#define FANOTIFY_METADATA_VERSION 3
struct fanotify_event_metadata {
__u32 event_len;
__u8 vers;
__u8 reserved;
__u16 metadata_len;
__u64 mask;
__s32 fd;
__s32 pid;
};
struct fanotify_response {
__s32 fd;
__u32 response;
};
/* Legit userspace responses to a _PERM event */
#define FAN_ALLOW 0x01
#define FAN_DENY 0x02
/* No fd set in event */
#define FAN_NOFD -1
/* Helper functions to deal with fanotify_event_metadata buffers */
#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))
#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \
(struct fanotify_event_metadata*)(((char *)(meta)) + \
(meta)->event_len))
#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \
(long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \
(long)(meta)->event_len <= (long)(len))
#endif /* _LINUX_FANOTIFY_H */

View File

@ -64,10 +64,10 @@ struct SwitchEvent {
};
struct WakeLockInformation {
nsString topic;
uint32_t numLocks;
uint32_t numHidden;
uint64_t[] lockingProcesses;
nsString topic;
};
struct ScreenConfiguration {
@ -173,8 +173,6 @@ parent:
sync GetCurrentSwitchState(SwitchDevice aDevice)
returns (SwitchState aState);
SetProcessPriority(int aPid, ProcessPriority aPriority);
FactoryReset();
child:

View File

@ -339,9 +339,11 @@ SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
}
void
SetProcessPriority(int aPid, ProcessPriority aPriority)
SetProcessPriority(int aPid,
ProcessPriority aPriority,
ProcessCPUPriority aCPUPriority)
{
Hal()->SendSetProcessPriority(aPid, aPriority);
NS_RUNTIMEABORT("Only the main process may set processes' priorities.");
}
void
@ -407,6 +409,18 @@ FactoryReset()
Hal()->SendFactoryReset();
}
void
StartDiskSpaceWatcher()
{
NS_RUNTIMEABORT("StartDiskSpaceWatcher() can't be called from sandboxed contexts.");
}
void
StopDiskSpaceWatcher()
{
NS_RUNTIMEABORT("StopDiskSpaceWatcher() can't be called from sandboxed contexts.");
}
class HalParent : public PHalParent
, public BatteryObserver
, public NetworkObserver
@ -782,15 +796,6 @@ public:
return true;
}
virtual bool
RecvSetProcessPriority(const int& aPid, const ProcessPriority& aPriority)
{
// TODO As a security check, we should ensure that aPid is either the pid
// of our child, or the pid of one of the child's children.
hal::SetProcessPriority(aPid, aPriority);
return true;
}
void Notify(const int64_t& aClockDeltaMS)
{
unused << SendNotifySystemClockChange(aClockDeltaMS);

View File

@ -3964,9 +3964,6 @@ pref("layers.offmainthreadcomposition.animate-opacity", false);
pref("layers.offmainthreadcomposition.animate-transform", false);
pref("layers.offmainthreadcomposition.log-animations", false);
// Whether to (try) to use a Composer2D if available on this platform.
pref("layers.composer2d.enabled", false);
#ifdef MOZ_X11
#ifdef MOZ_WIDGET_GTK2
pref("gfx.xrender.enabled",true);

View File

@ -0,0 +1,158 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "DiskSpaceWatcher.h"
#include "mozilla/Hal.h"
#include "mozilla/ModuleUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/ClearOnShutdown.h"
#define NS_DISKSPACEWATCHER_CID \
{ 0xab218518, 0xf197, 0x4fb4, { 0x8b, 0x0f, 0x8b, 0xb3, 0x4d, 0xf2, 0x4b, 0xf4 } }
using namespace mozilla;
StaticRefPtr<DiskSpaceWatcher> gDiskSpaceWatcher;
NS_IMPL_ISUPPORTS2(DiskSpaceWatcher, nsIDiskSpaceWatcher, nsIObserver)
uint64_t DiskSpaceWatcher::sFreeSpace = 0;
bool DiskSpaceWatcher::sIsDiskFull = false;
DiskSpaceWatcher::DiskSpaceWatcher()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!gDiskSpaceWatcher);
}
DiskSpaceWatcher::~DiskSpaceWatcher()
{
MOZ_ASSERT(!gDiskSpaceWatcher);
}
already_AddRefed<DiskSpaceWatcher>
DiskSpaceWatcher::FactoryCreate()
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
return nullptr;
}
MOZ_ASSERT(NS_IsMainThread());
if (!Preferences::GetBool("disk_space_watcher.enabled", false)) {
return nullptr;
}
if (!gDiskSpaceWatcher) {
gDiskSpaceWatcher = new DiskSpaceWatcher();
ClearOnShutdown(&gDiskSpaceWatcher);
}
nsRefPtr<DiskSpaceWatcher> service = gDiskSpaceWatcher.get();
return service.forget();
}
NS_IMETHODIMP
DiskSpaceWatcher::Observe(nsISupports* aSubject, const char* aTopic,
const PRUnichar* aData)
{
MOZ_ASSERT(NS_IsMainThread());
if (!strcmp(aTopic, "profile-after-change")) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->AddObserver(this, "profile-before-change", false);
mozilla::hal::StartDiskSpaceWatcher();
return NS_OK;
}
if (!strcmp(aTopic, "profile-before-change")) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->RemoveObserver(this, "profile-before-change");
mozilla::hal::StopDiskSpaceWatcher();
return NS_OK;
}
MOZ_ASSERT(false, "DiskSpaceWatcher got unexpected topic!");
return NS_ERROR_UNEXPECTED;
}
/* readonly attribute bool isDiskFull; */
NS_IMETHODIMP DiskSpaceWatcher::GetIsDiskFull(bool* aIsDiskFull)
{
*aIsDiskFull = sIsDiskFull;
return NS_OK;
}
// GetFreeSpace is a macro on windows, and that messes up with the c++
// compiler.
#ifdef XP_WIN
#undef GetFreeSpace
#endif
/* readonly attribute long freeSpace; */
NS_IMETHODIMP DiskSpaceWatcher::GetFreeSpace(uint64_t* aFreeSpace)
{
*aFreeSpace = sFreeSpace;
return NS_OK;
}
// static
void DiskSpaceWatcher::UpdateState(bool aIsDiskFull, uint64_t aFreeSpace)
{
MOZ_ASSERT(NS_IsMainThread());
if (!gDiskSpaceWatcher) {
return;
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
sIsDiskFull = aIsDiskFull;
sFreeSpace = aFreeSpace;
if (!observerService) {
return;
}
const PRUnichar stateFull[] = { 'f', 'u', 'l', 'l', 0 };
const PRUnichar stateFree[] = { 'f', 'r', 'e', 'e', 0 };
nsCOMPtr<nsISupports> subject;
CallQueryInterface(gDiskSpaceWatcher.get(), getter_AddRefs(subject));
MOZ_ASSERT(subject);
observerService->NotifyObservers(subject,
DISKSPACEWATCHER_OBSERVER_TOPIC,
sIsDiskFull ? stateFull : stateFree);
return;
}
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DiskSpaceWatcher,
DiskSpaceWatcher::FactoryCreate)
NS_DEFINE_NAMED_CID(NS_DISKSPACEWATCHER_CID);
static const mozilla::Module::CIDEntry kDiskSpaceWatcherCIDs[] = {
{ &kNS_DISKSPACEWATCHER_CID, false, nullptr, DiskSpaceWatcherConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kDiskSpaceWatcherContracts[] = {
{ "@mozilla.org/toolkit/disk-space-watcher;1", &kNS_DISKSPACEWATCHER_CID },
{ nullptr }
};
static const mozilla::Module::CategoryEntry kDiskSpaceWatcherCategories[] = {
{ "profile-after-change", "Disk Space Watcher Service", DISKSPACEWATCHER_CONTRACTID },
{ nullptr }
};
static const mozilla::Module kDiskSpaceWatcherModule = {
mozilla::Module::kVersion,
kDiskSpaceWatcherCIDs,
kDiskSpaceWatcherContracts,
kDiskSpaceWatcherCategories
};
NSMODULE_DEFN(DiskSpaceWatcherModule) = &kDiskSpaceWatcherModule;

View File

@ -0,0 +1,32 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef __DISKSPACEWATCHER_H__
#include "nsIDiskSpaceWatcher.h"
#include "nsIObserver.h"
#include "nsCOMPtr.h"
class DiskSpaceWatcher MOZ_FINAL : public nsIDiskSpaceWatcher,
public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDISKSPACEWATCHER
NS_DECL_NSIOBSERVER
static already_AddRefed<DiskSpaceWatcher>
FactoryCreate();
static void UpdateState(bool aIsDiskFull, uint64_t aFreeSpace);
private:
DiskSpaceWatcher();
~DiskSpaceWatcher();
static uint64_t sFreeSpace;
static bool sIsDiskFull;
};
#endif // __DISKSPACEWATCHER_H__

View File

@ -0,0 +1,23 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# 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/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
LIBRARY_NAME = diskspacewatcher
EXPORT_LIBRARY = 1
IS_COMPONENT = 1
MODULE_NAME = DiskSpaceWatcherModule
LIBXUL_LIBRARY = 1
CPPSRCS = \
DiskSpaceWatcher.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk

View File

@ -0,0 +1,16 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# 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/.
XPIDL_SOURCES += [
'nsIDiskSpaceWatcher.idl',
]
EXPORTS += [
'DiskSpaceWatcher.h'
]
XPIDL_MODULE = 'diskspacewatcher'
MODULE = 'toolkitcomps'

View File

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "nsISupports.idl"
[scriptable, uuid(3aceba74-2ed5-4e99-8fe4-06e90e2b8ef0)]
interface nsIDiskSpaceWatcher : nsISupports
{
readonly attribute bool isDiskFull; // True if we are low on disk space.
readonly attribute unsigned long long freeSpace; // The free space currently available.
};
%{ C++
#define DISKSPACEWATCHER_CONTRACTID "@mozilla.org/toolkit/disk-space-watcher;1"
// The data for this notification will be either 'free' or 'full'.
#define DISKSPACEWATCHER_OBSERVER_TOPIC "disk-space-watcher"
%}

View File

@ -16,6 +16,7 @@ PARALLEL_DIRS += [
'console',
'contentprefs',
'cookie',
'diskspacewatcher',
'downloads',
'exthelper',
'filepicker',

View File

@ -179,6 +179,7 @@ COMPONENT_LIBS += \
storagecomps \
rdf \
windowds \
diskspacewatcher \
$(NULL)
ifdef BUILD_CTYPES

View File

@ -242,6 +242,7 @@
MODULE(jsdebugger) \
PEERCONNECTION_MODULE \
GIO_MODULE \
MODULE(DiskSpaceWatcherModule) \
/* end of list */
#define MODULE(_name) \

View File

@ -182,7 +182,10 @@ nsWindow::nsWindow()
// This has to happen after other init has finished.
gfxPlatform::GetPlatform();
sUsingOMTC = ShouldUseOffMainThreadCompositing();
sUsingHwc = Preferences::GetBool("layers.composer2d.enabled", false);
property_get("ro.display.colorfill", propValue, "0");
sUsingHwc = Preferences::GetBool("layers.composer2d.enabled",
atoi(propValue) == 1);
if (sUsingOMTC) {
sOMTCSurface = new gfxImageSurface(gfxIntSize(1, 1),