/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=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 "mozilla/dom/ContentChild.h" #include "SmsIPCService.h" #include "nsXULAppAPI.h" #include "mozilla/dom/mobilemessage/SmsChild.h" #include "nsJSUtils.h" #include "mozilla/dom/MozMobileMessageManagerBinding.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/Preferences.h" #include "nsString.h" #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/unused.h" using namespace mozilla::dom; using namespace mozilla::dom::mobilemessage; namespace { #define kPrefMmsDefaultServiceId "dom.mms.defaultServiceId" #define kPrefSmsDefaultServiceId "dom.sms.defaultServiceId" // TODO: Bug 767082 - WebSMS: sSmsChild leaks at shutdown PSmsChild* gSmsChild; // SmsIPCService is owned by nsLayoutModule. SmsIPCService* sSingleton = nullptr; PSmsChild* GetSmsChild() { MOZ_ASSERT(NS_IsMainThread()); if (!gSmsChild) { gSmsChild = ContentChild::GetSingleton()->SendPSmsConstructor(); NS_WARN_IF_FALSE(gSmsChild, "Calling methods on SmsIPCService during shutdown!"); } return gSmsChild; } nsresult SendRequest(const IPCSmsRequest& aRequest, nsIMobileMessageCallback* aRequestReply) { PSmsChild* smsChild = GetSmsChild(); NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE); SmsRequestChild* actor = new SmsRequestChild(aRequestReply); smsChild->SendPSmsRequestConstructor(actor, aRequest); return NS_OK; } nsresult SendCursorRequest(const IPCMobileMessageCursor& aRequest, nsIMobileMessageCursorCallback* aRequestReply, nsICursorContinueCallback** aResult) { PSmsChild* smsChild = GetSmsChild(); NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE); RefPtr actor = new MobileMessageCursorChild(aRequestReply); // Add an extra ref for IPDL. Will be released in // SmsChild::DeallocPMobileMessageCursor(). RefPtr actorCopy(actor); mozilla::Unused << actorCopy.forget().take(); smsChild->SendPMobileMessageCursorConstructor(actor, aRequest); actor.forget(aResult); return NS_OK; } uint32_t getDefaultServiceId(const char* aPrefKey) { static const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces"; int32_t id = mozilla::Preferences::GetInt(aPrefKey, 0); int32_t numRil = mozilla::Preferences::GetInt(kPrefRilNumRadioInterfaces, 1); if (id >= numRil || id < 0) { id = 0; } return id; } } // namespace NS_IMPL_ISUPPORTS(SmsIPCService, nsISmsService, nsIMmsService, nsIMobileMessageDatabaseService, nsIObserver) /* static */ already_AddRefed SmsIPCService::GetSingleton() { MOZ_ASSERT(NS_IsMainThread()); if (!sSingleton) { sSingleton = new SmsIPCService(); } RefPtr service = sSingleton; return service.forget(); } SmsIPCService::SmsIPCService() { static const char* kObservedPrefs[] = { kPrefMmsDefaultServiceId, kPrefSmsDefaultServiceId, nullptr }; Preferences::AddStrongObservers(this, kObservedPrefs); mMmsDefaultServiceId = getDefaultServiceId(kPrefMmsDefaultServiceId); mSmsDefaultServiceId = getDefaultServiceId(kPrefSmsDefaultServiceId); } SmsIPCService::~SmsIPCService() { sSingleton = nullptr; } /* * Implementation of nsIObserver. */ NS_IMETHODIMP SmsIPCService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { nsDependentString data(aData); if (data.EqualsLiteral(kPrefMmsDefaultServiceId)) { mMmsDefaultServiceId = getDefaultServiceId(kPrefMmsDefaultServiceId); } else if (data.EqualsLiteral(kPrefSmsDefaultServiceId)) { mSmsDefaultServiceId = getDefaultServiceId(kPrefSmsDefaultServiceId); } return NS_OK; } MOZ_ASSERT(false, "SmsIPCService got unexpected topic!"); return NS_ERROR_UNEXPECTED; } /* * Implementation of nsISmsService. */ NS_IMETHODIMP SmsIPCService::GetSmsDefaultServiceId(uint32_t* aServiceId) { *aServiceId = mSmsDefaultServiceId; return NS_OK; } NS_IMETHODIMP SmsIPCService::GetSegmentInfoForText(const nsAString& aText, nsIMobileMessageCallback* aRequest) { return SendRequest(GetSegmentInfoForTextRequest(nsString(aText)), aRequest); } NS_IMETHODIMP SmsIPCService::GetSmscAddress(uint32_t aServiceId, nsIMobileMessageCallback* aRequest) { return SendRequest(GetSmscAddressRequest(aServiceId), aRequest); } NS_IMETHODIMP SmsIPCService::SetSmscAddress(uint32_t aServiceId, const nsAString& aNumber, uint32_t aTypeOfNumber, uint32_t aNumberPlanIdentification, nsIMobileMessageCallback* aRequest) { return SendRequest(SetSmscAddressRequest(aServiceId, nsString(aNumber), aTypeOfNumber, aNumberPlanIdentification), aRequest); } NS_IMETHODIMP SmsIPCService::Send(uint32_t aServiceId, const nsAString& aNumber, const nsAString& aMessage, bool aSilent, nsIMobileMessageCallback* aRequest) { return SendRequest(SendMessageRequest(SendSmsMessageRequest(aServiceId, nsString(aNumber), nsString(aMessage), aSilent)), aRequest); } NS_IMETHODIMP SmsIPCService::AddSilentNumber(const nsAString& aNumber) { PSmsChild* smsChild = GetSmsChild(); NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE); smsChild->SendAddSilentNumber(nsString(aNumber)); return NS_OK; } NS_IMETHODIMP SmsIPCService::RemoveSilentNumber(const nsAString& aNumber) { PSmsChild* smsChild = GetSmsChild(); NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE); smsChild->SendRemoveSilentNumber(nsString(aNumber)); return NS_OK; } /* * Implementation of nsIMobileMessageDatabaseService. */ NS_IMETHODIMP SmsIPCService::GetMessageMoz(int32_t aMessageId, nsIMobileMessageCallback* aRequest) { return SendRequest(GetMessageRequest(aMessageId), aRequest); } NS_IMETHODIMP SmsIPCService::DeleteMessage(int32_t *aMessageIds, uint32_t aSize, nsIMobileMessageCallback* aRequest) { DeleteMessageRequest data; data.messageIds().AppendElements(aMessageIds, aSize); return SendRequest(data, aRequest); } NS_IMETHODIMP SmsIPCService::CreateMessageCursor(bool aHasStartDate, uint64_t aStartDate, bool aHasEndDate, uint64_t aEndDate, const char16_t** aNumbers, uint32_t aNumbersCount, const nsAString& aDelivery, bool aHasRead, bool aRead, bool aHasThreadId, uint64_t aThreadId, bool aReverse, nsIMobileMessageCursorCallback* aCursorCallback, nsICursorContinueCallback** aResult) { SmsFilterData data; data.hasStartDate() = aHasStartDate; data.startDate() = aStartDate; data.hasEndDate() = aHasEndDate; data.endDate() = aEndDate; if (aNumbersCount && aNumbers) { nsTArray& numbers = data.numbers(); uint32_t index; for (index = 0; index < aNumbersCount; index++) { numbers.AppendElement(aNumbers[index]); } } data.delivery() = aDelivery; data.hasRead() = aHasRead; data.read() = aRead; data.hasThreadId() = aHasThreadId; data.threadId() = aThreadId; return SendCursorRequest(CreateMessageCursorRequest(data, aReverse), aCursorCallback, aResult); } NS_IMETHODIMP SmsIPCService::MarkMessageRead(int32_t aMessageId, bool aValue, bool aSendReadReport, nsIMobileMessageCallback* aRequest) { return SendRequest(MarkMessageReadRequest(aMessageId, aValue, aSendReadReport), aRequest); } NS_IMETHODIMP SmsIPCService::CreateThreadCursor(nsIMobileMessageCursorCallback* aCursorCallback, nsICursorContinueCallback** aResult) { return SendCursorRequest(CreateThreadCursorRequest(), aCursorCallback, aResult); } bool GetSendMmsMessageRequestFromParams(uint32_t aServiceId, const JS::Value& aParam, SendMmsMessageRequest& request) { if (aParam.isUndefined() || aParam.isNull() || !aParam.isObject()) { return false; } mozilla::AutoJSContext cx; JS::Rooted param(cx, aParam); RootedDictionary params(cx); if (!params.Init(cx, param)) { return false; } // SendMobileMessageRequest.receivers if (!params.mReceivers.WasPassed()) { return false; } request.receivers().AppendElements(params.mReceivers.Value()); // SendMobileMessageRequest.attachments mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); if (!params.mAttachments.WasPassed()) { return false; } for (uint32_t i = 0; i < params.mAttachments.Value().Length(); i++) { mozilla::dom::MmsAttachment& attachment = params.mAttachments.Value()[i]; MmsAttachmentData mmsAttachment; mmsAttachment.id().Assign(attachment.mId); mmsAttachment.location().Assign(attachment.mLocation); mmsAttachment.contentChild() = cc->GetOrCreateActorForBlob(attachment.mContent); if (!mmsAttachment.contentChild()) { return false; } request.attachments().AppendElement(mmsAttachment); } request.smil() = params.mSmil; request.subject() = params.mSubject; // Set service ID. request.serviceId() = aServiceId; return true; } /* * Implementation of nsIMmsService. */ NS_IMETHODIMP SmsIPCService::GetMmsDefaultServiceId(uint32_t* aServiceId) { *aServiceId = mMmsDefaultServiceId; return NS_OK; } NS_IMETHODIMP SmsIPCService::Send(uint32_t aServiceId, JS::Handle aParameters, nsIMobileMessageCallback *aRequest) { SendMmsMessageRequest req; if (!GetSendMmsMessageRequestFromParams(aServiceId, aParameters, req)) { return NS_ERROR_INVALID_ARG; } return SendRequest(SendMessageRequest(req), aRequest); } NS_IMETHODIMP SmsIPCService::Retrieve(int32_t aId, nsIMobileMessageCallback *aRequest) { return SendRequest(RetrieveMessageRequest(aId), aRequest); } NS_IMETHODIMP SmsIPCService::SendReadReport(const nsAString & messageID, const nsAString & toAddress, const nsAString & iccId) { NS_ERROR("We should not be here!"); return NS_OK; }