mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge birch to m-c. IGNORE BAD COMMIT MESSAGES
This commit is contained in:
commit
7f00594f07
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
105
content/html/content/test/test_video_wakelock.html
Normal file
105
content/html/content/test/test_video_wakelock.html
Normal 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>
|
BIN
content/html/content/test/wakelock.ogv
Normal file
BIN
content/html/content/test/wakelock.ogv
Normal file
Binary file not shown.
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
24
dom/bluetooth/BluetoothProfileManagerBase.h
Normal file
24
dom/bluetooth/BluetoothProfileManagerBase.h
Normal 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__
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ CPPSRCS += \
|
||||
BluetoothHfpManager.cpp \
|
||||
BluetoothOppManager.cpp \
|
||||
ObexBase.cpp \
|
||||
BluetoothScoManager.cpp \
|
||||
BluetoothUuid.cpp \
|
||||
BluetoothSocket.cpp \
|
||||
$(NULL)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
15
dom/browser-element/mochitest/priority/CAUTION
Normal file
15
dom/browser-element/mochitest/priority/CAUTION
Normal 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.
|
@ -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 \
|
||||
|
@ -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>
|
@ -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>
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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!");
|
||||
|
@ -46,7 +46,7 @@ struct GetMessageRequest
|
||||
|
||||
struct DeleteMessageRequest
|
||||
{
|
||||
int32_t messageId;
|
||||
int32_t[] messageIds;
|
||||
};
|
||||
|
||||
struct CreateMessageCursorRequest
|
||||
|
@ -48,7 +48,7 @@ struct ReplyGetMessageFail
|
||||
|
||||
struct ReplyMessageDelete
|
||||
{
|
||||
bool deleted;
|
||||
bool[] deleted;
|
||||
};
|
||||
|
||||
struct ReplyMessageDeleteFail
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]);
|
||||
},
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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();
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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++) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
93
hal/Hal.cpp
93
hal/Hal.cpp
@ -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
|
||||
|
36
hal/Hal.h
36
hal/Hal.h
@ -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
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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 #}
|
||||
|
||||
|
19
hal/fallback/FallbackDiskSpaceWatcher.cpp
Normal file
19
hal/fallback/FallbackDiskSpaceWatcher.cpp
Normal 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
|
@ -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
|
||||
|
308
hal/gonk/GonkDiskSpaceWatcher.cpp
Normal file
308
hal/gonk/GonkDiskSpaceWatcher.cpp
Normal 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
|
@ -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
118
hal/gonk/fanotify.h
Normal 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 */
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
158
toolkit/components/diskspacewatcher/DiskSpaceWatcher.cpp
Normal file
158
toolkit/components/diskspacewatcher/DiskSpaceWatcher.cpp
Normal 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;
|
32
toolkit/components/diskspacewatcher/DiskSpaceWatcher.h
Normal file
32
toolkit/components/diskspacewatcher/DiskSpaceWatcher.h
Normal 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__
|
23
toolkit/components/diskspacewatcher/Makefile.in
Normal file
23
toolkit/components/diskspacewatcher/Makefile.in
Normal 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
|
16
toolkit/components/diskspacewatcher/moz.build
Normal file
16
toolkit/components/diskspacewatcher/moz.build
Normal 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'
|
19
toolkit/components/diskspacewatcher/nsIDiskSpaceWatcher.idl
Normal file
19
toolkit/components/diskspacewatcher/nsIDiskSpaceWatcher.idl
Normal 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"
|
||||
%}
|
@ -16,6 +16,7 @@ PARALLEL_DIRS += [
|
||||
'console',
|
||||
'contentprefs',
|
||||
'cookie',
|
||||
'diskspacewatcher',
|
||||
'downloads',
|
||||
'exthelper',
|
||||
'filepicker',
|
||||
|
@ -179,6 +179,7 @@ COMPONENT_LIBS += \
|
||||
storagecomps \
|
||||
rdf \
|
||||
windowds \
|
||||
diskspacewatcher \
|
||||
$(NULL)
|
||||
|
||||
ifdef BUILD_CTYPES
|
||||
|
@ -242,6 +242,7 @@
|
||||
MODULE(jsdebugger) \
|
||||
PEERCONNECTION_MODULE \
|
||||
GIO_MODULE \
|
||||
MODULE(DiskSpaceWatcherModule) \
|
||||
/* end of list */
|
||||
|
||||
#define MODULE(_name) \
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user