Merge m-c to inbound on a CLOSED TREE.

This commit is contained in:
Ryan VanderMeulen 2013-07-22 15:23:47 -04:00
commit d5cffd69d5
15 changed files with 325 additions and 129 deletions

View File

@ -1,4 +1,4 @@
{
"revision": "e04bb3527c33dd6771e63397a3b52a4b9a5fce4e",
"revision": "f8d77075d437fa1df8929b2cdacec9f861183f4a",
"repo_path": "/integration/gaia-central"
}

View File

@ -148,7 +148,7 @@ static const char* sBluetoothDBusSignals[] =
* used by any other thread.
*
*/
static nsAutoPtr<RawDBusConnection> gThreadConnection;
static nsRefPtr<RawDBusConnection> gThreadConnection;
static nsDataHashtable<nsStringHashKey, DBusMessage* > sPairingReqTable;
static nsDataHashtable<nsStringHashKey, DBusMessage* > sAuthorizeReqTable;
static Atomic<int32_t> sIsPairing;
@ -1116,100 +1116,84 @@ handle_error:
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static const DBusObjectPathVTable agentVtable = {
NULL, AgentEventFilter, NULL, NULL, NULL, NULL
class RegisterAgentReplyHandler : public DBusReplyHandler
{
public:
RegisterAgentReplyHandler(const DBusObjectPathVTable* aAgentVTable)
: mAgentVTable(aAgentVTable)
{
MOZ_ASSERT(aAgentVTable);
}
void Handle(DBusMessage* aReply)
{
MOZ_ASSERT(!NS_IsMainThread()); // DBus thread
if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) {
return;
}
nsRefPtr<RawDBusConnection> threadConnection = gThreadConnection;
if (!threadConnection.get()) {
BT_WARNING("%s: DBus connection has been closed.", __FUNCTION__);
return;
}
// There is no "RegisterAgent" function defined in device interface.
// When we call "CreatePairedDevice", it will do device agent registration
// for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html)
if (!dbus_connection_register_object_path(threadConnection->GetConnection(),
KEY_REMOTE_AGENT,
mAgentVTable,
NULL)) {
BT_WARNING("%s: Can't register object path %s for remote device agent!",
__FUNCTION__, KEY_REMOTE_AGENT);
return;
}
NS_DispatchToMainThread(new PrepareProfileManagersRunnable());
}
private:
const DBusObjectPathVTable* mAgentVTable;
};
// Local agent means agent for Adapter, not agent for Device. Some signals
// will be passed to local agent, some will be passed to device agent.
// For example, if a remote device would like to pair with us, then the
// signal will be passed to local agent. If we start pairing process with
// calling CreatePairedDevice, we'll get signal which should be passed to
// device agent.
static bool
RegisterLocalAgent(const char* adapterPath,
const char* agentPath,
const char* capabilities)
static bool RegisterAgent(const DBusObjectPathVTable* aAgentVTable)
{
MOZ_ASSERT(!NS_IsMainThread());
const char* agentPath = KEY_LOCAL_AGENT;
const char* capabilities = B2G_AGENT_CAPABILITIES;
// Local agent means agent for Adapter, not agent for Device. Some signals
// will be passed to local agent, some will be passed to device agent.
// For example, if a remote device would like to pair with us, then the
// signal will be passed to local agent. If we start pairing process with
// calling CreatePairedDevice, we'll get signal which should be passed to
// device agent.
if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(),
agentPath,
&agentVtable,
KEY_LOCAL_AGENT,
aAgentVTable,
NULL)) {
BT_WARNING("%s: Can't register object path %s for agent!",
__FUNCTION__, agentPath);
__FUNCTION__, KEY_LOCAL_AGENT);
return false;
}
DBusMessage* msg =
dbus_message_new_method_call("org.bluez", adapterPath,
DBUS_ADAPTER_IFACE, "RegisterAgent");
if (!msg) {
BT_WARNING("%s: Can't allocate new method call for agent!", __FUNCTION__);
return false;
}
nsRefPtr<RegisterAgentReplyHandler> handler = new RegisterAgentReplyHandler(aAgentVTable);
MOZ_ASSERT(handler.get());
if (!dbus_message_append_args(msg,
DBUS_TYPE_OBJECT_PATH, &agentPath,
DBUS_TYPE_STRING, &capabilities,
DBUS_TYPE_INVALID)) {
BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
return false;
}
bool success = dbus_func_args_async(gThreadConnection->GetConnection(), -1,
RegisterAgentReplyHandler::Callback, handler.get(),
NS_ConvertUTF16toUTF8(sAdapterPath).get(),
DBUS_ADAPTER_IFACE, "RegisterAgent",
DBUS_TYPE_OBJECT_PATH, &agentPath,
DBUS_TYPE_STRING, &capabilities,
DBUS_TYPE_INVALID);
NS_ENSURE_TRUE(success, false);
DBusError err;
dbus_error_init(&err);
DBusMessage* reply =
dbus_connection_send_with_reply_and_block(gThreadConnection->GetConnection(),
msg, -1, &err);
dbus_message_unref(msg);
if (!reply) {
if (dbus_error_is_set(&err)) {
if (!strcmp(err.name, "org.bluez.Error.AlreadyExists")) {
LOG_AND_FREE_DBUS_ERROR(&err);
#ifdef DEBUG
BT_WARNING("Agent already registered, still returning true");
#endif
} else {
LOG_AND_FREE_DBUS_ERROR(&err);
BT_WARNING("%s: Can't register agent!", __FUNCTION__);
return false;
}
}
} else {
dbus_message_unref(reply);
}
dbus_connection_flush(gThreadConnection->GetConnection());
return true;
}
static bool
RegisterAgent()
{
MOZ_ASSERT(!NS_IsMainThread());
if (!RegisterLocalAgent(NS_ConvertUTF16toUTF8(sAdapterPath).get(),
KEY_LOCAL_AGENT,
B2G_AGENT_CAPABILITIES)) {
return false;
}
// There is no "RegisterAgent" function defined in device interface.
// When we call "CreatePairedDevice", it will do device agent registration
// for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html)
if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(),
KEY_REMOTE_AGENT,
&agentVtable,
NULL)) {
BT_WARNING("%s: Can't register object path %s for remote device agent!",
__FUNCTION__, KEY_REMOTE_AGENT);
return false;
}
handler.forget();
return true;
}
@ -1217,8 +1201,12 @@ RegisterAgent()
class PrepareAdapterRunnable : public nsRunnable
{
public:
nsresult Run()
NS_IMETHOD Run()
{
static const DBusObjectPathVTable sAgentVTable = {
NULL, AgentEventFilter, NULL, NULL, NULL, NULL
};
MOZ_ASSERT(!NS_IsMainThread());
nsTArray<uint32_t> uuids;
@ -1237,12 +1225,11 @@ public:
#endif
}
if(!RegisterAgent()) {
if(!RegisterAgent(&sAgentVTable)) {
NS_WARNING("Failed to register agent");
return NS_ERROR_FAILURE;
}
NS_DispatchToMainThread(new PrepareProfileManagersRunnable());
return NS_OK;
}
};

View File

@ -13,7 +13,7 @@ dictionary SmsThreadListItem
unsigned long long unreadCount;
};
[scriptable, builtinclass, uuid(e73baef1-7a9f-48c1-8b04-20d9d16c4974)]
[scriptable, builtinclass, uuid(a22d9aae-ee0a-11e2-949e-e770d0d3883f)]
interface nsIMobileMessageCallback : nsISupports
{
/**
@ -28,6 +28,7 @@ interface nsIMobileMessageCallback : nsISupports
const unsigned short INTERNAL_ERROR = 4;
const unsigned short NO_SIM_CARD_ERROR = 5;
const unsigned short RADIO_DISABLED_ERROR = 6;
const unsigned short INVALID_ADDRESS_ERROR = 7;
/**
* |message| can be nsIDOMMoz{Mms,Sms}Message.

View File

@ -91,6 +91,9 @@ MobileMessageCallback::NotifyError(int32_t aError)
case nsIMobileMessageCallback::RADIO_DISABLED_ERROR:
mDOMRequest->FireError(NS_LITERAL_STRING("RadioDisabledError"));
break;
case nsIMobileMessageCallback::INVALID_ADDRESS_ERROR:
mDOMRequest->FireError(NS_LITERAL_STRING("InvalidAddressError"));
break;
default: // SUCCESS_NO_ERROR is handled above.
MOZ_CRASH("Should never get here!");
}

View File

@ -1549,6 +1549,9 @@ MmsService.prototype = {
*
* @param aParams
* The MmsParameters dictionay object.
* @param aMessage (output)
* The database-savable message.
* Return the error code by veryfying if the |aParams| is valid or not.
*
* Notes:
*
@ -1561,13 +1564,14 @@ MmsService.prototype = {
* name-parameter of Content-Type header nor filename parameter of Content-Disposition
* header is available, Content-Location header SHALL be used if available.
*/
createSavableFromParams: function createSavableFromParams(aParams) {
createSavableFromParams: function createSavableFromParams(aParams, aMessage) {
if (DEBUG) debug("createSavableFromParams: aParams: " + JSON.stringify(aParams));
let message = {};
let isAddrValid = true;
let smil = aParams.smil;
// |message.headers|
let headers = message["headers"] = {};
// |aMessage.headers|
let headers = aMessage["headers"] = {};
let receivers = aParams.receivers;
if (receivers.length != 0) {
let headersTo = headers["to"] = [];
@ -1577,16 +1581,23 @@ MmsService.prototype = {
"from " + receivers[i] + " to " + normalizedAddress);
headersTo.push({"address": normalizedAddress, "type": "PLMN"});
// Check if the address is valid to send MMS.
if (!PhoneNumberUtils.isPlainPhoneNumber(normalizedAddress)) {
if (DEBUG) debug("Error! Address is invalid to send MMS: " +
normalizedAddress);
isAddrValid = false;
}
}
}
if (aParams.subject) {
headers["subject"] = aParams.subject;
}
// |message.parts|
// |aMessage.parts|
let attachments = aParams.attachments;
if (attachments.length != 0 || smil) {
let parts = message["parts"] = [];
let parts = aMessage["parts"] = [];
// Set the SMIL part if needed.
if (smil) {
@ -1641,22 +1652,59 @@ MmsService.prototype = {
}
// The following attributes are needed for saving message into DB.
message["type"] = "mms";
message["deliveryStatusRequested"] = true;
message["timestamp"] = Date.now();
message["receivers"] = receivers;
message["sender"] = this.getMsisdn();
aMessage["type"] = "mms";
aMessage["deliveryStatusRequested"] = true;
aMessage["timestamp"] = Date.now();
aMessage["receivers"] = receivers;
aMessage["sender"] = this.getMsisdn();
if (DEBUG) debug("createSavableFromParams: message: " + JSON.stringify(message));
return message;
if (DEBUG) debug("createSavableFromParams: aMessage: " +
JSON.stringify(aMessage));
return isAddrValid ? Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR
: Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
},
// nsIMmsService
send: function send(aParams, aRequest) {
if (DEBUG) debug("send: aParams: " + JSON.stringify(aParams));
if (aParams.receivers.length == 0) {
aRequest.notifySendMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
// Note that the following sanity checks for |aParams| should be consistent
// with the checks in SmsIPCService.GetSendMmsMessageRequestFromParams.
// Check if |aParams| is valid.
if (aParams == null || typeof aParams != "object") {
if (DEBUG) debug("Error! 'aParams' should be a non-null object.");
throw Cr.NS_ERROR_INVALID_ARG;
return;
}
// Check if |receivers| is valid.
if (!Array.isArray(aParams.receivers)) {
if (DEBUG) debug("Error! 'receivers' should be an array.");
throw Cr.NS_ERROR_INVALID_ARG;
return;
}
// Check if |subject| is valid.
if (aParams.subject != null && typeof aParams.subject != "string") {
if (DEBUG) debug("Error! 'subject' should be a string if passed.");
throw Cr.NS_ERROR_INVALID_ARG;
return;
}
// Check if |smil| is valid.
if (aParams.smil != null && typeof aParams.smil != "string") {
if (DEBUG) debug("Error! 'smil' should be a string if passed.");
throw Cr.NS_ERROR_INVALID_ARG;
return;
}
// Check if |attachments| is valid.
if (!Array.isArray(aParams.attachments)) {
if (DEBUG) debug("Error! 'attachments' should be an array.");
throw Cr.NS_ERROR_INVALID_ARG;
return;
}
@ -1683,15 +1731,13 @@ MmsService.prototype = {
if (DEBUG) debug("Marking the delivery state/staus is done. Notify sent or failed.");
// TODO bug 832140 handle !Components.isSuccessCode(aRv)
if (!isSentSuccess) {
if (DEBUG) debug("Send MMS fail. aParams.receivers = " +
JSON.stringify(aParams.receivers));
if (DEBUG) debug("Sending MMS failed.");
aRequest.notifySendMessageFailed(aErrorCode);
Services.obs.notifyObservers(aDomMessage, kSmsFailedObserverTopic, null);
return;
}
if (DEBUG) debug("Send MMS successful. aParams.receivers = " +
JSON.stringify(aParams.receivers));
if (DEBUG) debug("Sending MMS succeeded.");
// Notifying observers the MMS message is sent.
self.broadcastSentMessageEvent(aDomMessage);
@ -1701,7 +1747,8 @@ MmsService.prototype = {
});
};
let savableMessage = this.createSavableFromParams(aParams);
let savableMessage = {};
let errorCode = this.createSavableFromParams(aParams, savableMessage);
gMobileMessageDatabaseService
.saveSendingMessage(savableMessage,
function notifySendingResult(aRv, aDomMessage) {
@ -1710,6 +1757,12 @@ MmsService.prototype = {
// TODO bug 832140 handle !Components.isSuccessCode(aRv)
Services.obs.notifyObservers(aDomMessage, kSmsSendingObserverTopic, null);
if (errorCode !== Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR) {
if (DEBUG) debug("Error! The params for sending MMS are invalid.");
sendTransactionCb(aDomMessage, errorCode);
return;
}
// For radio disabled error.
if (gMmsConnection.radioDisabled) {
if (DEBUG) debug("Error! Radio is disabled when sending MMS.");
@ -1726,6 +1779,7 @@ MmsService.prototype = {
return;
}
// This is the entry point starting to send MMS.
let sendTransaction;
try {
sendTransaction = new SendTransaction(aDomMessage.id, savableMessage);

View File

@ -1230,7 +1230,7 @@ MobileMessageDatabaseService.prototype = {
saveSendingMessage: function saveSendingMessage(aMessage, aCallback) {
if ((aMessage.type != "sms" && aMessage.type != "mms") ||
(aMessage.type == "sms" && !aMessage.receiver) ||
(aMessage.type == "sms" && aMessage.receiver == undefined) ||
(aMessage.type == "mms" && !Array.isArray(aMessage.receivers)) ||
aMessage.deliveryStatusRequested == undefined ||
aMessage.timestamp == undefined) {

View File

@ -221,7 +221,7 @@ SmsIPCService::Send(const JS::Value& aParameters,
{
SendMmsMessageRequest req;
if (!GetSendMmsMessageRequestFromParams(aParameters, req)) {
return NS_ERROR_UNEXPECTED;
return NS_ERROR_INVALID_ARG;
}
return SendRequest(SendMessageRequest(req), aRequest);
}

View File

@ -34,5 +34,6 @@ qemu = true
[test_massive_incoming_delete.js]
[test_getsegmentinfofortext.js]
[test_phone_number_normalization.js]
[test_invalid_address.js]
[test_mmsmessage_attachments.js]
[test_getthreads.js]

View File

@ -0,0 +1,142 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
const MMS_MAX_LENGTH_SUBJECT = 40;
SpecialPowers.addPermission("sms", true, document);
SpecialPowers.setBoolPref("dom.sms.enabled", true);
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();
}
};
let mozMobileMessage;
function getAllMessages(callback, filter, reverse) {
if (!filter) {
filter = new MozSmsFilter;
}
let messages = [];
let request = mozMobileMessage.getMessages(filter, reverse || false);
request.onsuccess = function(event) {
if (request.result) {
messages.push(request.result);
request.continue();
return;
}
window.setTimeout(callback.bind(null, messages), 0);
}
}
function deleteAllMessages() {
getAllMessages(function deleteAll(messages) {
let message = messages.shift();
if (!message) {
ok(true, "all messages deleted");
tasks.next();
return;
}
let request = mozMobileMessage.delete(message.id);
request.onsuccess = deleteAll.bind(null, messages);
request.onerror = function (event) {
ok(false, "failed to delete all messages");
tasks.finish();
}
});
}
function testInvalidAddressForSMS(aInvalidAddr) {
log("mozMobileMessage.send(...) should get 'InvalidAddressError' error " +
"when attempting to send SMS to: " + aInvalidAddr);
let request = mozMobileMessage.send(aInvalidAddr, "Test");
request.onerror = function(event) {
log("Received 'onerror' DOMRequest event.");
let error = event.target.error;
ok(error instanceof DOMError, "should be a valid DOMError object");
ok(error.name === "InvalidAddressError", "should be 'InvalidAddressError'");
tasks.next();
};
}
function testInvalidAddressForMMS(aInvalidAddrs) {
log("mozMobileMessage.sendMMS(...) should get 'InvalidAddressError' error " +
"when attempting to send MMS to: " + aInvalidAddrs);
let request = mozMobileMessage.sendMMS({
subject: "Test",
receivers: aInvalidAddrs,
attachments: [],
});
request.onerror = function(event) {
log("Received 'onerror' DOMRequest event.");
let error = event.target.error;
ok(error instanceof DOMError, "should be a valid DOMError object");
ok(error.name === "InvalidAddressError", "should be 'InvalidAddressError'");
tasks.next();
};
}
tasks.push(function () {
log("Verifying initial state.");
mozMobileMessage = window.navigator.mozMobileMessage;
ok(mozMobileMessage instanceof MozMobileMessageManager);
tasks.next();
});
// Test sending SMS to invalid addresses.
tasks.push(testInvalidAddressForSMS.bind(this, "&%&"));
tasks.push(testInvalidAddressForSMS.bind(this, ""));
// Test sending MMS to invalid addresses.
tasks.push(testInvalidAddressForMMS.bind(this, ["&%&"]));
tasks.push(testInvalidAddressForMMS.bind(this, [""]));
tasks.push(testInvalidAddressForMMS.bind(this, ["123", "&%&", "456"]));
tasks.push(deleteAllMessages);
// WARNING: All tasks should be pushed before this!!!
tasks.push(function cleanUp() {
SpecialPowers.removePermission("sms", document);
SpecialPowers.clearUserPref("dom.sms.enabled");
finish();
});
tasks.run();

View File

@ -3156,7 +3156,11 @@ RadioInterface.prototype = {
// If the radio is disabled or the SIM card is not ready, just directly
// return with the corresponding error code.
let errorCode;
if (!this._radioEnabled) {
if (!PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
if (DEBUG) this.debug("Error! Address is invalid when sending SMS: " +
options.number);
errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
} else if (!this._radioEnabled) {
if (DEBUG) this.debug("Error! Radio is disabled when sending SMS.");
errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
} else if (this.rilContext.cardState != "ready") {
@ -3184,12 +3188,8 @@ RadioInterface.prototype = {
requestStatusReport: options.requestStatusReport
});
if (PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
this.worker.postMessage(options);
} else {
if (DEBUG) this.debug('Number ' + options.number + ' is not sendable.');
this.handleSmsSendFailed(options);
}
// This is the entry point starting to send SMS.
this.worker.postMessage(options);
}.bind(this));
},

View File

@ -308,6 +308,7 @@ public class GeckoSmsManager
public final static int kInternalError = 4;
public final static int kNoSimCardError = 5;
public final static int kRadioDisabledError = 6;
public final static int kInvalidAddressError = 7;
private final static int kMaxMessageSize = 160;

View File

@ -486,8 +486,8 @@ private:
DBusThread* mConnection;
};
static StaticAutoPtr<DBusThread> gDBusThread;
static StaticRefPtr<nsIThread> gDBusServiceThread;
static StaticRefPtr<DBusThread> gDBusThread;
static StaticRefPtr<nsIThread> gDBusServiceThread;
// Startup/Shutdown utility functions
@ -497,7 +497,7 @@ StartDBus()
MOZ_ASSERT(!NS_IsMainThread());
NS_ENSURE_TRUE(!gDBusThread, true);
nsAutoPtr<DBusThread> dbusThread(new DBusThread());
nsRefPtr<DBusThread> dbusThread(new DBusThread());
bool eventLoopStarted = dbusThread->Initialize();
NS_ENSURE_TRUE(eventLoopStarted, false);
@ -521,7 +521,7 @@ StartDBus()
rv = gDBusServiceThread->Dispatch(pollTask, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, false);
gDBusThread = dbusThread.forget();
gDBusThread = dbusThread;
return true;
}

View File

@ -11,10 +11,12 @@ using namespace mozilla::ipc;
bool RawDBusConnection::sDBusIsInit(false);
RawDBusConnection::RawDBusConnection() {
RawDBusConnection::RawDBusConnection()
{
}
RawDBusConnection::~RawDBusConnection() {
RawDBusConnection::~RawDBusConnection()
{
}
nsresult RawDBusConnection::EstablishDBusConnection()

View File

@ -14,6 +14,7 @@
#include <stdlib.h>
#include "nscore.h"
#include "mozilla/Scoped.h"
#include <mozilla/RefPtr.h>
#include <mozilla/Mutex.h>
struct DBusConnection;
@ -21,7 +22,7 @@ struct DBusConnection;
namespace mozilla {
namespace ipc {
class RawDBusConnection
class RawDBusConnection : public detail::RefCounted<RawDBusConnection, detail::AtomicRefCount>
{
struct ScopedDBusConnectionPtrTraits : ScopedFreePtrTraits<DBusConnection>
{
@ -30,7 +31,7 @@ class RawDBusConnection
public:
RawDBusConnection();
~RawDBusConnection();
virtual ~RawDBusConnection();
nsresult EstablishDBusConnection();
DBusConnection* GetConnection() {
return mConnection;

View File

@ -295,11 +295,15 @@ public class GeckoSmsManager
* dom/mobilemessage/src/Types.h
* The error code are owned by the DOM.
*/
public final static int kNoError = 0;
public final static int kNoSignalError = 1;
public final static int kNotFoundError = 2;
public final static int kUnknownError = 3;
public final static int kInternalError = 4;
public final static int kNoError = 0;
public final static int kNoSignalError = 1;
public final static int kNotFoundError = 2;
public final static int kUnknownError = 3;
public final static int kInternalError = 4;
public final static int kNoSimCardError = 5;
public final static int kRadioDisabledError = 6;
public final static int kInvalidAddressError = 7;
private final static int kMaxMessageSize = 160;