mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-30 12:27:29 +09:00
Remove Gonk build directories
This commit is contained in:
parent
c1cd55bc25
commit
66ea8f0645
355 changed files with 15 additions and 139695 deletions
|
|
@ -1,522 +0,0 @@
|
||||||
/* 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 "MediaManager.h"
|
|
||||||
#include "MediaPermissionGonk.h"
|
|
||||||
|
|
||||||
#include "nsArray.h"
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsIContentPermissionPrompt.h"
|
|
||||||
#include "nsIDocument.h"
|
|
||||||
#include "nsIDOMNavigatorUserMedia.h"
|
|
||||||
#include "nsIStringEnumerator.h"
|
|
||||||
#include "nsJSUtils.h"
|
|
||||||
#include "nsQueryObject.h"
|
|
||||||
#include "nsPIDOMWindow.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
#include "GetUserMediaRequest.h"
|
|
||||||
#include "mozilla/dom/PBrowserChild.h"
|
|
||||||
#include "mozilla/dom/MediaStreamTrackBinding.h"
|
|
||||||
#include "mozilla/dom/MediaStreamError.h"
|
|
||||||
#include "nsISupportsPrimitives.h"
|
|
||||||
#include "nsServiceManagerUtils.h"
|
|
||||||
#include "nsArrayUtils.h"
|
|
||||||
#include "nsContentPermissionHelper.h"
|
|
||||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
|
||||||
|
|
||||||
#define AUDIO_PERMISSION_NAME "audio-capture"
|
|
||||||
#define VIDEO_PERMISSION_NAME "video-capture"
|
|
||||||
|
|
||||||
using namespace mozilla::dom;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
static MediaPermissionManager *gMediaPermMgr = nullptr;
|
|
||||||
|
|
||||||
static void
|
|
||||||
CreateDeviceNameList(nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices,
|
|
||||||
nsTArray<nsString> &aDeviceNameList)
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < aDevices.Length(); ++i) {
|
|
||||||
nsString name;
|
|
||||||
nsresult rv = aDevices[i]->GetName(name);
|
|
||||||
NS_ENSURE_SUCCESS_VOID(rv);
|
|
||||||
aDeviceNameList.AppendElement(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static already_AddRefed<nsIMediaDevice>
|
|
||||||
FindDeviceByName(nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices,
|
|
||||||
const nsAString &aDeviceName)
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < aDevices.Length(); ++i) {
|
|
||||||
nsCOMPtr<nsIMediaDevice> device = aDevices[i];
|
|
||||||
nsString deviceName;
|
|
||||||
device->GetName(deviceName);
|
|
||||||
if (deviceName.Equals(aDeviceName)) {
|
|
||||||
return device.forget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function for notifying permission granted
|
|
||||||
static nsresult
|
|
||||||
NotifyPermissionAllow(const nsAString &aCallID, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
|
|
||||||
{
|
|
||||||
nsresult rv;
|
|
||||||
nsCOMPtr<nsIMutableArray> array = nsArray::Create();
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < aDevices.Length(); ++i) {
|
|
||||||
rv = array->AppendElement(aDevices.ElementAt(i), /*weak =*/ false);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
||||||
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
return obs->NotifyObservers(array, "getUserMedia:response:allow",
|
|
||||||
aCallID.BeginReading());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function for notifying permision denial or error
|
|
||||||
static nsresult
|
|
||||||
NotifyPermissionDeny(const nsAString &aCallID, const nsAString &aErrorMsg)
|
|
||||||
{
|
|
||||||
nsresult rv;
|
|
||||||
nsCOMPtr<nsISupportsString> supportsString =
|
|
||||||
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
rv = supportsString->SetData(aErrorMsg);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
||||||
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
return obs->NotifyObservers(supportsString, "getUserMedia:response:deny",
|
|
||||||
aCallID.BeginReading());
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MediaPermissionRequest will send a prompt ipdl request to b2g process according
|
|
||||||
* to its owned type.
|
|
||||||
*/
|
|
||||||
class MediaPermissionRequest : public nsIContentPermissionRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
|
||||||
|
|
||||||
MediaPermissionRequest(RefPtr<dom::GetUserMediaRequest> &aRequest,
|
|
||||||
nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices);
|
|
||||||
|
|
||||||
already_AddRefed<nsPIDOMWindowInner> GetOwner();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~MediaPermissionRequest() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsresult DoAllow(const nsString &audioDevice, const nsString &videoDevice);
|
|
||||||
|
|
||||||
bool mAudio; // Request for audio permission
|
|
||||||
bool mVideo; // Request for video permission
|
|
||||||
RefPtr<dom::GetUserMediaRequest> mRequest;
|
|
||||||
nsTArray<nsCOMPtr<nsIMediaDevice> > mAudioDevices; // candidate audio devices
|
|
||||||
nsTArray<nsCOMPtr<nsIMediaDevice> > mVideoDevices; // candidate video devices
|
|
||||||
nsCOMPtr<nsIContentPermissionRequester> mRequester;
|
|
||||||
};
|
|
||||||
|
|
||||||
// MediaPermissionRequest
|
|
||||||
NS_IMPL_ISUPPORTS(MediaPermissionRequest, nsIContentPermissionRequest)
|
|
||||||
|
|
||||||
MediaPermissionRequest::MediaPermissionRequest(RefPtr<dom::GetUserMediaRequest> &aRequest,
|
|
||||||
nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
|
|
||||||
: mRequest(aRequest)
|
|
||||||
{
|
|
||||||
dom::MediaStreamConstraints constraints;
|
|
||||||
mRequest->GetConstraints(constraints);
|
|
||||||
|
|
||||||
mAudio = !constraints.mAudio.IsBoolean() || constraints.mAudio.GetAsBoolean();
|
|
||||||
mVideo = !constraints.mVideo.IsBoolean() || constraints.mVideo.GetAsBoolean();
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < aDevices.Length(); ++i) {
|
|
||||||
nsCOMPtr<nsIMediaDevice> device(aDevices[i]);
|
|
||||||
nsAutoString deviceType;
|
|
||||||
device->GetType(deviceType);
|
|
||||||
if (mAudio && deviceType.EqualsLiteral("audio")) {
|
|
||||||
mAudioDevices.AppendElement(device);
|
|
||||||
}
|
|
||||||
if (mVideo && deviceType.EqualsLiteral("video")) {
|
|
||||||
mVideoDevices.AppendElement(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
|
|
||||||
mRequester = new nsContentPermissionRequester(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
// nsIContentPermissionRequest methods
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaPermissionRequest::GetTypes(nsIArray** aTypes)
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
|
||||||
//XXX append device list
|
|
||||||
if (mAudio) {
|
|
||||||
nsTArray<nsString> audioDeviceNames;
|
|
||||||
CreateDeviceNameList(mAudioDevices, audioDeviceNames);
|
|
||||||
nsCOMPtr<nsISupports> AudioType =
|
|
||||||
new ContentPermissionType(NS_LITERAL_CSTRING(AUDIO_PERMISSION_NAME),
|
|
||||||
NS_LITERAL_CSTRING("unused"),
|
|
||||||
audioDeviceNames);
|
|
||||||
types->AppendElement(AudioType, false);
|
|
||||||
}
|
|
||||||
if (mVideo) {
|
|
||||||
nsTArray<nsString> videoDeviceNames;
|
|
||||||
CreateDeviceNameList(mVideoDevices, videoDeviceNames);
|
|
||||||
nsCOMPtr<nsISupports> VideoType =
|
|
||||||
new ContentPermissionType(NS_LITERAL_CSTRING(VIDEO_PERMISSION_NAME),
|
|
||||||
NS_LITERAL_CSTRING("unused"),
|
|
||||||
videoDeviceNames);
|
|
||||||
types->AppendElement(VideoType, false);
|
|
||||||
}
|
|
||||||
NS_IF_ADDREF(*aTypes = types);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
|
|
||||||
{
|
|
||||||
NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
|
|
||||||
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
|
||||||
nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID())->AsInner();
|
|
||||||
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
|
|
||||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
NS_ADDREF(*aRequestingPrincipal = doc->NodePrincipal());
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaPermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
|
|
||||||
{
|
|
||||||
NS_ENSURE_ARG_POINTER(aRequestingWindow);
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
|
||||||
nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID())->AsInner();
|
|
||||||
window.forget(aRequestingWindow);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaPermissionRequest::GetElement(nsIDOMElement** aRequestingElement)
|
|
||||||
{
|
|
||||||
NS_ENSURE_ARG_POINTER(aRequestingElement);
|
|
||||||
*aRequestingElement = nullptr;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaPermissionRequest::Cancel()
|
|
||||||
{
|
|
||||||
nsString callID;
|
|
||||||
mRequest->GetCallID(callID);
|
|
||||||
NotifyPermissionDeny(callID, NS_LITERAL_STRING("SecurityError"));
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaPermissionRequest::Allow(JS::HandleValue aChoices)
|
|
||||||
{
|
|
||||||
// check if JS object
|
|
||||||
if (!aChoices.isObject()) {
|
|
||||||
MOZ_ASSERT(false, "Not a correct format of PermissionChoice");
|
|
||||||
return NS_ERROR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
// iterate through audio-capture and video-capture
|
|
||||||
AutoJSAPI jsapi;
|
|
||||||
if (!jsapi.Init(&aChoices.toObject())) {
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
JSContext* cx = jsapi.cx();
|
|
||||||
JS::Rooted<JSObject*> obj(cx, &aChoices.toObject());
|
|
||||||
JS::Rooted<JS::Value> v(cx);
|
|
||||||
|
|
||||||
// get selected audio device name
|
|
||||||
nsString audioDevice;
|
|
||||||
if (mAudio) {
|
|
||||||
if (!JS_GetProperty(cx, obj, AUDIO_PERMISSION_NAME, &v) || !v.isString()) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
nsAutoJSString deviceName;
|
|
||||||
if (!deviceName.init(cx, v)) {
|
|
||||||
MOZ_ASSERT(false, "Couldn't initialize string from aChoices");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
audioDevice = deviceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get selected video device name
|
|
||||||
nsString videoDevice;
|
|
||||||
if (mVideo) {
|
|
||||||
if (!JS_GetProperty(cx, obj, VIDEO_PERMISSION_NAME, &v) || !v.isString()) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
nsAutoJSString deviceName;
|
|
||||||
if (!deviceName.init(cx, v)) {
|
|
||||||
MOZ_ASSERT(false, "Couldn't initialize string from aChoices");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
videoDevice = deviceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DoAllow(audioDevice, videoDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
|
|
||||||
{
|
|
||||||
NS_ENSURE_ARG_POINTER(aRequester);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
|
|
||||||
requester.forget(aRequester);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
MediaPermissionRequest::DoAllow(const nsString &audioDevice,
|
|
||||||
const nsString &videoDevice)
|
|
||||||
{
|
|
||||||
nsTArray<nsCOMPtr<nsIMediaDevice> > selectedDevices;
|
|
||||||
if (mAudio) {
|
|
||||||
nsCOMPtr<nsIMediaDevice> device =
|
|
||||||
FindDeviceByName(mAudioDevices, audioDevice);
|
|
||||||
if (device) {
|
|
||||||
selectedDevices.AppendElement(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mVideo) {
|
|
||||||
nsCOMPtr<nsIMediaDevice> device =
|
|
||||||
FindDeviceByName(mVideoDevices, videoDevice);
|
|
||||||
if (device) {
|
|
||||||
selectedDevices.AppendElement(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsString callID;
|
|
||||||
mRequest->GetCallID(callID);
|
|
||||||
return NotifyPermissionAllow(callID, selectedDevices);
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<nsPIDOMWindowInner>
|
|
||||||
MediaPermissionRequest::GetOwner()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
|
||||||
nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID())->AsInner();
|
|
||||||
return window.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success callback for MediaManager::GetUserMediaDevices().
|
|
||||||
class MediaDeviceSuccessCallback: public nsIGetUserMediaDevicesSuccessCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIGETUSERMEDIADEVICESSUCCESSCALLBACK
|
|
||||||
|
|
||||||
explicit MediaDeviceSuccessCallback(RefPtr<dom::GetUserMediaRequest> &aRequest)
|
|
||||||
: mRequest(aRequest) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~MediaDeviceSuccessCallback() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsresult DoPrompt(RefPtr<MediaPermissionRequest> &req);
|
|
||||||
RefPtr<dom::GetUserMediaRequest> mRequest;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(MediaDeviceSuccessCallback, nsIGetUserMediaDevicesSuccessCallback)
|
|
||||||
|
|
||||||
// nsIGetUserMediaDevicesSuccessCallback method
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaDeviceSuccessCallback::OnSuccess(nsIVariant* aDevices)
|
|
||||||
{
|
|
||||||
nsIID elementIID;
|
|
||||||
uint16_t elementType;
|
|
||||||
void* rawArray;
|
|
||||||
uint32_t arrayLen;
|
|
||||||
|
|
||||||
nsresult rv;
|
|
||||||
rv = aDevices->GetAsArray(&elementType, &elementIID, &arrayLen, &rawArray);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
if (elementType != nsIDataType::VTYPE_INTERFACE) {
|
|
||||||
free(rawArray);
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create array for nsIMediaDevice
|
|
||||||
nsTArray<nsCOMPtr<nsIMediaDevice> > devices;
|
|
||||||
|
|
||||||
nsISupports **supportsArray = reinterpret_cast<nsISupports **>(rawArray);
|
|
||||||
for (uint32_t i = 0; i < arrayLen; ++i) {
|
|
||||||
nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supportsArray[i]));
|
|
||||||
devices.AppendElement(device);
|
|
||||||
NS_IF_RELEASE(supportsArray[i]); // explicitly decrease reference count for raw pointer
|
|
||||||
}
|
|
||||||
free(rawArray); // explicitly free for the memory from nsIVariant::GetAsArray
|
|
||||||
|
|
||||||
// Send MediaPermissionRequest
|
|
||||||
RefPtr<MediaPermissionRequest> req = new MediaPermissionRequest(mRequest, devices);
|
|
||||||
rv = DoPrompt(req);
|
|
||||||
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger permission prompt UI
|
|
||||||
nsresult
|
|
||||||
MediaDeviceSuccessCallback::DoPrompt(RefPtr<MediaPermissionRequest> &req)
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window(req->GetOwner());
|
|
||||||
return dom::nsContentPermissionUtils::AskPermission(req, window);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error callback for MediaManager::GetUserMediaDevices()
|
|
||||||
class MediaDeviceErrorCallback: public nsIDOMGetUserMediaErrorCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIDOMGETUSERMEDIAERRORCALLBACK
|
|
||||||
|
|
||||||
explicit MediaDeviceErrorCallback(const nsAString &aCallID)
|
|
||||||
: mCallID(aCallID) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~MediaDeviceErrorCallback() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const nsString mCallID;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(MediaDeviceErrorCallback, nsIDOMGetUserMediaErrorCallback)
|
|
||||||
|
|
||||||
// nsIDOMGetUserMediaErrorCallback method
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaDeviceErrorCallback::OnError(nsISupports* aError)
|
|
||||||
{
|
|
||||||
RefPtr<MediaStreamError> error = do_QueryObject(aError);
|
|
||||||
if (!error) {
|
|
||||||
return NS_ERROR_NO_INTERFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsString name;
|
|
||||||
error->GetName(name);
|
|
||||||
return NotifyPermissionDeny(mCallID, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace anonymous
|
|
||||||
|
|
||||||
// MediaPermissionManager
|
|
||||||
NS_IMPL_ISUPPORTS(MediaPermissionManager, nsIObserver)
|
|
||||||
|
|
||||||
MediaPermissionManager*
|
|
||||||
MediaPermissionManager::GetInstance()
|
|
||||||
{
|
|
||||||
if (!gMediaPermMgr) {
|
|
||||||
gMediaPermMgr = new MediaPermissionManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
return gMediaPermMgr;
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaPermissionManager::MediaPermissionManager()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
||||||
if (obs) {
|
|
||||||
obs->AddObserver(this, "getUserMedia:request", false);
|
|
||||||
obs->AddObserver(this, "xpcom-shutdown", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaPermissionManager::~MediaPermissionManager()
|
|
||||||
{
|
|
||||||
this->Deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
MediaPermissionManager::Deinit()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
||||||
if (obs) {
|
|
||||||
obs->RemoveObserver(this, "getUserMedia:request");
|
|
||||||
obs->RemoveObserver(this, "xpcom-shutdown");
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// nsIObserver method
|
|
||||||
NS_IMETHODIMP
|
|
||||||
MediaPermissionManager::Observe(nsISupports* aSubject, const char* aTopic,
|
|
||||||
const char16_t* aData)
|
|
||||||
{
|
|
||||||
nsresult rv;
|
|
||||||
if (!strcmp(aTopic, "getUserMedia:request")) {
|
|
||||||
RefPtr<dom::GetUserMediaRequest> req =
|
|
||||||
static_cast<dom::GetUserMediaRequest*>(aSubject);
|
|
||||||
rv = HandleRequest(req);
|
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
nsString callID;
|
|
||||||
req->GetCallID(callID);
|
|
||||||
NotifyPermissionDeny(callID, NS_LITERAL_STRING("unable to enumerate media device"));
|
|
||||||
}
|
|
||||||
} else if (!strcmp(aTopic, "xpcom-shutdown")) {
|
|
||||||
rv = this->Deinit();
|
|
||||||
} else {
|
|
||||||
// not reachable
|
|
||||||
rv = NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle GetUserMediaRequest, query available media device first.
|
|
||||||
nsresult
|
|
||||||
MediaPermissionManager::HandleRequest(RefPtr<dom::GetUserMediaRequest> &req)
|
|
||||||
{
|
|
||||||
nsString callID;
|
|
||||||
req->GetCallID(callID);
|
|
||||||
uint64_t innerWindowID = req->InnerWindowID();
|
|
||||||
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
|
|
||||||
nsGlobalWindow::GetInnerWindowWithId(innerWindowID)->AsInner();
|
|
||||||
if (!innerWindow) {
|
|
||||||
MOZ_ASSERT(false, "No inner window");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess =
|
|
||||||
new MediaDeviceSuccessCallback(req);
|
|
||||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError =
|
|
||||||
new MediaDeviceErrorCallback(callID);
|
|
||||||
|
|
||||||
dom::MediaStreamConstraints constraints;
|
|
||||||
req->GetConstraints(constraints);
|
|
||||||
|
|
||||||
RefPtr<MediaManager> MediaMgr = MediaManager::GetInstance();
|
|
||||||
nsresult rv = MediaMgr->GetUserMediaDevices(innerWindow, constraints,
|
|
||||||
onSuccess, onError,
|
|
||||||
innerWindowID, callID);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
/* 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 DOM_MEDIA_MEDIAPERMISSIONGONK_H
|
|
||||||
#define DOM_MEDIA_MEDIAPERMISSIONGONK_H
|
|
||||||
|
|
||||||
#include "nsError.h"
|
|
||||||
#include "nsIObserver.h"
|
|
||||||
#include "nsISupportsImpl.h"
|
|
||||||
#include "GetUserMediaRequest.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The observer to create the MediaPermissionMgr. This is the entry point of
|
|
||||||
* permission request on b2g.
|
|
||||||
*/
|
|
||||||
class MediaPermissionManager : public nsIObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
|
|
||||||
static MediaPermissionManager* GetInstance();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~MediaPermissionManager();
|
|
||||||
|
|
||||||
private:
|
|
||||||
MediaPermissionManager();
|
|
||||||
nsresult Deinit();
|
|
||||||
nsresult HandleRequest(RefPtr<dom::GetUserMediaRequest> &req);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // DOM_MEDIA_MEDIAPERMISSIONGONK_H
|
|
||||||
|
|
||||||
|
|
@ -169,11 +169,6 @@ IPDL_SOURCES += [
|
||||||
'webrtc/PWebrtcGlobal.ipdl'
|
'webrtc/PWebrtcGlobal.ipdl'
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG['MOZ_B2G']:
|
|
||||||
EXPORTS.mozilla += [
|
|
||||||
'MediaPermissionGonk.h',
|
|
||||||
]
|
|
||||||
|
|
||||||
EXPORTS.mozilla.dom += [
|
EXPORTS.mozilla.dom += [
|
||||||
'AudioStreamTrack.h',
|
'AudioStreamTrack.h',
|
||||||
'AudioTrack.h',
|
'AudioTrack.h',
|
||||||
|
|
@ -270,11 +265,6 @@ UNIFIED_SOURCES += [
|
||||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||||
SOURCES += [ 'ThreadPoolCOMListener.cpp' ]
|
SOURCES += [ 'ThreadPoolCOMListener.cpp' ]
|
||||||
|
|
||||||
if CONFIG['MOZ_B2G']:
|
|
||||||
SOURCES += [
|
|
||||||
'MediaPermissionGonk.cpp',
|
|
||||||
]
|
|
||||||
|
|
||||||
# DecoderTraits.cpp needs to be built separately because of Mac OS X headers.
|
# DecoderTraits.cpp needs to be built separately because of Mac OS X headers.
|
||||||
SOURCES += [
|
SOURCES += [
|
||||||
'DecoderTraits.cpp',
|
'DecoderTraits.cpp',
|
||||||
|
|
@ -325,9 +315,6 @@ else:
|
||||||
if CONFIG['ANDROID_VERSION'] > '15':
|
if CONFIG['ANDROID_VERSION'] > '15':
|
||||||
DEFINES['MOZ_OMX_WEBM_DECODER'] = True
|
DEFINES['MOZ_OMX_WEBM_DECODER'] = True
|
||||||
|
|
||||||
if CONFIG['MOZ_GONK_MEDIACODEC']:
|
|
||||||
DEFINES['MOZ_GONK_MEDIACODEC'] = True
|
|
||||||
|
|
||||||
include('/ipc/chromium/chromium-config.mozbuild')
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
|
||||||
# Suppress some GCC warnings being treated as errors:
|
# Suppress some GCC warnings being treated as errors:
|
||||||
|
|
|
||||||
|
|
@ -1,268 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* 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 "MediaCodecProxy.h"
|
|
||||||
#include <OMX_IVCommon.h>
|
|
||||||
#include <gui/Surface.h>
|
|
||||||
#include <ICrypto.h>
|
|
||||||
#include "GonkAudioDecoderManager.h"
|
|
||||||
#include "MediaDecoderReader.h"
|
|
||||||
#include "VideoUtils.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
#include "mozilla/Logging.h"
|
|
||||||
#include "stagefright/MediaBuffer.h"
|
|
||||||
#include "stagefright/MetaData.h"
|
|
||||||
#include "stagefright/MediaErrors.h"
|
|
||||||
#include <stagefright/foundation/AMessage.h>
|
|
||||||
#include <stagefright/foundation/ALooper.h>
|
|
||||||
#include "media/openmax/OMX_Audio.h"
|
|
||||||
#include "MediaData.h"
|
|
||||||
#include "MediaInfo.h"
|
|
||||||
|
|
||||||
#define CODECCONFIG_TIMEOUT_US 10000LL
|
|
||||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 0LL
|
|
||||||
|
|
||||||
#include <android/log.h>
|
|
||||||
#define GADM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkAudioDecoderManager", __VA_ARGS__)
|
|
||||||
|
|
||||||
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
GonkAudioDecoderManager::GonkAudioDecoderManager(const AudioInfo& aConfig)
|
|
||||||
: mAudioChannels(aConfig.mChannels)
|
|
||||||
, mAudioRate(aConfig.mRate)
|
|
||||||
, mAudioProfile(aConfig.mProfile)
|
|
||||||
, mAudioCompactor(mAudioQueue)
|
|
||||||
{
|
|
||||||
MOZ_COUNT_CTOR(GonkAudioDecoderManager);
|
|
||||||
MOZ_ASSERT(mAudioChannels);
|
|
||||||
mCodecSpecificData = aConfig.mCodecSpecificConfig;
|
|
||||||
mMimeType = aConfig.mMimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
GonkAudioDecoderManager::~GonkAudioDecoderManager()
|
|
||||||
{
|
|
||||||
MOZ_COUNT_DTOR(GonkAudioDecoderManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<MediaDataDecoder::InitPromise>
|
|
||||||
GonkAudioDecoderManager::Init()
|
|
||||||
{
|
|
||||||
if (InitMediaCodecProxy()) {
|
|
||||||
return InitPromise::CreateAndResolve(TrackType::kAudioTrack, __func__);
|
|
||||||
} else {
|
|
||||||
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
GonkAudioDecoderManager::InitMediaCodecProxy()
|
|
||||||
{
|
|
||||||
status_t rv = OK;
|
|
||||||
if (!InitLoopers(MediaData::AUDIO_DATA)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper, mMimeType.get(), false);
|
|
||||||
if (!mDecoder.get()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mDecoder->AllocateAudioMediaCodec())
|
|
||||||
{
|
|
||||||
mDecoder = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sp<AMessage> format = new AMessage;
|
|
||||||
// Fixed values
|
|
||||||
GADM_LOG("Configure audio mime type:%s, chan no:%d, sample-rate:%d, profile:%d",
|
|
||||||
mMimeType.get(), mAudioChannels, mAudioRate, mAudioProfile);
|
|
||||||
format->setString("mime", mMimeType.get());
|
|
||||||
format->setInt32("channel-count", mAudioChannels);
|
|
||||||
format->setInt32("sample-rate", mAudioRate);
|
|
||||||
format->setInt32("aac-profile", mAudioProfile);
|
|
||||||
status_t err = mDecoder->configure(format, nullptr, nullptr, 0);
|
|
||||||
if (err != OK || !mDecoder->Prepare()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mMimeType.EqualsLiteral("audio/mp4a-latm")) {
|
|
||||||
rv = mDecoder->Input(mCodecSpecificData->Elements(), mCodecSpecificData->Length(), 0,
|
|
||||||
android::MediaCodec::BUFFER_FLAG_CODECCONFIG,
|
|
||||||
CODECCONFIG_TIMEOUT_US);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rv == OK) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
GADM_LOG("Failed to input codec specific data!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
GonkAudioDecoderManager::CreateAudioData(MediaBuffer* aBuffer, int64_t aStreamOffset)
|
|
||||||
{
|
|
||||||
if (!(aBuffer != nullptr && aBuffer->data() != nullptr)) {
|
|
||||||
GADM_LOG("Audio Buffer is not valid!");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t timeUs;
|
|
||||||
if (!aBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aBuffer->range_length() == 0) {
|
|
||||||
// Some decoders may return spurious empty buffers that we just want to ignore
|
|
||||||
// quoted from Android's AwesomePlayer.cpp
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mLastTime > timeUs) {
|
|
||||||
GADM_LOG("Output decoded sample time is revert. time=%lld", timeUs);
|
|
||||||
MOZ_ASSERT(false);
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
mLastTime = timeUs;
|
|
||||||
|
|
||||||
const uint8_t *data = static_cast<const uint8_t*>(aBuffer->data());
|
|
||||||
size_t dataOffset = aBuffer->range_offset();
|
|
||||||
size_t size = aBuffer->range_length();
|
|
||||||
|
|
||||||
uint32_t frames = size / (2 * mAudioChannels);
|
|
||||||
|
|
||||||
CheckedInt64 duration = FramesToUsecs(frames, mAudioRate);
|
|
||||||
if (!duration.isValid()) {
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef AudioCompactor::NativeCopy OmxCopy;
|
|
||||||
mAudioCompactor.Push(aStreamOffset,
|
|
||||||
timeUs,
|
|
||||||
mAudioRate,
|
|
||||||
frames,
|
|
||||||
mAudioChannels,
|
|
||||||
OmxCopy(data+dataOffset,
|
|
||||||
size,
|
|
||||||
mAudioChannels));
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
GonkAudioDecoderManager::Output(int64_t aStreamOffset,
|
|
||||||
RefPtr<MediaData>& aOutData)
|
|
||||||
{
|
|
||||||
aOutData = nullptr;
|
|
||||||
if (mAudioQueue.GetSize() > 0) {
|
|
||||||
aOutData = mAudioQueue.PopFront();
|
|
||||||
return mAudioQueue.AtEndOfStream() ? NS_ERROR_ABORT : NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t err;
|
|
||||||
MediaBuffer* audioBuffer = nullptr;
|
|
||||||
err = mDecoder->Output(&audioBuffer, READ_OUTPUT_BUFFER_TIMEOUT_US);
|
|
||||||
AutoReleaseMediaBuffer a(audioBuffer, mDecoder.get());
|
|
||||||
|
|
||||||
switch (err) {
|
|
||||||
case OK:
|
|
||||||
{
|
|
||||||
nsresult rv = CreateAudioData(audioBuffer, aStreamOffset);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case android::INFO_FORMAT_CHANGED:
|
|
||||||
{
|
|
||||||
// If the format changed, update our cached info.
|
|
||||||
GADM_LOG("Decoder format changed");
|
|
||||||
sp<AMessage> audioCodecFormat;
|
|
||||||
|
|
||||||
if (mDecoder->getOutputFormat(&audioCodecFormat) != OK ||
|
|
||||||
audioCodecFormat == nullptr) {
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t codec_channel_count = 0;
|
|
||||||
int32_t codec_sample_rate = 0;
|
|
||||||
|
|
||||||
if (!audioCodecFormat->findInt32("channel-count", &codec_channel_count) ||
|
|
||||||
!audioCodecFormat->findInt32("sample-rate", &codec_sample_rate)) {
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update AudioInfo
|
|
||||||
AudioConfig::ChannelLayout layout(codec_channel_count);
|
|
||||||
if (!layout.IsValid()) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
mAudioChannels = codec_channel_count;
|
|
||||||
mAudioRate = codec_sample_rate;
|
|
||||||
|
|
||||||
return Output(aStreamOffset, aOutData);
|
|
||||||
}
|
|
||||||
case android::INFO_OUTPUT_BUFFERS_CHANGED:
|
|
||||||
{
|
|
||||||
GADM_LOG("Info Output Buffers Changed");
|
|
||||||
if (mDecoder->UpdateOutputBuffers()) {
|
|
||||||
return Output(aStreamOffset, aOutData);
|
|
||||||
}
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
case -EAGAIN:
|
|
||||||
{
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
case android::ERROR_END_OF_STREAM:
|
|
||||||
{
|
|
||||||
GADM_LOG("Got EOS frame!");
|
|
||||||
nsresult rv = CreateAudioData(audioBuffer, aStreamOffset);
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
|
|
||||||
MOZ_ASSERT(mAudioQueue.GetSize() > 0);
|
|
||||||
mAudioQueue.Finish();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case -ETIMEDOUT:
|
|
||||||
{
|
|
||||||
GADM_LOG("Timeout. can try again next time");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
GADM_LOG("Decoder failed, err=%d", err);
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mAudioQueue.GetSize() > 0) {
|
|
||||||
aOutData = mAudioQueue.PopFront();
|
|
||||||
// Return NS_ERROR_ABORT at the last sample.
|
|
||||||
return mAudioQueue.AtEndOfStream() ? NS_ERROR_ABORT : NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkAudioDecoderManager::ProcessFlush()
|
|
||||||
{
|
|
||||||
GADM_LOG("FLUSH<<<");
|
|
||||||
mAudioQueue.Reset();
|
|
||||||
GADM_LOG(">>>FLUSH");
|
|
||||||
GonkDecoderManager::ProcessFlush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkAudioDecoderManager::ResetEOS()
|
|
||||||
{
|
|
||||||
GADM_LOG("ResetEOS(<<<");
|
|
||||||
mAudioQueue.Reset();
|
|
||||||
GADM_LOG(">>>ResetEOS(");
|
|
||||||
GonkDecoderManager::ResetEOS();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
#if !defined(GonkAudioDecoderManager_h_)
|
|
||||||
#define GonkAudioDecoderManager_h_
|
|
||||||
|
|
||||||
#include "AudioCompactor.h"
|
|
||||||
#include "mozilla/RefPtr.h"
|
|
||||||
#include "GonkMediaDataDecoder.h"
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
class MOZ_EXPORT MediaBuffer;
|
|
||||||
} // namespace android
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
class GonkAudioDecoderManager : public GonkDecoderManager {
|
|
||||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
|
||||||
public:
|
|
||||||
GonkAudioDecoderManager(const AudioInfo& aConfig);
|
|
||||||
|
|
||||||
virtual ~GonkAudioDecoderManager();
|
|
||||||
|
|
||||||
RefPtr<InitPromise> Init() override;
|
|
||||||
|
|
||||||
nsresult Output(int64_t aStreamOffset,
|
|
||||||
RefPtr<MediaData>& aOutput) override;
|
|
||||||
|
|
||||||
void ProcessFlush() override;
|
|
||||||
virtual void ResetEOS() override;
|
|
||||||
|
|
||||||
const char* GetDescriptionName() const override
|
|
||||||
{
|
|
||||||
return "gonk audio decoder";
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool InitMediaCodecProxy();
|
|
||||||
|
|
||||||
nsresult CreateAudioData(MediaBuffer* aBuffer, int64_t aStreamOffset);
|
|
||||||
|
|
||||||
uint32_t mAudioChannels;
|
|
||||||
uint32_t mAudioRate;
|
|
||||||
const uint32_t mAudioProfile;
|
|
||||||
|
|
||||||
MediaQueue<AudioData> mAudioQueue;
|
|
||||||
|
|
||||||
AudioCompactor mAudioCompactor;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // GonkAudioDecoderManager_h_
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* 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 "GonkDecoderModule.h"
|
|
||||||
#include "GonkVideoDecoderManager.h"
|
|
||||||
#include "GonkAudioDecoderManager.h"
|
|
||||||
#include "mozilla/DebugOnly.h"
|
|
||||||
#include "GonkMediaDataDecoder.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
GonkDecoderModule::GonkDecoderModule()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GonkDecoderModule::~GonkDecoderModule()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<MediaDataDecoder>
|
|
||||||
GonkDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
|
|
||||||
{
|
|
||||||
RefPtr<MediaDataDecoder> decoder =
|
|
||||||
new GonkMediaDataDecoder(new GonkVideoDecoderManager(aParams.mImageContainer, aParams.VideoConfig()),
|
|
||||||
aParams.mCallback);
|
|
||||||
return decoder.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<MediaDataDecoder>
|
|
||||||
GonkDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
|
|
||||||
{
|
|
||||||
RefPtr<MediaDataDecoder> decoder =
|
|
||||||
new GonkMediaDataDecoder(new GonkAudioDecoderManager(aParams.AudioConfig()),
|
|
||||||
aParams.mCallback);
|
|
||||||
return decoder.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformDecoderModule::ConversionRequired
|
|
||||||
GonkDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
|
|
||||||
{
|
|
||||||
if (aConfig.IsVideo()) {
|
|
||||||
return ConversionRequired::kNeedAnnexB;
|
|
||||||
} else {
|
|
||||||
return ConversionRequired::kNeedNone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
GonkDecoderModule::SupportsMimeType(const nsACString& aMimeType,
|
|
||||||
DecoderDoctorDiagnostics* aDiagnostics) const
|
|
||||||
{
|
|
||||||
return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
|
|
||||||
aMimeType.EqualsLiteral("audio/3gpp") ||
|
|
||||||
aMimeType.EqualsLiteral("audio/amr-wb") ||
|
|
||||||
aMimeType.EqualsLiteral("audio/mpeg") ||
|
|
||||||
aMimeType.EqualsLiteral("video/mp4") ||
|
|
||||||
aMimeType.EqualsLiteral("video/mp4v-es") ||
|
|
||||||
aMimeType.EqualsLiteral("video/avc") ||
|
|
||||||
aMimeType.EqualsLiteral("video/3gpp");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
#if !defined(GonkPlatformDecoderModule_h_)
|
|
||||||
#define GonkPlatformDecoderModule_h_
|
|
||||||
|
|
||||||
#include "PlatformDecoderModule.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
class GonkDecoderModule : public PlatformDecoderModule {
|
|
||||||
public:
|
|
||||||
GonkDecoderModule();
|
|
||||||
virtual ~GonkDecoderModule();
|
|
||||||
|
|
||||||
// Decode thread.
|
|
||||||
already_AddRefed<MediaDataDecoder>
|
|
||||||
CreateVideoDecoder(const CreateDecoderParams& aParams) override;
|
|
||||||
|
|
||||||
// Decode thread.
|
|
||||||
already_AddRefed<MediaDataDecoder>
|
|
||||||
CreateAudioDecoder(const CreateDecoderParams& aParams) override;
|
|
||||||
|
|
||||||
ConversionRequired
|
|
||||||
DecoderNeedsConversion(const TrackInfo& aConfig) const override;
|
|
||||||
|
|
||||||
bool SupportsMimeType(const nsACString& aMimeType,
|
|
||||||
DecoderDoctorDiagnostics* aDiagnostics) const override;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,385 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* 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 "GonkMediaDataDecoder.h"
|
|
||||||
#include "VideoUtils.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
#include "MediaCodecProxy.h"
|
|
||||||
|
|
||||||
#include <stagefright/foundation/ADebug.h>
|
|
||||||
|
|
||||||
#include "mozilla/Logging.h"
|
|
||||||
#include <android/log.h>
|
|
||||||
#define GMDD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkMediaDataDecoder", __VA_ARGS__)
|
|
||||||
#define INPUT_TIMEOUT_US 0LL // Don't wait for buffer if none is available.
|
|
||||||
#define MIN_QUEUED_SAMPLES 2
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#include <utils/AndroidThreads.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
bool
|
|
||||||
GonkDecoderManager::InitLoopers(MediaData::Type aType)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(mDecodeLooper.get() == nullptr && mTaskLooper.get() == nullptr);
|
|
||||||
MOZ_ASSERT(aType == MediaData::VIDEO_DATA || aType == MediaData::AUDIO_DATA);
|
|
||||||
|
|
||||||
const char* suffix = (aType == MediaData::VIDEO_DATA ? "video" : "audio");
|
|
||||||
mDecodeLooper = new ALooper;
|
|
||||||
android::AString name("MediaCodecProxy/");
|
|
||||||
name.append(suffix);
|
|
||||||
mDecodeLooper->setName(name.c_str());
|
|
||||||
|
|
||||||
mTaskLooper = new ALooper;
|
|
||||||
name.setTo("GonkDecoderManager/");
|
|
||||||
name.append(suffix);
|
|
||||||
mTaskLooper->setName(name.c_str());
|
|
||||||
mTaskLooper->registerHandler(this);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
sp<AMessage> findThreadId(new AMessage(kNotifyFindLooperId, id()));
|
|
||||||
findThreadId->post();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return mDecodeLooper->start() == OK && mTaskLooper->start() == OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
GonkDecoderManager::Input(MediaRawData* aSample)
|
|
||||||
{
|
|
||||||
RefPtr<MediaRawData> sample;
|
|
||||||
|
|
||||||
if (aSample) {
|
|
||||||
sample = aSample;
|
|
||||||
} else {
|
|
||||||
// It means EOS with empty sample.
|
|
||||||
sample = new MediaRawData();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
MutexAutoLock lock(mMutex);
|
|
||||||
mQueuedSamples.AppendElement(sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
sp<AMessage> input = new AMessage(kNotifyProcessInput, id());
|
|
||||||
if (!aSample) {
|
|
||||||
input->setInt32("input-eos", 1);
|
|
||||||
}
|
|
||||||
input->post();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t
|
|
||||||
GonkDecoderManager::ProcessQueuedSamples()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(OnTaskLooper());
|
|
||||||
|
|
||||||
MutexAutoLock lock(mMutex);
|
|
||||||
status_t rv;
|
|
||||||
while (mQueuedSamples.Length()) {
|
|
||||||
RefPtr<MediaRawData> data = mQueuedSamples.ElementAt(0);
|
|
||||||
rv = mDecoder->Input(reinterpret_cast<const uint8_t*>(data->Data()),
|
|
||||||
data->Size(),
|
|
||||||
data->mTime,
|
|
||||||
0,
|
|
||||||
INPUT_TIMEOUT_US);
|
|
||||||
if (rv == OK) {
|
|
||||||
mQueuedSamples.RemoveElementAt(0);
|
|
||||||
mWaitOutput.AppendElement(WaitOutputInfo(data->mOffset, data->mTime,
|
|
||||||
/* eos */ data->Data() == nullptr));
|
|
||||||
} else if (rv == -EAGAIN || rv == -ETIMEDOUT) {
|
|
||||||
// In most cases, EAGAIN or ETIMEOUT are safe because OMX can't fill
|
|
||||||
// buffer on time.
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mQueuedSamples.Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
GonkDecoderManager::Flush()
|
|
||||||
{
|
|
||||||
if (mDecoder == nullptr) {
|
|
||||||
GMDD_LOG("Decoder is not initialized");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mInitPromise.IsEmpty()) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
MutexAutoLock lock(mMutex);
|
|
||||||
mQueuedSamples.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
MonitorAutoLock lock(mFlushMonitor);
|
|
||||||
mIsFlushing = true;
|
|
||||||
sp<AMessage> flush = new AMessage(kNotifyProcessFlush, id());
|
|
||||||
flush->post();
|
|
||||||
while (mIsFlushing) {
|
|
||||||
lock.Wait();
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
GonkDecoderManager::Shutdown()
|
|
||||||
{
|
|
||||||
if (mDecoder.get()) {
|
|
||||||
mDecoder->stop();
|
|
||||||
mDecoder->ReleaseMediaResources();
|
|
||||||
mDecoder = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
GonkDecoderManager::NumQueuedSamples()
|
|
||||||
{
|
|
||||||
MutexAutoLock lock(mMutex);
|
|
||||||
return mQueuedSamples.Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkDecoderManager::ProcessInput(bool aEndOfStream)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(OnTaskLooper());
|
|
||||||
|
|
||||||
status_t rv = ProcessQueuedSamples();
|
|
||||||
if (rv >= 0) {
|
|
||||||
if (!aEndOfStream && rv <= MIN_QUEUED_SAMPLES) {
|
|
||||||
mDecodeCallback->InputExhausted();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mToDo.get() == nullptr) {
|
|
||||||
mToDo = new AMessage(kNotifyDecoderActivity, id());
|
|
||||||
if (aEndOfStream) {
|
|
||||||
mToDo->setInt32("input-eos", 1);
|
|
||||||
}
|
|
||||||
mDecoder->requestActivityNotification(mToDo);
|
|
||||||
} else if (aEndOfStream) {
|
|
||||||
mToDo->setInt32("input-eos", 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GMDD_LOG("input processed: error#%d", rv);
|
|
||||||
mDecodeCallback->Error(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
|
||||||
__func__));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkDecoderManager::ProcessFlush()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(OnTaskLooper());
|
|
||||||
|
|
||||||
mLastTime = INT64_MIN;
|
|
||||||
MonitorAutoLock lock(mFlushMonitor);
|
|
||||||
mWaitOutput.Clear();
|
|
||||||
if (mDecoder->flush() != OK) {
|
|
||||||
GMDD_LOG("flush error");
|
|
||||||
mDecodeCallback->Error(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
|
||||||
__func__));
|
|
||||||
}
|
|
||||||
mIsFlushing = false;
|
|
||||||
lock.NotifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use output timestamp to determine which output buffer is already returned
|
|
||||||
// and remove corresponding info, except for EOS, from the waiting list.
|
|
||||||
// This method handles the cases that audio decoder sends multiple output
|
|
||||||
// buffers for one input.
|
|
||||||
void
|
|
||||||
GonkDecoderManager::UpdateWaitingList(int64_t aForgetUpTo)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(OnTaskLooper());
|
|
||||||
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < mWaitOutput.Length(); i++) {
|
|
||||||
const auto& item = mWaitOutput.ElementAt(i);
|
|
||||||
if (item.mEOS || item.mTimestamp > aForgetUpTo) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i > 0) {
|
|
||||||
mWaitOutput.RemoveElementsAt(0, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkDecoderManager::ProcessToDo(bool aEndOfStream)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(OnTaskLooper());
|
|
||||||
|
|
||||||
MOZ_ASSERT(mToDo.get() != nullptr);
|
|
||||||
mToDo.clear();
|
|
||||||
|
|
||||||
if (NumQueuedSamples() > 0 && ProcessQueuedSamples() < 0) {
|
|
||||||
mDecodeCallback->Error(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
|
||||||
__func__));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (mWaitOutput.Length() > 0) {
|
|
||||||
RefPtr<MediaData> output;
|
|
||||||
WaitOutputInfo wait = mWaitOutput.ElementAt(0);
|
|
||||||
nsresult rv = Output(wait.mOffset, output);
|
|
||||||
if (rv == NS_OK) {
|
|
||||||
MOZ_ASSERT(output);
|
|
||||||
mDecodeCallback->Output(output);
|
|
||||||
UpdateWaitingList(output->mTime);
|
|
||||||
} else if (rv == NS_ERROR_ABORT) {
|
|
||||||
// EOS
|
|
||||||
MOZ_ASSERT(mQueuedSamples.IsEmpty());
|
|
||||||
if (output) {
|
|
||||||
mDecodeCallback->Output(output);
|
|
||||||
UpdateWaitingList(output->mTime);
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(mWaitOutput.Length() == 1);
|
|
||||||
mWaitOutput.RemoveElementAt(0);
|
|
||||||
mDecodeCallback->DrainComplete();
|
|
||||||
ResetEOS();
|
|
||||||
return;
|
|
||||||
} else if (rv == NS_ERROR_NOT_AVAILABLE) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
mDecodeCallback->Error(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
|
||||||
__func__));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aEndOfStream && NumQueuedSamples() <= MIN_QUEUED_SAMPLES) {
|
|
||||||
mDecodeCallback->InputExhausted();
|
|
||||||
// No need to shedule todo task this time because InputExhausted() will
|
|
||||||
// cause Input() to be invoked and do it for us.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NumQueuedSamples() || mWaitOutput.Length() > 0) {
|
|
||||||
mToDo = new AMessage(kNotifyDecoderActivity, id());
|
|
||||||
if (aEndOfStream) {
|
|
||||||
mToDo->setInt32("input-eos", 1);
|
|
||||||
}
|
|
||||||
mDecoder->requestActivityNotification(mToDo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkDecoderManager::ResetEOS()
|
|
||||||
{
|
|
||||||
// After eos, android::MediaCodec needs to be flushed to receive next input
|
|
||||||
mWaitOutput.Clear();
|
|
||||||
if (mDecoder->flush() != OK) {
|
|
||||||
GMDD_LOG("flush error");
|
|
||||||
mDecodeCallback->Error(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
|
||||||
__func__));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
|
|
||||||
{
|
|
||||||
switch (aMessage->what()) {
|
|
||||||
case kNotifyProcessInput:
|
|
||||||
{
|
|
||||||
int32_t eos = 0;
|
|
||||||
ProcessInput(aMessage->findInt32("input-eos", &eos) && eos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case kNotifyProcessFlush:
|
|
||||||
{
|
|
||||||
ProcessFlush();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case kNotifyDecoderActivity:
|
|
||||||
{
|
|
||||||
int32_t eos = 0;
|
|
||||||
ProcessToDo(aMessage->findInt32("input-eos", &eos) && eos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG
|
|
||||||
case kNotifyFindLooperId:
|
|
||||||
{
|
|
||||||
mTaskLooperId = androidGetThreadId();
|
|
||||||
MOZ_ASSERT(mTaskLooperId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
TRESPASS();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
bool
|
|
||||||
GonkDecoderManager::OnTaskLooper()
|
|
||||||
{
|
|
||||||
return androidGetThreadId() == mTaskLooperId;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
|
|
||||||
MediaDataDecoderCallback* aCallback)
|
|
||||||
: mManager(aManager)
|
|
||||||
{
|
|
||||||
MOZ_COUNT_CTOR(GonkMediaDataDecoder);
|
|
||||||
mManager->SetDecodeCallback(aCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
GonkMediaDataDecoder::~GonkMediaDataDecoder()
|
|
||||||
{
|
|
||||||
MOZ_COUNT_DTOR(GonkMediaDataDecoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<MediaDataDecoder::InitPromise>
|
|
||||||
GonkMediaDataDecoder::Init()
|
|
||||||
{
|
|
||||||
return mManager->Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkMediaDataDecoder::Shutdown()
|
|
||||||
{
|
|
||||||
mManager->Shutdown();
|
|
||||||
|
|
||||||
// Because codec allocated runnable and init promise is at reader TaskQueue,
|
|
||||||
// so manager needs to be destroyed at reader TaskQueue to prevent racing.
|
|
||||||
mManager = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserts data into the decoder's pipeline.
|
|
||||||
void
|
|
||||||
GonkMediaDataDecoder::Input(MediaRawData* aSample)
|
|
||||||
{
|
|
||||||
mManager->Input(aSample);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkMediaDataDecoder::Flush()
|
|
||||||
{
|
|
||||||
mManager->Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkMediaDataDecoder::Drain()
|
|
||||||
{
|
|
||||||
mManager->Input(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
#if !defined(GonkMediaDataDecoder_h_)
|
|
||||||
#define GonkMediaDataDecoder_h_
|
|
||||||
#include "PlatformDecoderModule.h"
|
|
||||||
#include <stagefright/foundation/AHandler.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
struct ALooper;
|
|
||||||
class MediaBuffer;
|
|
||||||
class MediaCodecProxy;
|
|
||||||
} // namespace android
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
class MediaRawData;
|
|
||||||
|
|
||||||
// Manage the data flow from inputting encoded data and outputting decode data.
|
|
||||||
class GonkDecoderManager : public android::AHandler {
|
|
||||||
public:
|
|
||||||
typedef TrackInfo::TrackType TrackType;
|
|
||||||
typedef MediaDataDecoder::InitPromise InitPromise;
|
|
||||||
|
|
||||||
virtual ~GonkDecoderManager() {}
|
|
||||||
|
|
||||||
virtual RefPtr<InitPromise> Init() = 0;
|
|
||||||
virtual const char* GetDescriptionName() const = 0;
|
|
||||||
|
|
||||||
// Asynchronously send sample into mDecoder. If out of input buffer, aSample
|
|
||||||
// will be queued for later re-send.
|
|
||||||
nsresult Input(MediaRawData* aSample);
|
|
||||||
|
|
||||||
// Flush the queued samples and signal decoder to throw all pending input/output away.
|
|
||||||
nsresult Flush();
|
|
||||||
|
|
||||||
// Shutdown decoder and rejects the init promise.
|
|
||||||
virtual nsresult Shutdown();
|
|
||||||
|
|
||||||
// How many samples are waiting for processing.
|
|
||||||
size_t NumQueuedSamples();
|
|
||||||
|
|
||||||
// Set callback for decoder events, such as requesting more input,
|
|
||||||
// returning output, or reporting error.
|
|
||||||
void SetDecodeCallback(MediaDataDecoderCallback* aCallback)
|
|
||||||
{
|
|
||||||
mDecodeCallback = aCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
GonkDecoderManager()
|
|
||||||
: mMutex("GonkDecoderManager")
|
|
||||||
, mLastTime(INT64_MIN)
|
|
||||||
, mFlushMonitor("GonkDecoderManager::Flush")
|
|
||||||
, mIsFlushing(false)
|
|
||||||
, mDecodeCallback(nullptr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool InitLoopers(MediaData::Type aType);
|
|
||||||
|
|
||||||
void onMessageReceived(const android::sp<android::AMessage> &aMessage) override;
|
|
||||||
|
|
||||||
// Produces decoded output. It returns NS_OK on success, or NS_ERROR_NOT_AVAILABLE
|
|
||||||
// when output is not produced yet.
|
|
||||||
// If this returns a failure code other than NS_ERROR_NOT_AVAILABLE, an error
|
|
||||||
// will be reported through mDecodeCallback.
|
|
||||||
virtual nsresult Output(int64_t aStreamOffset,
|
|
||||||
RefPtr<MediaData>& aOutput) = 0;
|
|
||||||
|
|
||||||
// Send queued samples to OMX. It returns how many samples are still in
|
|
||||||
// queue after processing, or negative error code if failed.
|
|
||||||
int32_t ProcessQueuedSamples();
|
|
||||||
|
|
||||||
void ProcessInput(bool aEndOfStream);
|
|
||||||
virtual void ProcessFlush();
|
|
||||||
void ProcessToDo(bool aEndOfStream);
|
|
||||||
virtual void ResetEOS();
|
|
||||||
|
|
||||||
RefPtr<MediaByteBuffer> mCodecSpecificData;
|
|
||||||
|
|
||||||
nsAutoCString mMimeType;
|
|
||||||
|
|
||||||
// MediaCodedc's wrapper that performs the decoding.
|
|
||||||
android::sp<android::MediaCodecProxy> mDecoder;
|
|
||||||
// Looper for mDecoder to run on.
|
|
||||||
android::sp<android::ALooper> mDecodeLooper;
|
|
||||||
// Looper to run decode tasks such as processing input, output, flush, and
|
|
||||||
// recycling output buffers.
|
|
||||||
android::sp<android::ALooper> mTaskLooper;
|
|
||||||
// Message codes for tasks running on mTaskLooper.
|
|
||||||
enum {
|
|
||||||
// Decoder will send this to indicate internal state change such as input or
|
|
||||||
// output buffers availability. Used to run pending input & output tasks.
|
|
||||||
kNotifyDecoderActivity = 'nda ',
|
|
||||||
// Signal the decoder to flush.
|
|
||||||
kNotifyProcessFlush = 'npf ',
|
|
||||||
// Used to process queued samples when there is new input.
|
|
||||||
kNotifyProcessInput = 'npi ',
|
|
||||||
#ifdef DEBUG
|
|
||||||
kNotifyFindLooperId = 'nfli',
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
MozPromiseHolder<InitPromise> mInitPromise;
|
|
||||||
|
|
||||||
Mutex mMutex; // Protects mQueuedSamples.
|
|
||||||
// A queue that stores the samples waiting to be sent to mDecoder.
|
|
||||||
// Empty element means EOS and there shouldn't be any sample be queued after it.
|
|
||||||
// Samples are queued in caller's thread and dequeued in mTaskLooper.
|
|
||||||
nsTArray<RefPtr<MediaRawData>> mQueuedSamples;
|
|
||||||
|
|
||||||
// The last decoded frame presentation time. Only accessed on mTaskLooper.
|
|
||||||
int64_t mLastTime;
|
|
||||||
|
|
||||||
Monitor mFlushMonitor; // Waits for flushing to complete.
|
|
||||||
bool mIsFlushing; // Protected by mFlushMonitor.
|
|
||||||
|
|
||||||
// Remembers the notification that is currently waiting for the decoder event
|
|
||||||
// to avoid requesting more than one notification at the time, which is
|
|
||||||
// forbidden by mDecoder.
|
|
||||||
android::sp<android::AMessage> mToDo;
|
|
||||||
|
|
||||||
// Stores sample info for output buffer processing later.
|
|
||||||
struct WaitOutputInfo {
|
|
||||||
WaitOutputInfo(int64_t aOffset, int64_t aTimestamp, bool aEOS)
|
|
||||||
: mOffset(aOffset)
|
|
||||||
, mTimestamp(aTimestamp)
|
|
||||||
, mEOS(aEOS)
|
|
||||||
{}
|
|
||||||
const int64_t mOffset;
|
|
||||||
const int64_t mTimestamp;
|
|
||||||
const bool mEOS;
|
|
||||||
};
|
|
||||||
|
|
||||||
nsTArray<WaitOutputInfo> mWaitOutput;
|
|
||||||
|
|
||||||
MediaDataDecoderCallback* mDecodeCallback; // Reports decoder output or error.
|
|
||||||
|
|
||||||
private:
|
|
||||||
void UpdateWaitingList(int64_t aForgetUpTo);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
typedef void* LooperId;
|
|
||||||
|
|
||||||
bool OnTaskLooper();
|
|
||||||
LooperId mTaskLooperId;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
class AutoReleaseMediaBuffer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AutoReleaseMediaBuffer(android::MediaBuffer* aBuffer, android::MediaCodecProxy* aCodec)
|
|
||||||
: mBuffer(aBuffer)
|
|
||||||
, mCodec(aCodec)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~AutoReleaseMediaBuffer()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(mCodec.get());
|
|
||||||
if (mBuffer) {
|
|
||||||
mCodec->ReleaseMediaBuffer(mBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
android::MediaBuffer* forget()
|
|
||||||
{
|
|
||||||
android::MediaBuffer* tmp = mBuffer;
|
|
||||||
mBuffer = nullptr;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
android::MediaBuffer* mBuffer;
|
|
||||||
android::sp<android::MediaCodecProxy> mCodec;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Samples are decoded using the GonkDecoder (MediaCodec)
|
|
||||||
// created by the GonkDecoderManager. This class implements
|
|
||||||
// the higher-level logic that drives mapping the Gonk to the async
|
|
||||||
// MediaDataDecoder interface. The specifics of decoding the exact stream
|
|
||||||
// type are handled by GonkDecoderManager and the GonkDecoder it creates.
|
|
||||||
class GonkMediaDataDecoder : public MediaDataDecoder {
|
|
||||||
public:
|
|
||||||
GonkMediaDataDecoder(GonkDecoderManager* aDecoderManager,
|
|
||||||
MediaDataDecoderCallback* aCallback);
|
|
||||||
|
|
||||||
~GonkMediaDataDecoder();
|
|
||||||
|
|
||||||
RefPtr<InitPromise> Init() override;
|
|
||||||
|
|
||||||
void Input(MediaRawData* aSample) override;
|
|
||||||
|
|
||||||
void Flush() override;
|
|
||||||
|
|
||||||
void Drain() override;
|
|
||||||
|
|
||||||
void Shutdown() override;
|
|
||||||
|
|
||||||
const char* GetDescriptionName() const override
|
|
||||||
{
|
|
||||||
return "gonk decoder";
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
android::sp<GonkDecoderManager> mManager;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // GonkMediaDataDecoder_h_
|
|
||||||
|
|
@ -1,772 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* 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 "MediaCodecProxy.h"
|
|
||||||
#include <OMX_IVCommon.h>
|
|
||||||
#include <gui/Surface.h>
|
|
||||||
#include <ICrypto.h>
|
|
||||||
#include "GonkVideoDecoderManager.h"
|
|
||||||
#include "GrallocImages.h"
|
|
||||||
#include "MediaDecoderReader.h"
|
|
||||||
#include "ImageContainer.h"
|
|
||||||
#include "VideoUtils.h"
|
|
||||||
#include "nsThreadUtils.h"
|
|
||||||
#include "Layers.h"
|
|
||||||
#include "mozilla/Logging.h"
|
|
||||||
#include <stagefright/MediaBuffer.h>
|
|
||||||
#include <stagefright/MetaData.h>
|
|
||||||
#include <stagefright/MediaErrors.h>
|
|
||||||
#include <stagefright/foundation/AString.h>
|
|
||||||
#include "GonkNativeWindow.h"
|
|
||||||
#include "mozilla/layers/GrallocTextureClient.h"
|
|
||||||
#include "mozilla/layers/ImageBridgeChild.h"
|
|
||||||
#include "mozilla/layers/TextureClient.h"
|
|
||||||
#include "mozilla/layers/TextureClientRecycleAllocator.h"
|
|
||||||
#include <cutils/properties.h>
|
|
||||||
|
|
||||||
#define CODECCONFIG_TIMEOUT_US 10000LL
|
|
||||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 0LL
|
|
||||||
|
|
||||||
#include <android/log.h>
|
|
||||||
#define GVDM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkVideoDecoderManager", __VA_ARGS__)
|
|
||||||
|
|
||||||
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
||||||
using namespace mozilla::layers;
|
|
||||||
using namespace android;
|
|
||||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
class GonkTextureClientAllocationHelper : public layers::ITextureClientAllocationHelper
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GonkTextureClientAllocationHelper(uint32_t aGrallocFormat,
|
|
||||||
gfx::IntSize aSize)
|
|
||||||
: ITextureClientAllocationHelper(gfx::SurfaceFormat::UNKNOWN,
|
|
||||||
aSize,
|
|
||||||
BackendSelector::Content,
|
|
||||||
TextureFlags::DEALLOCATE_CLIENT,
|
|
||||||
ALLOC_DISALLOW_BUFFERTEXTURECLIENT)
|
|
||||||
, mGrallocFormat(aGrallocFormat)
|
|
||||||
{}
|
|
||||||
|
|
||||||
already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override
|
|
||||||
{
|
|
||||||
uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN |
|
|
||||||
android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
|
|
||||||
android::GraphicBuffer::USAGE_HW_TEXTURE;
|
|
||||||
|
|
||||||
GrallocTextureData* texData = GrallocTextureData::Create(mSize, mGrallocFormat,
|
|
||||||
gfx::BackendType::NONE,
|
|
||||||
usage, aAllocator->GetTextureForwarder());
|
|
||||||
if (!texData) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();
|
|
||||||
if (!graphicBuffer.get()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
RefPtr<TextureClient> textureClient =
|
|
||||||
TextureClient::CreateWithData(texData, TextureFlags::DEALLOCATE_CLIENT, aAllocator->GetTextureForwarder());
|
|
||||||
return textureClient.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsCompatible(TextureClient* aTextureClient) override
|
|
||||||
{
|
|
||||||
if (!aTextureClient) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sp<GraphicBuffer> graphicBuffer =
|
|
||||||
static_cast<GrallocTextureData*>(aTextureClient->GetInternalData())->GetGraphicBuffer();
|
|
||||||
if (!graphicBuffer.get() ||
|
|
||||||
static_cast<uint32_t>(graphicBuffer->getPixelFormat()) != mGrallocFormat ||
|
|
||||||
aTextureClient->GetSize() != mSize) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t mGrallocFormat;
|
|
||||||
};
|
|
||||||
|
|
||||||
GonkVideoDecoderManager::GonkVideoDecoderManager(
|
|
||||||
mozilla::layers::ImageContainer* aImageContainer,
|
|
||||||
const VideoInfo& aConfig)
|
|
||||||
: mConfig(aConfig)
|
|
||||||
, mImageContainer(aImageContainer)
|
|
||||||
, mColorConverterBufferSize(0)
|
|
||||||
, mPendingReleaseItemsLock("GonkVideoDecoderManager::mPendingReleaseItemsLock")
|
|
||||||
, mNeedsCopyBuffer(false)
|
|
||||||
{
|
|
||||||
MOZ_COUNT_CTOR(GonkVideoDecoderManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
GonkVideoDecoderManager::~GonkVideoDecoderManager()
|
|
||||||
{
|
|
||||||
MOZ_COUNT_DTOR(GonkVideoDecoderManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
GonkVideoDecoderManager::Shutdown()
|
|
||||||
{
|
|
||||||
mVideoCodecRequest.DisconnectIfExists();
|
|
||||||
return GonkDecoderManager::Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<MediaDataDecoder::InitPromise>
|
|
||||||
GonkVideoDecoderManager::Init()
|
|
||||||
{
|
|
||||||
mNeedsCopyBuffer = false;
|
|
||||||
|
|
||||||
uint32_t maxWidth, maxHeight;
|
|
||||||
char propValue[PROPERTY_VALUE_MAX];
|
|
||||||
property_get("ro.moz.omx.hw.max_width", propValue, "-1");
|
|
||||||
maxWidth = -1 == atoi(propValue) ? MAX_VIDEO_WIDTH : atoi(propValue);
|
|
||||||
property_get("ro.moz.omx.hw.max_height", propValue, "-1");
|
|
||||||
maxHeight = -1 == atoi(propValue) ? MAX_VIDEO_HEIGHT : atoi(propValue) ;
|
|
||||||
|
|
||||||
if (uint32_t(mConfig.mImage.width * mConfig.mImage.height) > maxWidth * maxHeight) {
|
|
||||||
GVDM_LOG("Video resolution exceeds hw codec capability");
|
|
||||||
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the container-reported frame and pictureRect sizes. This ensures
|
|
||||||
// that our video frame creation code doesn't overflow.
|
|
||||||
if (!IsValidVideoRegion(mConfig.mImage, mConfig.ImageRect(), mConfig.mDisplay)) {
|
|
||||||
GVDM_LOG("It is not a valid region");
|
|
||||||
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
mReaderTaskQueue = AbstractThread::GetCurrent()->AsTaskQueue();
|
|
||||||
MOZ_ASSERT(mReaderTaskQueue);
|
|
||||||
|
|
||||||
if (mDecodeLooper.get() != nullptr) {
|
|
||||||
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!InitLoopers(MediaData::VIDEO_DATA)) {
|
|
||||||
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
|
|
||||||
android::sp<GonkVideoDecoderManager> self = this;
|
|
||||||
mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper,
|
|
||||||
mConfig.mMimeType.get(),
|
|
||||||
false);
|
|
||||||
|
|
||||||
uint32_t capability = MediaCodecProxy::kEmptyCapability;
|
|
||||||
if (mDecoder->getCapability(&capability) == OK && (capability &
|
|
||||||
MediaCodecProxy::kCanExposeGraphicBuffer)) {
|
|
||||||
#if ANDROID_VERSION >= 21
|
|
||||||
sp<IGonkGraphicBufferConsumer> consumer;
|
|
||||||
GonkBufferQueue::createBufferQueue(&mGraphicBufferProducer, &consumer);
|
|
||||||
mNativeWindow = new GonkNativeWindow(consumer);
|
|
||||||
#else
|
|
||||||
mNativeWindow = new GonkNativeWindow();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
mVideoCodecRequest.Begin(mDecoder->AsyncAllocateVideoMediaCodec()
|
|
||||||
->Then(mReaderTaskQueue, __func__,
|
|
||||||
[self] (bool) -> void {
|
|
||||||
self->mVideoCodecRequest.Complete();
|
|
||||||
self->codecReserved();
|
|
||||||
}, [self] (bool) -> void {
|
|
||||||
self->mVideoCodecRequest.Complete();
|
|
||||||
self->codecCanceled();
|
|
||||||
}));
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
GonkVideoDecoderManager::CreateVideoData(MediaBuffer* aBuffer,
|
|
||||||
int64_t aStreamOffset,
|
|
||||||
VideoData **v)
|
|
||||||
{
|
|
||||||
*v = nullptr;
|
|
||||||
RefPtr<VideoData> data;
|
|
||||||
int64_t timeUs;
|
|
||||||
int32_t keyFrame;
|
|
||||||
|
|
||||||
if (aBuffer == nullptr) {
|
|
||||||
GVDM_LOG("Video Buffer is not valid!");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoReleaseMediaBuffer autoRelease(aBuffer, mDecoder.get());
|
|
||||||
|
|
||||||
if (!aBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
|
|
||||||
GVDM_LOG("Decoder did not return frame time");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mLastTime > timeUs) {
|
|
||||||
GVDM_LOG("Output decoded sample time is revert. time=%lld", timeUs);
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
mLastTime = timeUs;
|
|
||||||
|
|
||||||
if (aBuffer->range_length() == 0) {
|
|
||||||
// Some decoders may return spurious empty buffers that we just want to ignore
|
|
||||||
// quoted from Android's AwesomePlayer.cpp
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) {
|
|
||||||
keyFrame = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::IntRect picture =
|
|
||||||
mConfig.ScaledImageRect(mFrameInfo.mWidth, mFrameInfo.mHeight);
|
|
||||||
if (aBuffer->graphicBuffer().get()) {
|
|
||||||
data = CreateVideoDataFromGraphicBuffer(aBuffer, picture);
|
|
||||||
if (data && !mNeedsCopyBuffer) {
|
|
||||||
// RecycleCallback() will be responsible for release the buffer.
|
|
||||||
autoRelease.forget();
|
|
||||||
}
|
|
||||||
mNeedsCopyBuffer = false;
|
|
||||||
} else {
|
|
||||||
data = CreateVideoDataFromDataBuffer(aBuffer, picture);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
// Fill necessary info.
|
|
||||||
data->mOffset = aStreamOffset;
|
|
||||||
data->mTime = timeUs;
|
|
||||||
data->mKeyframe = keyFrame;
|
|
||||||
|
|
||||||
data.forget(v);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy pixels from one planar YUV to another.
|
|
||||||
static void
|
|
||||||
CopyYUV(PlanarYCbCrData& aSource, PlanarYCbCrData& aDestination)
|
|
||||||
{
|
|
||||||
// Fill Y plane.
|
|
||||||
uint8_t* srcY = aSource.mYChannel;
|
|
||||||
gfx::IntSize ySize = aSource.mYSize;
|
|
||||||
uint8_t* destY = aDestination.mYChannel;
|
|
||||||
// Y plane.
|
|
||||||
for (int i = 0; i < ySize.height; i++) {
|
|
||||||
memcpy(destY, srcY, ySize.width);
|
|
||||||
srcY += aSource.mYStride;
|
|
||||||
destY += aDestination.mYStride;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill UV plane.
|
|
||||||
// Line start
|
|
||||||
uint8_t* srcU = aSource.mCbChannel;
|
|
||||||
uint8_t* srcV = aSource.mCrChannel;
|
|
||||||
uint8_t* destU = aDestination.mCbChannel;
|
|
||||||
uint8_t* destV = aDestination.mCrChannel;
|
|
||||||
|
|
||||||
gfx::IntSize uvSize = aSource.mCbCrSize;
|
|
||||||
for (int i = 0; i < uvSize.height; i++) {
|
|
||||||
uint8_t* su = srcU;
|
|
||||||
uint8_t* sv = srcV;
|
|
||||||
uint8_t* du = destU;
|
|
||||||
uint8_t* dv =destV;
|
|
||||||
for (int j = 0; j < uvSize.width; j++) {
|
|
||||||
*du++ = *su++;
|
|
||||||
*dv++ = *sv++;
|
|
||||||
// Move to next pixel.
|
|
||||||
su += aSource.mCbSkip;
|
|
||||||
sv += aSource.mCrSkip;
|
|
||||||
du += aDestination.mCbSkip;
|
|
||||||
dv += aDestination.mCrSkip;
|
|
||||||
}
|
|
||||||
// Move to next line.
|
|
||||||
srcU += aSource.mCbCrStride;
|
|
||||||
srcV += aSource.mCbCrStride;
|
|
||||||
destU += aDestination.mCbCrStride;
|
|
||||||
destV += aDestination.mCbCrStride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static int
|
|
||||||
Align(int aX, int aAlign)
|
|
||||||
{
|
|
||||||
return (aX + aAlign - 1) & ~(aAlign - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Venus formats are doucmented in kernel/include/media/msm_media_info.h:
|
|
||||||
// * Y_Stride : Width aligned to 128
|
|
||||||
// * UV_Stride : Width aligned to 128
|
|
||||||
// * Y_Scanlines: Height aligned to 32
|
|
||||||
// * UV_Scanlines: Height/2 aligned to 16
|
|
||||||
// * Total size = align((Y_Stride * Y_Scanlines
|
|
||||||
// * + UV_Stride * UV_Scanlines + 4096), 4096)
|
|
||||||
static void
|
|
||||||
CopyVenus(uint8_t* aSrc, uint8_t* aDest, uint32_t aWidth, uint32_t aHeight)
|
|
||||||
{
|
|
||||||
size_t yStride = Align(aWidth, 128);
|
|
||||||
uint8_t* s = aSrc;
|
|
||||||
uint8_t* d = aDest;
|
|
||||||
for (size_t i = 0; i < aHeight; i++) {
|
|
||||||
memcpy(d, s, aWidth);
|
|
||||||
s += yStride;
|
|
||||||
d += yStride;
|
|
||||||
}
|
|
||||||
size_t uvStride = yStride;
|
|
||||||
size_t uvLines = (aHeight + 1) / 2;
|
|
||||||
size_t ySize = yStride * Align(aHeight, 32);
|
|
||||||
s = aSrc + ySize;
|
|
||||||
d = aDest + ySize;
|
|
||||||
for (size_t i = 0; i < uvLines; i++) {
|
|
||||||
memcpy(d, s, aWidth);
|
|
||||||
s += uvStride;
|
|
||||||
d += uvStride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
CopyGraphicBuffer(sp<GraphicBuffer>& aSource, sp<GraphicBuffer>& aDestination)
|
|
||||||
{
|
|
||||||
void* srcPtr = nullptr;
|
|
||||||
aSource->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &srcPtr);
|
|
||||||
void* destPtr = nullptr;
|
|
||||||
aDestination->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &destPtr);
|
|
||||||
MOZ_ASSERT(srcPtr && destPtr);
|
|
||||||
|
|
||||||
// Build PlanarYCbCrData for source buffer.
|
|
||||||
PlanarYCbCrData srcData;
|
|
||||||
switch (aSource->getPixelFormat()) {
|
|
||||||
case HAL_PIXEL_FORMAT_YV12: {
|
|
||||||
// Android YV12 format is defined in system/core/include/system/graphics.h
|
|
||||||
srcData.mYChannel = static_cast<uint8_t*>(srcPtr);
|
|
||||||
srcData.mYSkip = 0;
|
|
||||||
srcData.mYSize.width = aSource->getWidth();
|
|
||||||
srcData.mYSize.height = aSource->getHeight();
|
|
||||||
srcData.mYStride = aSource->getStride();
|
|
||||||
// 4:2:0.
|
|
||||||
srcData.mCbCrSize.width = srcData.mYSize.width / 2;
|
|
||||||
srcData.mCbCrSize.height = srcData.mYSize.height / 2;
|
|
||||||
srcData.mCrChannel = srcData.mYChannel + (srcData.mYStride * srcData.mYSize.height);
|
|
||||||
// Aligned to 16 bytes boundary.
|
|
||||||
srcData.mCbCrStride = Align(srcData.mYStride / 2, 16);
|
|
||||||
srcData.mCrSkip = 0;
|
|
||||||
srcData.mCbChannel = srcData.mCrChannel + (srcData.mCbCrStride * srcData.mCbCrSize.height);
|
|
||||||
srcData.mCbSkip = 0;
|
|
||||||
|
|
||||||
// Build PlanarYCbCrData for destination buffer.
|
|
||||||
PlanarYCbCrData destData;
|
|
||||||
destData.mYChannel = static_cast<uint8_t*>(destPtr);
|
|
||||||
destData.mYSkip = 0;
|
|
||||||
destData.mYSize.width = aDestination->getWidth();
|
|
||||||
destData.mYSize.height = aDestination->getHeight();
|
|
||||||
destData.mYStride = aDestination->getStride();
|
|
||||||
// 4:2:0.
|
|
||||||
destData.mCbCrSize.width = destData.mYSize.width / 2;
|
|
||||||
destData.mCbCrSize.height = destData.mYSize.height / 2;
|
|
||||||
destData.mCrChannel = destData.mYChannel + (destData.mYStride * destData.mYSize.height);
|
|
||||||
// Aligned to 16 bytes boundary.
|
|
||||||
destData.mCbCrStride = Align(destData.mYStride / 2, 16);
|
|
||||||
destData.mCrSkip = 0;
|
|
||||||
destData.mCbChannel = destData.mCrChannel + (destData.mCbCrStride * destData.mCbCrSize.height);
|
|
||||||
destData.mCbSkip = 0;
|
|
||||||
|
|
||||||
CopyYUV(srcData, destData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
|
|
||||||
CopyVenus(static_cast<uint8_t*>(srcPtr),
|
|
||||||
static_cast<uint8_t*>(destPtr),
|
|
||||||
aSource->getWidth(),
|
|
||||||
aSource->getHeight());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
NS_ERROR("Unsupported input gralloc image type. Should never be here.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
aSource->unlock();
|
|
||||||
aDestination->unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<VideoData>
|
|
||||||
GonkVideoDecoderManager::CreateVideoDataFromGraphicBuffer(MediaBuffer* aSource,
|
|
||||||
gfx::IntRect& aPicture)
|
|
||||||
{
|
|
||||||
sp<GraphicBuffer> srcBuffer(aSource->graphicBuffer());
|
|
||||||
RefPtr<TextureClient> textureClient;
|
|
||||||
|
|
||||||
if (mNeedsCopyBuffer) {
|
|
||||||
// Copy buffer contents for bug 1199809.
|
|
||||||
if (!mCopyAllocator) {
|
|
||||||
RefPtr<layers::ImageBridgeChild> bridge = layers::ImageBridgeChild::GetSingleton();
|
|
||||||
mCopyAllocator = new TextureClientRecycleAllocator(bridge);
|
|
||||||
}
|
|
||||||
if (!mCopyAllocator) {
|
|
||||||
GVDM_LOG("Create buffer allocator failed!");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::IntSize size(srcBuffer->getWidth(), srcBuffer->getHeight());
|
|
||||||
GonkTextureClientAllocationHelper helper(srcBuffer->getPixelFormat(), size);
|
|
||||||
textureClient = mCopyAllocator->CreateOrRecycle(helper);
|
|
||||||
if (!textureClient) {
|
|
||||||
GVDM_LOG("Copy buffer allocation failed!");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
sp<GraphicBuffer> destBuffer =
|
|
||||||
static_cast<GrallocTextureData*>(textureClient->GetInternalData())->GetGraphicBuffer();
|
|
||||||
|
|
||||||
CopyGraphicBuffer(srcBuffer, destBuffer);
|
|
||||||
} else {
|
|
||||||
textureClient = mNativeWindow->getTextureClientFromBuffer(srcBuffer.get());
|
|
||||||
textureClient->SetRecycleCallback(GonkVideoDecoderManager::RecycleCallback, this);
|
|
||||||
static_cast<GrallocTextureData*>(textureClient->GetInternalData())->SetMediaBuffer(aSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<VideoData> data =
|
|
||||||
VideoData::CreateAndCopyIntoTextureClient(mConfig,
|
|
||||||
0, // Filled later by caller.
|
|
||||||
0, // Filled later by caller.
|
|
||||||
1, // No way to pass sample duration from muxer to
|
|
||||||
// OMX codec, so we hardcode the duration here.
|
|
||||||
textureClient,
|
|
||||||
false, // Filled later by caller.
|
|
||||||
-1,
|
|
||||||
aPicture);
|
|
||||||
return data.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<VideoData>
|
|
||||||
GonkVideoDecoderManager::CreateVideoDataFromDataBuffer(MediaBuffer* aSource, gfx::IntRect& aPicture)
|
|
||||||
{
|
|
||||||
if (!aSource->data()) {
|
|
||||||
GVDM_LOG("No data in Video Buffer!");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
uint8_t *yuv420p_buffer = (uint8_t *)aSource->data();
|
|
||||||
int32_t stride = mFrameInfo.mStride;
|
|
||||||
int32_t slice_height = mFrameInfo.mSliceHeight;
|
|
||||||
|
|
||||||
// Converts to OMX_COLOR_FormatYUV420Planar
|
|
||||||
if (mFrameInfo.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
|
|
||||||
ARect crop;
|
|
||||||
crop.top = 0;
|
|
||||||
crop.bottom = mFrameInfo.mHeight;
|
|
||||||
crop.left = 0;
|
|
||||||
crop.right = mFrameInfo.mWidth;
|
|
||||||
yuv420p_buffer = GetColorConverterBuffer(mFrameInfo.mWidth, mFrameInfo.mHeight);
|
|
||||||
if (mColorConverter.convertDecoderOutputToI420(aSource->data(),
|
|
||||||
mFrameInfo.mWidth, mFrameInfo.mHeight, crop, yuv420p_buffer) != OK) {
|
|
||||||
GVDM_LOG("Color conversion failed!");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
stride = mFrameInfo.mWidth;
|
|
||||||
slice_height = mFrameInfo.mHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t yuv420p_y_size = stride * slice_height;
|
|
||||||
size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
|
|
||||||
uint8_t *yuv420p_y = yuv420p_buffer;
|
|
||||||
uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
|
|
||||||
uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
|
|
||||||
|
|
||||||
VideoData::YCbCrBuffer b;
|
|
||||||
b.mPlanes[0].mData = yuv420p_y;
|
|
||||||
b.mPlanes[0].mWidth = mFrameInfo.mWidth;
|
|
||||||
b.mPlanes[0].mHeight = mFrameInfo.mHeight;
|
|
||||||
b.mPlanes[0].mStride = stride;
|
|
||||||
b.mPlanes[0].mOffset = 0;
|
|
||||||
b.mPlanes[0].mSkip = 0;
|
|
||||||
|
|
||||||
b.mPlanes[1].mData = yuv420p_u;
|
|
||||||
b.mPlanes[1].mWidth = (mFrameInfo.mWidth + 1) / 2;
|
|
||||||
b.mPlanes[1].mHeight = (mFrameInfo.mHeight + 1) / 2;
|
|
||||||
b.mPlanes[1].mStride = (stride + 1) / 2;
|
|
||||||
b.mPlanes[1].mOffset = 0;
|
|
||||||
b.mPlanes[1].mSkip = 0;
|
|
||||||
|
|
||||||
b.mPlanes[2].mData = yuv420p_v;
|
|
||||||
b.mPlanes[2].mWidth =(mFrameInfo.mWidth + 1) / 2;
|
|
||||||
b.mPlanes[2].mHeight = (mFrameInfo.mHeight + 1) / 2;
|
|
||||||
b.mPlanes[2].mStride = (stride + 1) / 2;
|
|
||||||
b.mPlanes[2].mOffset = 0;
|
|
||||||
b.mPlanes[2].mSkip = 0;
|
|
||||||
|
|
||||||
RefPtr<VideoData> data =
|
|
||||||
VideoData::CreateAndCopyData(mConfig,
|
|
||||||
mImageContainer,
|
|
||||||
0, // Filled later by caller.
|
|
||||||
0, // Filled later by caller.
|
|
||||||
1, // We don't know the duration.
|
|
||||||
b,
|
|
||||||
0, // Filled later by caller.
|
|
||||||
-1,
|
|
||||||
aPicture);
|
|
||||||
|
|
||||||
return data.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
GonkVideoDecoderManager::SetVideoFormat()
|
|
||||||
{
|
|
||||||
// read video metadata from MediaCodec
|
|
||||||
sp<AMessage> codecFormat;
|
|
||||||
if (mDecoder->getOutputFormat(&codecFormat) == OK) {
|
|
||||||
AString mime;
|
|
||||||
int32_t width = 0;
|
|
||||||
int32_t height = 0;
|
|
||||||
int32_t stride = 0;
|
|
||||||
int32_t slice_height = 0;
|
|
||||||
int32_t color_format = 0;
|
|
||||||
int32_t crop_left = 0;
|
|
||||||
int32_t crop_top = 0;
|
|
||||||
int32_t crop_right = 0;
|
|
||||||
int32_t crop_bottom = 0;
|
|
||||||
if (!codecFormat->findString("mime", &mime) ||
|
|
||||||
!codecFormat->findInt32("width", &width) ||
|
|
||||||
!codecFormat->findInt32("height", &height) ||
|
|
||||||
!codecFormat->findInt32("stride", &stride) ||
|
|
||||||
!codecFormat->findInt32("slice-height", &slice_height) ||
|
|
||||||
!codecFormat->findInt32("color-format", &color_format) ||
|
|
||||||
!codecFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
|
|
||||||
GVDM_LOG("Failed to find values");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mFrameInfo.mWidth = width;
|
|
||||||
mFrameInfo.mHeight = height;
|
|
||||||
mFrameInfo.mStride = stride;
|
|
||||||
mFrameInfo.mSliceHeight = slice_height;
|
|
||||||
mFrameInfo.mColorFormat = color_format;
|
|
||||||
|
|
||||||
nsIntSize displaySize(width, height);
|
|
||||||
if (!IsValidVideoRegion(mConfig.mDisplay,
|
|
||||||
mConfig.ScaledImageRect(width, height),
|
|
||||||
displaySize)) {
|
|
||||||
GVDM_LOG("It is not a valid region");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
GVDM_LOG("Fail to get output format");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocks until decoded sample is produced by the deoder.
|
|
||||||
nsresult
|
|
||||||
GonkVideoDecoderManager::Output(int64_t aStreamOffset,
|
|
||||||
RefPtr<MediaData>& aOutData)
|
|
||||||
{
|
|
||||||
aOutData = nullptr;
|
|
||||||
status_t err;
|
|
||||||
if (mDecoder == nullptr) {
|
|
||||||
GVDM_LOG("Decoder is not inited");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
MediaBuffer* outputBuffer = nullptr;
|
|
||||||
err = mDecoder->Output(&outputBuffer, READ_OUTPUT_BUFFER_TIMEOUT_US);
|
|
||||||
|
|
||||||
switch (err) {
|
|
||||||
case OK:
|
|
||||||
{
|
|
||||||
RefPtr<VideoData> data;
|
|
||||||
nsresult rv = CreateVideoData(outputBuffer, aStreamOffset, getter_AddRefs(data));
|
|
||||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
|
||||||
// Decoder outputs a empty video buffer, try again
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
} else if (rv != NS_OK || data == nullptr) {
|
|
||||||
GVDM_LOG("Failed to create VideoData");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
aOutData = data;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
case android::INFO_FORMAT_CHANGED:
|
|
||||||
{
|
|
||||||
// If the format changed, update our cached info.
|
|
||||||
GVDM_LOG("Decoder format changed");
|
|
||||||
if (!SetVideoFormat()) {
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
return Output(aStreamOffset, aOutData);
|
|
||||||
}
|
|
||||||
case android::INFO_OUTPUT_BUFFERS_CHANGED:
|
|
||||||
{
|
|
||||||
if (mDecoder->UpdateOutputBuffers()) {
|
|
||||||
return Output(aStreamOffset, aOutData);
|
|
||||||
}
|
|
||||||
GVDM_LOG("Fails to update output buffers!");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
case -EAGAIN:
|
|
||||||
{
|
|
||||||
// GVDM_LOG("Need to try again!");
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
case android::ERROR_END_OF_STREAM:
|
|
||||||
{
|
|
||||||
GVDM_LOG("Got the EOS frame!");
|
|
||||||
RefPtr<VideoData> data;
|
|
||||||
nsresult rv = CreateVideoData(outputBuffer, aStreamOffset, getter_AddRefs(data));
|
|
||||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
|
||||||
// For EOS, no need to do any thing.
|
|
||||||
return NS_ERROR_ABORT;
|
|
||||||
}
|
|
||||||
if (rv != NS_OK || data == nullptr) {
|
|
||||||
GVDM_LOG("Failed to create video data");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
aOutData = data;
|
|
||||||
return NS_ERROR_ABORT;
|
|
||||||
}
|
|
||||||
case -ETIMEDOUT:
|
|
||||||
{
|
|
||||||
GVDM_LOG("Timeout. can try again next time");
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
GVDM_LOG("Decoder failed, err=%d", err);
|
|
||||||
return NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkVideoDecoderManager::codecReserved()
|
|
||||||
{
|
|
||||||
if (mInitPromise.IsEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GVDM_LOG("codecReserved");
|
|
||||||
sp<AMessage> format = new AMessage;
|
|
||||||
sp<Surface> surface;
|
|
||||||
status_t rv = OK;
|
|
||||||
// Fixed values
|
|
||||||
GVDM_LOG("Configure video mime type: %s, width:%d, height:%d", mConfig.mMimeType.get(), mConfig.mImage.width, mConfig.mImage.height);
|
|
||||||
format->setString("mime", mConfig.mMimeType.get());
|
|
||||||
format->setInt32("width", mConfig.mImage.width);
|
|
||||||
format->setInt32("height", mConfig.mImage.height);
|
|
||||||
// Set the "moz-use-undequeued-bufs" to use the undeque buffers to accelerate
|
|
||||||
// the video decoding.
|
|
||||||
format->setInt32("moz-use-undequeued-bufs", 1);
|
|
||||||
if (mNativeWindow != nullptr) {
|
|
||||||
#if ANDROID_VERSION >= 21
|
|
||||||
surface = new Surface(mGraphicBufferProducer);
|
|
||||||
#else
|
|
||||||
surface = new Surface(mNativeWindow->getBufferQueue());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
mDecoder->configure(format, surface, nullptr, 0);
|
|
||||||
mDecoder->Prepare();
|
|
||||||
|
|
||||||
if (mConfig.mMimeType.EqualsLiteral("video/mp4v-es")) {
|
|
||||||
rv = mDecoder->Input(mConfig.mCodecSpecificConfig->Elements(),
|
|
||||||
mConfig.mCodecSpecificConfig->Length(), 0,
|
|
||||||
android::MediaCodec::BUFFER_FLAG_CODECCONFIG,
|
|
||||||
CODECCONFIG_TIMEOUT_US);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rv != OK) {
|
|
||||||
GVDM_LOG("Failed to configure codec!!!!");
|
|
||||||
mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mInitPromise.Resolve(TrackType::kVideoTrack, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkVideoDecoderManager::codecCanceled()
|
|
||||||
{
|
|
||||||
GVDM_LOG("codecCanceled");
|
|
||||||
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called on GonkDecoderManager::mTaskLooper thread.
|
|
||||||
void
|
|
||||||
GonkVideoDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
|
|
||||||
{
|
|
||||||
switch (aMessage->what()) {
|
|
||||||
case kNotifyPostReleaseBuffer:
|
|
||||||
{
|
|
||||||
ReleaseAllPendingVideoBuffers();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
GonkDecoderManager::onMessageReceived(aMessage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *
|
|
||||||
GonkVideoDecoderManager::GetColorConverterBuffer(int32_t aWidth, int32_t aHeight)
|
|
||||||
{
|
|
||||||
// Allocate a temporary YUV420Planer buffer.
|
|
||||||
size_t yuv420p_y_size = aWidth * aHeight;
|
|
||||||
size_t yuv420p_u_size = ((aWidth + 1) / 2) * ((aHeight + 1) / 2);
|
|
||||||
size_t yuv420p_v_size = yuv420p_u_size;
|
|
||||||
size_t yuv420p_size = yuv420p_y_size + yuv420p_u_size + yuv420p_v_size;
|
|
||||||
if (mColorConverterBufferSize != yuv420p_size) {
|
|
||||||
mColorConverterBuffer = MakeUnique<uint8_t[]>(yuv420p_size);
|
|
||||||
mColorConverterBufferSize = yuv420p_size;
|
|
||||||
}
|
|
||||||
return mColorConverterBuffer.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */
|
|
||||||
void
|
|
||||||
GonkVideoDecoderManager::RecycleCallback(TextureClient* aClient, void* aClosure)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aClient && !aClient->IsDead());
|
|
||||||
GonkVideoDecoderManager* videoManager = static_cast<GonkVideoDecoderManager*>(aClosure);
|
|
||||||
GrallocTextureData* client = static_cast<GrallocTextureData*>(aClient->GetInternalData());
|
|
||||||
aClient->ClearRecycleCallback();
|
|
||||||
FenceHandle handle = aClient->GetAndResetReleaseFenceHandle();
|
|
||||||
videoManager->PostReleaseVideoBuffer(client->GetMediaBuffer(), handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GonkVideoDecoderManager::PostReleaseVideoBuffer(
|
|
||||||
android::MediaBuffer *aBuffer,
|
|
||||||
FenceHandle aReleaseFence)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
MutexAutoLock autoLock(mPendingReleaseItemsLock);
|
|
||||||
if (aBuffer) {
|
|
||||||
mPendingReleaseItems.AppendElement(ReleaseItem(aBuffer, aReleaseFence));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sp<AMessage> notify =
|
|
||||||
new AMessage(kNotifyPostReleaseBuffer, id());
|
|
||||||
notify->post();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void GonkVideoDecoderManager::ReleaseAllPendingVideoBuffers()
|
|
||||||
{
|
|
||||||
nsTArray<ReleaseItem> releasingItems;
|
|
||||||
{
|
|
||||||
MutexAutoLock autoLock(mPendingReleaseItemsLock);
|
|
||||||
releasingItems.AppendElements(mPendingReleaseItems);
|
|
||||||
mPendingReleaseItems.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free all pending video buffers without holding mPendingReleaseItemsLock.
|
|
||||||
size_t size = releasingItems.Length();
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
|
||||||
RefPtr<FenceHandle::FdObj> fdObj = releasingItems[i].mReleaseFence.GetAndResetFdObj();
|
|
||||||
sp<Fence> fence = new Fence(fdObj->GetAndResetFd());
|
|
||||||
fence->waitForever("GonkVideoDecoderManager");
|
|
||||||
mDecoder->ReleaseMediaBuffer(releasingItems[i].mBuffer);
|
|
||||||
}
|
|
||||||
releasingItems.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
#if !defined(GonkVideoDecoderManager_h_)
|
|
||||||
#define GonkVideoDecoderManager_h_
|
|
||||||
|
|
||||||
#include "nsRect.h"
|
|
||||||
#include "GonkMediaDataDecoder.h"
|
|
||||||
#include "mozilla/RefPtr.h"
|
|
||||||
#include "I420ColorConverterHelper.h"
|
|
||||||
#include "MediaCodecProxy.h"
|
|
||||||
#include "GonkNativeWindow.h"
|
|
||||||
#include "mozilla/layers/FenceUtils.h"
|
|
||||||
#include "mozilla/UniquePtr.h"
|
|
||||||
#include <ui/Fence.h>
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
class MediaBuffer;
|
|
||||||
struct MOZ_EXPORT AString;
|
|
||||||
class GonkNativeWindow;
|
|
||||||
} // namespace android
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
namespace layers {
|
|
||||||
class TextureClient;
|
|
||||||
class TextureClientRecycleAllocator;
|
|
||||||
} // namespace mozilla::layers
|
|
||||||
|
|
||||||
class GonkVideoDecoderManager : public GonkDecoderManager {
|
|
||||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
|
||||||
typedef mozilla::layers::TextureClient TextureClient;
|
|
||||||
|
|
||||||
public:
|
|
||||||
GonkVideoDecoderManager(mozilla::layers::ImageContainer* aImageContainer,
|
|
||||||
const VideoInfo& aConfig);
|
|
||||||
|
|
||||||
virtual ~GonkVideoDecoderManager();
|
|
||||||
|
|
||||||
RefPtr<InitPromise> Init() override;
|
|
||||||
|
|
||||||
nsresult Output(int64_t aStreamOffset,
|
|
||||||
RefPtr<MediaData>& aOutput) override;
|
|
||||||
|
|
||||||
nsresult Shutdown() override;
|
|
||||||
|
|
||||||
const char* GetDescriptionName() const override
|
|
||||||
{
|
|
||||||
return "gonk video decoder";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RecycleCallback(TextureClient* aClient, void* aClosure);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Bug 1199809: workaround to avoid sending the graphic buffer by making a
|
|
||||||
// copy of output buffer after calling flush(). Bug 1203859 was created to
|
|
||||||
// reimplementing Gonk PDM on top of OpenMax IL directly. Its buffer
|
|
||||||
// management will work better with Gecko and solve problems like this.
|
|
||||||
void ProcessFlush() override
|
|
||||||
{
|
|
||||||
mNeedsCopyBuffer = true;
|
|
||||||
GonkDecoderManager::ProcessFlush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct FrameInfo
|
|
||||||
{
|
|
||||||
int32_t mWidth = 0;
|
|
||||||
int32_t mHeight = 0;
|
|
||||||
int32_t mStride = 0;
|
|
||||||
int32_t mSliceHeight = 0;
|
|
||||||
int32_t mColorFormat = 0;
|
|
||||||
int32_t mCropLeft = 0;
|
|
||||||
int32_t mCropTop = 0;
|
|
||||||
int32_t mCropRight = 0;
|
|
||||||
int32_t mCropBottom = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
void onMessageReceived(const android::sp<android::AMessage> &aMessage) override;
|
|
||||||
|
|
||||||
bool SetVideoFormat();
|
|
||||||
|
|
||||||
nsresult CreateVideoData(MediaBuffer* aBuffer, int64_t aStreamOffset, VideoData** aOutData);
|
|
||||||
already_AddRefed<VideoData> CreateVideoDataFromGraphicBuffer(android::MediaBuffer* aSource,
|
|
||||||
gfx::IntRect& aPicture);
|
|
||||||
already_AddRefed<VideoData> CreateVideoDataFromDataBuffer(android::MediaBuffer* aSource,
|
|
||||||
gfx::IntRect& aPicture);
|
|
||||||
|
|
||||||
uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
|
|
||||||
|
|
||||||
// For codec resource management
|
|
||||||
void codecReserved();
|
|
||||||
void codecCanceled();
|
|
||||||
|
|
||||||
void ReleaseAllPendingVideoBuffers();
|
|
||||||
void PostReleaseVideoBuffer(android::MediaBuffer *aBuffer,
|
|
||||||
layers::FenceHandle mReleaseFence);
|
|
||||||
|
|
||||||
VideoInfo mConfig;
|
|
||||||
|
|
||||||
RefPtr<layers::ImageContainer> mImageContainer;
|
|
||||||
RefPtr<layers::TextureClientRecycleAllocator> mCopyAllocator;
|
|
||||||
|
|
||||||
MozPromiseRequestHolder<android::MediaCodecProxy::CodecPromise> mVideoCodecRequest;
|
|
||||||
FrameInfo mFrameInfo;
|
|
||||||
|
|
||||||
// color converter
|
|
||||||
android::I420ColorConverterHelper mColorConverter;
|
|
||||||
UniquePtr<uint8_t[]> mColorConverterBuffer;
|
|
||||||
size_t mColorConverterBufferSize;
|
|
||||||
|
|
||||||
android::sp<android::GonkNativeWindow> mNativeWindow;
|
|
||||||
#if ANDROID_VERSION >= 21
|
|
||||||
android::sp<android::IGraphicBufferProducer> mGraphicBufferProducer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum {
|
|
||||||
kNotifyPostReleaseBuffer = 'nprb',
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ReleaseItem {
|
|
||||||
ReleaseItem(android::MediaBuffer* aBuffer, layers::FenceHandle& aFence)
|
|
||||||
: mBuffer(aBuffer)
|
|
||||||
, mReleaseFence(aFence) {}
|
|
||||||
android::MediaBuffer* mBuffer;
|
|
||||||
layers::FenceHandle mReleaseFence;
|
|
||||||
};
|
|
||||||
nsTArray<ReleaseItem> mPendingReleaseItems;
|
|
||||||
|
|
||||||
// The lock protects mPendingReleaseItems.
|
|
||||||
Mutex mPendingReleaseItemsLock;
|
|
||||||
|
|
||||||
// This TaskQueue should be the same one in mDecodeCallback->OnReaderTaskQueue().
|
|
||||||
// It is for codec resource mangement, decoding task should not dispatch to it.
|
|
||||||
RefPtr<TaskQueue> mReaderTaskQueue;
|
|
||||||
|
|
||||||
// Bug 1199809: do we need to make a copy of output buffer? Used only when
|
|
||||||
// the decoder outputs graphic buffers.
|
|
||||||
bool mNeedsCopyBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // GonkVideoDecoderManager_h_
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
# -*- Mode: python; 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/.
|
|
||||||
|
|
||||||
EXPORTS += [
|
|
||||||
'GonkAudioDecoderManager.h',
|
|
||||||
'GonkDecoderModule.h',
|
|
||||||
'GonkMediaDataDecoder.h',
|
|
||||||
'GonkVideoDecoderManager.h',
|
|
||||||
]
|
|
||||||
UNIFIED_SOURCES += [
|
|
||||||
'GonkAudioDecoderManager.cpp',
|
|
||||||
'GonkDecoderModule.cpp',
|
|
||||||
'GonkMediaDataDecoder.cpp',
|
|
||||||
'GonkVideoDecoderManager.cpp',
|
|
||||||
]
|
|
||||||
LOCAL_INCLUDES += [
|
|
||||||
'/dom/media/omx/',
|
|
||||||
]
|
|
||||||
include('/ipc/chromium/chromium-config.mozbuild')
|
|
||||||
|
|
||||||
# Suppress some GCC/clang warnings being treated as errors:
|
|
||||||
# - about attributes on forward declarations for types that are already
|
|
||||||
# defined, which complains about an important MOZ_EXPORT for android::AString
|
|
||||||
# - about multi-character constants which are used in codec-related code
|
|
||||||
if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
|
|
||||||
CXXFLAGS += [
|
|
||||||
'-Wno-error=attributes',
|
|
||||||
'-Wno-error=multichar'
|
|
||||||
]
|
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
|
||||||
|
|
||||||
LOCAL_INCLUDES += [
|
|
||||||
'%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
|
||||||
'frameworks/native/opengl/include',]
|
|
||||||
]
|
|
||||||
|
|
@ -70,10 +70,6 @@ if CONFIG['MOZ_APPLEMEDIA']:
|
||||||
'-framework AudioToolbox',
|
'-framework AudioToolbox',
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG['MOZ_GONK_MEDIACODEC']:
|
|
||||||
DEFINES['MOZ_GONK_MEDIACODEC'] = True
|
|
||||||
DIRS += ['gonk']
|
|
||||||
|
|
||||||
include('/ipc/chromium/chromium-config.mozbuild')
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
|
||||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||||
|
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* Copyright © 2015, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.SEUtils = {
|
|
||||||
byteArrayToHexString: function byteArrayToHexString(array) {
|
|
||||||
let hexStr = "";
|
|
||||||
|
|
||||||
let len = array ? array.length : 0;
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
let hex = (array[i] & 0xff).toString(16);
|
|
||||||
hex = (hex.length === 1) ? "0" + hex : hex;
|
|
||||||
hexStr += hex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hexStr.toUpperCase();
|
|
||||||
},
|
|
||||||
|
|
||||||
hexStringToByteArray: function hexStringToByteArray(hexStr) {
|
|
||||||
if (typeof hexStr !== "string" || hexStr.length % 2 !== 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let array = [];
|
|
||||||
for (let i = 0, len = hexStr.length; i < len; i += 2) {
|
|
||||||
array.push(parseInt(hexStr.substr(i, 2), 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
|
||||||
},
|
|
||||||
|
|
||||||
arraysEqual: function arraysEqual(a1, a2) {
|
|
||||||
if (!a1 || !a2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a1.length !== a2.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0, len = a1.length; i < len; i++) {
|
|
||||||
if (a1[i] !== a2[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
ensureIsArray: function ensureIsArray(obj) {
|
|
||||||
return Array.isArray(obj) ? obj : [obj];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parseTLV is intended primarily to be used to parse Global Platform Device
|
|
||||||
* Technology secure element access control data.
|
|
||||||
*
|
|
||||||
* The parsed result value is an internal format only.
|
|
||||||
*
|
|
||||||
* All tags will be treated as simple Tag Length Values (TLV), (i.e. with a
|
|
||||||
* plain value, not subject to further unpacking), unless those tags are
|
|
||||||
* listed in the containerTags array.
|
|
||||||
*
|
|
||||||
* @param bytes - byte array
|
|
||||||
* @param containerTags - byte array of tags
|
|
||||||
*/
|
|
||||||
parseTLV: function parseTLV(bytes, containerTags) {
|
|
||||||
let result = {};
|
|
||||||
|
|
||||||
if (typeof bytes === "string") {
|
|
||||||
bytes = this.hexStringToByteArray(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Array.isArray(bytes)) {
|
|
||||||
debug("Passed value is not an array nor a string.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let pos = 0; pos < bytes.length; ) {
|
|
||||||
let tag = bytes[pos],
|
|
||||||
length = bytes[pos + 1],
|
|
||||||
value = bytes.slice(pos + 2, pos + 2 + length),
|
|
||||||
parsed = null;
|
|
||||||
|
|
||||||
// Support for 0xFF padded files (GPD 7.1.2)
|
|
||||||
if (tag === 0xFF) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containerTags.indexOf(tag) >= 0) {
|
|
||||||
parsed = this.parseTLV(value, containerTags);
|
|
||||||
} else {
|
|
||||||
parsed = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal parsed format.
|
|
||||||
if (!result[tag]) {
|
|
||||||
result[tag] = parsed;
|
|
||||||
} else if (Array.isArray(result[tag])) {
|
|
||||||
result[tag].push(parsed);
|
|
||||||
} else {
|
|
||||||
result[tag] = [result[tag], parsed];
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = pos + 2 + length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["SEUtils"];
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* Copyright © 2015, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
|
|
||||||
"resource://gre/modules/SEUtils.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "SE", function() {
|
|
||||||
let obj = {};
|
|
||||||
Cu.import("resource://gre/modules/se_consts.js", obj);
|
|
||||||
return obj;
|
|
||||||
});
|
|
||||||
|
|
||||||
var DEBUG = SE.DEBUG_ACE;
|
|
||||||
function debug(msg) {
|
|
||||||
if (DEBUG) {
|
|
||||||
dump("ACEservice: " + msg + "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements decision making algorithm as described in GPD specification,
|
|
||||||
* mostly in 3.1, 3.2 and 4.2.3.
|
|
||||||
*
|
|
||||||
* TODO: Bug 1137533: Implement GPAccessRulesManager APDU filters
|
|
||||||
*/
|
|
||||||
function GPAccessDecision(rules, certHash, aid) {
|
|
||||||
this.rules = rules;
|
|
||||||
this.certHash = certHash;
|
|
||||||
this.aid = aid;
|
|
||||||
}
|
|
||||||
|
|
||||||
GPAccessDecision.prototype = {
|
|
||||||
isAccessAllowed: function isAccessAllowed() {
|
|
||||||
// GPD SE Access Control v1.1, 3.4.1, Table 3-2: (Conflict resolution)
|
|
||||||
// If a specific rule allows, all other non-specific access is denied.
|
|
||||||
// Conflicting specific rules will resolve to the first Allowed == "true"
|
|
||||||
// match. Given no specific rule, the global "All" rules will determine
|
|
||||||
// access. "Some", skips further processing if access Allowed == "true".
|
|
||||||
//
|
|
||||||
// Access must be decided before the SE connector openChannel, and the
|
|
||||||
// exchangeAPDU call.
|
|
||||||
//
|
|
||||||
// NOTE: This implementation may change with the introduction of APDU
|
|
||||||
// filters.
|
|
||||||
let decision = this.rules.some(this._decideAppAccess.bind(this));
|
|
||||||
return decision;
|
|
||||||
},
|
|
||||||
|
|
||||||
_decideAppAccess: function _decideAppAccess(rule) {
|
|
||||||
let appMatched, appletMatched;
|
|
||||||
|
|
||||||
// GPD SE AC 4.2.3: Algorithm for Applying Rules
|
|
||||||
// Specific rule overrides global rule.
|
|
||||||
//
|
|
||||||
// DeviceAppID is the application hash, and the AID is SE Applet ID:
|
|
||||||
//
|
|
||||||
// GPD SE AC 4.2.3 A:
|
|
||||||
// SearchRuleFor(DeviceAppID, AID)
|
|
||||||
// GPD SE AC 4.2.3 B: If no rule fits A:
|
|
||||||
// SearchRuleFor(<AllDeviceApplications>, AID)
|
|
||||||
// GPD SE AC 4.2.3 C: If no rule fits A or B:
|
|
||||||
// SearchRuleFor(DeviceAppID, <AllSEApplications>)
|
|
||||||
// GPD SE AC 4.2.3 D: If no rule fits A, B, or C:
|
|
||||||
// SearchRuleFor(<AllDeviceApplications>, <AllSEApplications>)
|
|
||||||
|
|
||||||
// Device App
|
|
||||||
appMatched = Array.isArray(rule.application) ?
|
|
||||||
// GPD SE AC 4.2.3 A and 4.2.3 C (DeviceAppID rule)
|
|
||||||
this._appCertHashMatches(rule.application) :
|
|
||||||
// GPD SE AC 4.2.3 B and 4.2.3 D (All Device Applications)
|
|
||||||
rule.application === Ci.nsIAccessRulesManager.ALLOW_ALL;
|
|
||||||
|
|
||||||
if (!appMatched) {
|
|
||||||
return false; // bail out early.
|
|
||||||
}
|
|
||||||
|
|
||||||
// SE Applet
|
|
||||||
appletMatched = Array.isArray(rule.applet) ?
|
|
||||||
// GPD SE AC 4.2.3 A and 4.2.3 B (AID rule)
|
|
||||||
SEUtils.arraysEqual(rule.applet, this.aid) :
|
|
||||||
// GPD SE AC 4.2.3 C and 4.2.3 D (All AID)
|
|
||||||
rule.applet === Ci.nsIAccessRulesManager.ALL_APPLET;
|
|
||||||
|
|
||||||
return appletMatched;
|
|
||||||
},
|
|
||||||
|
|
||||||
_appCertHashMatches: function _appCertHashMatches(hashArray) {
|
|
||||||
if (!Array.isArray(hashArray)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!(hashArray.find((hash) => {
|
|
||||||
return SEUtils.arraysEqual(hash, this.certHash);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function ACEService() {
|
|
||||||
this._rulesManagers = new Map();
|
|
||||||
|
|
||||||
this._rulesManagers.set(
|
|
||||||
SE.TYPE_UICC,
|
|
||||||
Cc["@mozilla.org/secureelement/access-control/rules-manager;1"]
|
|
||||||
.createInstance(Ci.nsIAccessRulesManager));
|
|
||||||
}
|
|
||||||
|
|
||||||
ACEService.prototype = {
|
|
||||||
_rulesManagers: null,
|
|
||||||
|
|
||||||
isAccessAllowed: function isAccessAllowed(localId, seType, aid) {
|
|
||||||
if(!Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps")) {
|
|
||||||
debug("Certified apps debug enabled, allowing access");
|
|
||||||
return Promise.resolve(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
},
|
|
||||||
|
|
||||||
_getDevCertHashForApp: function getDevCertHashForApp(manifestURL) {
|
|
||||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
},
|
|
||||||
|
|
||||||
classID: Components.ID("{882a7463-2ca7-4d61-a89a-10eb6fd70478}"),
|
|
||||||
contractID: "@mozilla.org/secureelement/access-control/ace;1",
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessControlEnforcer])
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ACEService]);
|
|
||||||
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
component {882a7463-2ca7-4d61-a89a-10eb6fd70478} ACEService.js
|
|
||||||
contract @mozilla.org/secureelement/access-control/ace;1 {882a7463-2ca7-4d61-a89a-10eb6fd70478}
|
|
||||||
|
|
@ -1,436 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* Copyright © 2015, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const Ci = Components.interfaces;
|
|
||||||
const Cu = Components.utils;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Task.jsm");
|
|
||||||
Cu.import("resource://gre/modules/systemlibs.js");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "UiccConnector",
|
|
||||||
"@mozilla.org/secureelement/connector/uicc;1",
|
|
||||||
"nsISecureElementConnector");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
|
|
||||||
"resource://gre/modules/SEUtils.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "SE", function() {
|
|
||||||
let obj = {};
|
|
||||||
Cu.import("resource://gre/modules/se_consts.js", obj);
|
|
||||||
return obj;
|
|
||||||
});
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "GP", function() {
|
|
||||||
let obj = {};
|
|
||||||
Cu.import("resource://gre/modules/gp_consts.js", obj);
|
|
||||||
return obj;
|
|
||||||
});
|
|
||||||
|
|
||||||
var DEBUG = SE.DEBUG_ACE;
|
|
||||||
function debug(msg) {
|
|
||||||
if (DEBUG) {
|
|
||||||
dump("-*- GPAccessRulesManager " + msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Based on [1] - "GlobalPlatform Device Technology
|
|
||||||
* Secure Element Access Control Version 1.0".
|
|
||||||
* GPAccessRulesManager reads and parses access rules from SE file system
|
|
||||||
* as defined in section #7 of [1]: "Structure of Access Rule Files (ARF)".
|
|
||||||
* Rules retrieval from ARA-M applet is not implmented due to lack of
|
|
||||||
* commercial implemenations of ARA-M.
|
|
||||||
* @todo Bug 1137537: Implement ARA-M support according to section #4 of [1]
|
|
||||||
*/
|
|
||||||
function GPAccessRulesManager() {}
|
|
||||||
|
|
||||||
GPAccessRulesManager.prototype = {
|
|
||||||
// source [1] section 7.1.3 PKCS#15 Selection
|
|
||||||
PKCS_AID: "a000000063504b43532d3135",
|
|
||||||
|
|
||||||
// APDUs (ISO 7816-4) for accessing rules on SE file system
|
|
||||||
// see for more details: http://www.cardwerk.com/smartcards/
|
|
||||||
// smartcard_standard_ISO7816-4_6_basic_interindustry_commands.aspx
|
|
||||||
READ_BINARY: [GP.CLA_SM, GP.INS_RB, GP.P1_RB, GP.P2_RB],
|
|
||||||
GET_RESPONSE: [GP.CLA_SM, GP.INS_GR, GP.P1_GR, GP.P2_GR],
|
|
||||||
SELECT_BY_DF: [GP.CLA_SM, GP.INS_SF, GP.P1_SF_DF, GP.P2_SF_FCP],
|
|
||||||
|
|
||||||
// Non-null if there is a channel open
|
|
||||||
channel: null,
|
|
||||||
|
|
||||||
// Refresh tag path in the acMain file as described in GPD spec,
|
|
||||||
// sections 7.1.5 and C.1.
|
|
||||||
REFRESH_TAG_PATH: [GP.TAG_SEQUENCE, GP.TAG_OCTETSTRING],
|
|
||||||
refreshTag: null,
|
|
||||||
|
|
||||||
// Contains rules as read from the SE
|
|
||||||
rules: [],
|
|
||||||
|
|
||||||
// Returns the latest rules. Results are cached.
|
|
||||||
getAccessRules: function getAccessRules() {
|
|
||||||
debug("getAccessRules");
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this._readAccessRules(() => resolve(this.rules));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_readAccessRules: Task.async(function*(done) {
|
|
||||||
try {
|
|
||||||
yield this._openChannel(this.PKCS_AID);
|
|
||||||
|
|
||||||
let odf = yield this._readODF();
|
|
||||||
let dodf = yield this._readDODF(odf);
|
|
||||||
|
|
||||||
let acmf = yield this._readACMF(dodf);
|
|
||||||
let refreshTag = acmf[this.REFRESH_TAG_PATH[0]]
|
|
||||||
[this.REFRESH_TAG_PATH[1]];
|
|
||||||
|
|
||||||
// Update cached rules based on refreshTag.
|
|
||||||
if (SEUtils.arraysEqual(this.refreshTag, refreshTag)) {
|
|
||||||
debug("_readAccessRules: refresh tag equals to the one saved.");
|
|
||||||
yield this._closeChannel();
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.refreshTag = refreshTag;
|
|
||||||
debug("_readAccessRules: refresh tag saved: " + this.refreshTag);
|
|
||||||
|
|
||||||
let acrf = yield this._readACRules(acmf);
|
|
||||||
let accf = yield this._readACConditions(acrf);
|
|
||||||
this.rules = yield this._parseRules(acrf, accf);
|
|
||||||
|
|
||||||
DEBUG && debug("_readAccessRules: " + JSON.stringify(this.rules, 0, 2));
|
|
||||||
|
|
||||||
yield this._closeChannel();
|
|
||||||
done();
|
|
||||||
} catch (error) {
|
|
||||||
debug("_readAccessRules: " + error);
|
|
||||||
this.rules = [];
|
|
||||||
yield this._closeChannel();
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
_openChannel: function _openChannel(aid) {
|
|
||||||
if (this.channel !== null) {
|
|
||||||
debug("_openChannel: Channel already opened, rejecting.");
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
UiccConnector.openChannel(aid, {
|
|
||||||
notifyOpenChannelSuccess: (channel, openResponse) => {
|
|
||||||
debug("_openChannel/notifyOpenChannelSuccess: Channel " + channel +
|
|
||||||
" opened, open response: " + openResponse);
|
|
||||||
this.channel = channel;
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
notifyError: (error) => {
|
|
||||||
debug("_openChannel/notifyError: failed to open channel, error: " +
|
|
||||||
error);
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_closeChannel: function _closeChannel() {
|
|
||||||
if (this.channel === null) {
|
|
||||||
debug("_closeChannel: Channel not opened, rejecting.");
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
UiccConnector.closeChannel(this.channel, {
|
|
||||||
notifyCloseChannelSuccess: () => {
|
|
||||||
debug("_closeChannel/notifyCloseChannelSuccess: chanel " +
|
|
||||||
this.channel + " closed");
|
|
||||||
this.channel = null;
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
notifyError: (error) => {
|
|
||||||
debug("_closeChannel/notifyError: error closing channel, error" +
|
|
||||||
error);
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_exchangeAPDU: function _exchangeAPDU(bytes) {
|
|
||||||
DEBUG && debug("apdu " + JSON.stringify(bytes));
|
|
||||||
|
|
||||||
let apdu = this._bytesToAPDU(bytes);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
UiccConnector.exchangeAPDU(this.channel, apdu.cla,
|
|
||||||
apdu.ins, apdu.p1, apdu.p2, apdu.data, apdu.le,
|
|
||||||
{
|
|
||||||
notifyExchangeAPDUResponse: (sw1, sw2, data) => {
|
|
||||||
debug("APDU response is " + sw1.toString(16) + sw2.toString(16) +
|
|
||||||
" data: " + data);
|
|
||||||
|
|
||||||
// 90 00 is "success"
|
|
||||||
if (sw1 !== 0x90 && sw2 !== 0x00) {
|
|
||||||
debug("rejecting APDU response");
|
|
||||||
reject(new Error("Response " + sw1 + "," + sw2));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(this._parseTLV(data));
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: (error) => {
|
|
||||||
debug("_exchangeAPDU/notifyError " + error);
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_readBinaryFile: function _readBinaryFile(selectResponse) {
|
|
||||||
DEBUG && debug("Select response: " + JSON.stringify(selectResponse));
|
|
||||||
// 0x80 tag parameter - get the elementary file (EF) length
|
|
||||||
// without structural information.
|
|
||||||
let fileLength = selectResponse[GP.TAG_FCP][0x80];
|
|
||||||
|
|
||||||
// If file is empty, no need to attempt to read it.
|
|
||||||
if (fileLength[0] === 0 && fileLength[1] === 0) {
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO READ BINARY with filelength not supported
|
|
||||||
// let readApdu = this.READ_BINARY.concat(fileLength);
|
|
||||||
return this._exchangeAPDU(this.READ_BINARY);
|
|
||||||
},
|
|
||||||
|
|
||||||
_selectAndRead: function _selectAndRead(df) {
|
|
||||||
return this._exchangeAPDU(this.SELECT_BY_DF.concat(df.length & 0xFF, df))
|
|
||||||
.then((resp) => this._readBinaryFile(resp));
|
|
||||||
},
|
|
||||||
|
|
||||||
_readODF: function _readODF() {
|
|
||||||
debug("_readODF");
|
|
||||||
return this._selectAndRead(GP.ODF_DF);
|
|
||||||
},
|
|
||||||
|
|
||||||
_readDODF: function _readDODF(odfFile) {
|
|
||||||
debug("_readDODF, ODF file: " + odfFile);
|
|
||||||
|
|
||||||
// Data Object Directory File (DODF) is used as an entry point to the
|
|
||||||
// Access Control data. It is specified in PKCS#15 section 6.7.6.
|
|
||||||
// DODF is referenced by the ODF file, which looks as follows:
|
|
||||||
// A7 06
|
|
||||||
// 30 04
|
|
||||||
// 04 02 XY WZ
|
|
||||||
// where [0xXY, 0xWZ] is a DF of DODF file.
|
|
||||||
let DODF_DF = odfFile[GP.TAG_EF_ODF][GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
|
|
||||||
return this._selectAndRead(DODF_DF);
|
|
||||||
},
|
|
||||||
|
|
||||||
_readACMF: function _readACMF(dodfFile) {
|
|
||||||
debug("_readACMF, DODF file: " + dodfFile);
|
|
||||||
|
|
||||||
// ACMF file DF is referenced in DODF file, which looks like this:
|
|
||||||
//
|
|
||||||
// A1 29
|
|
||||||
// 30 00
|
|
||||||
// 30 0F
|
|
||||||
// 0C 0D 47 50 20 53 45 20 41 63 63 20 43 74 6C
|
|
||||||
// A1 14
|
|
||||||
// 30 12
|
|
||||||
// 06 0A 2A 86 48 86 FC 6B 81 48 01 01 <-- GPD registered OID
|
|
||||||
// 30 04
|
|
||||||
// 04 02 AB CD <-- ACMF DF
|
|
||||||
// A1 2B
|
|
||||||
// 30 00
|
|
||||||
// 30 0F
|
|
||||||
// 0C 0D 53 41 54 53 41 20 47 54 4F 20 31 2E 31
|
|
||||||
// A1 16
|
|
||||||
// 30 14
|
|
||||||
// 06 0C 2B 06 01 04 01 2A 02 6E 03 01 01 01 <-- some other OID
|
|
||||||
// 30 04
|
|
||||||
// 04 02 XY WZ <-- some other file's DF
|
|
||||||
//
|
|
||||||
// DODF file consists of DataTypes with oidDO entries. Entry with OID
|
|
||||||
// equal to "1.2.840.114283.200.1.1" ("2A 86 48 86 FC 6B 81 48 01 01")
|
|
||||||
// contains DF of the ACMF. In the file above, it means that ACMF DF
|
|
||||||
// equals to [0xAB, 0xCD], and not [0xXY, 0xWZ].
|
|
||||||
//
|
|
||||||
// Algorithm used to encode OID to an byte array:
|
|
||||||
// http://www.snmpsharpnet.com/?p=153
|
|
||||||
|
|
||||||
let gpdOid = [0x2A, // 1.2
|
|
||||||
0x86, 0x48, // 840
|
|
||||||
0x86, 0xFC, 0x6B, // 114283
|
|
||||||
0x81, 0x48, // 129
|
|
||||||
0x01, // 1
|
|
||||||
0x01]; // 1
|
|
||||||
|
|
||||||
let records = SEUtils.ensureIsArray(dodfFile[GP.TAG_EXTERNALDO]);
|
|
||||||
|
|
||||||
// Look for the OID registered for GPD SE.
|
|
||||||
let gpdRecords = records.filter((record) => {
|
|
||||||
let oid = record[GP.TAG_EXTERNALDO][GP.TAG_SEQUENCE][GP.TAG_OID];
|
|
||||||
return SEUtils.arraysEqual(oid, gpdOid);
|
|
||||||
});
|
|
||||||
|
|
||||||
// [1] 7.1.5: "There shall be only one ACMF file per Secure Element.
|
|
||||||
// If a Secure Element contains several ACMF files, then the security shall
|
|
||||||
// be considered compromised and the Access Control enforcer shall forbid
|
|
||||||
// access to all (...) apps."
|
|
||||||
if (gpdRecords.length !== 1) {
|
|
||||||
return Promise.reject(new Error(gpdRecords.length + " ACMF files found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let ACMain_DF = gpdRecords[0][GP.TAG_EXTERNALDO][GP.TAG_SEQUENCE]
|
|
||||||
[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
|
|
||||||
return this._selectAndRead(ACMain_DF);
|
|
||||||
},
|
|
||||||
|
|
||||||
_readACRules: function _readACRules(acMainFile) {
|
|
||||||
debug("_readACRules, ACMain file: " + acMainFile);
|
|
||||||
|
|
||||||
// ACMF looks like this:
|
|
||||||
//
|
|
||||||
// 30 10
|
|
||||||
// 04 08 XX XX XX XX XX XX XX XX
|
|
||||||
// 30 04
|
|
||||||
// 04 02 XY WZ
|
|
||||||
//
|
|
||||||
// where [XY, WZ] is a DF of ACRF, and XX XX XX XX XX XX XX XX is a refresh
|
|
||||||
// tag.
|
|
||||||
|
|
||||||
let ACRules_DF = acMainFile[GP.TAG_SEQUENCE][GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
|
|
||||||
return this._selectAndRead(ACRules_DF);
|
|
||||||
},
|
|
||||||
|
|
||||||
_readACConditions: function _readACConditions(acRulesFile) {
|
|
||||||
debug("_readACCondition, ACRules file: " + acRulesFile);
|
|
||||||
|
|
||||||
let acRules = SEUtils.ensureIsArray(acRulesFile[GP.TAG_SEQUENCE]);
|
|
||||||
if (acRules.length === 0) {
|
|
||||||
debug("No rules found in ACRules file.");
|
|
||||||
return Promise.reject(new Error("No rules found in ACRules file"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We first read all the condition files referenced in the ACRules file,
|
|
||||||
// because ACRules file might reference one ACCondition file more than
|
|
||||||
// once. Since reading it isn't exactly fast, we optimize here.
|
|
||||||
let acReadQueue = Promise.resolve({});
|
|
||||||
|
|
||||||
acRules.forEach((ruleEntry) => {
|
|
||||||
let df = ruleEntry[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
|
|
||||||
|
|
||||||
// Promise chain read condition entries:
|
|
||||||
let readAcCondition = (acConditionFiles) => {
|
|
||||||
if (acConditionFiles[df] !== undefined) {
|
|
||||||
debug("Skipping previously read acCondition df: " + df);
|
|
||||||
return acConditionFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._selectAndRead(df)
|
|
||||||
.then((acConditionFileContents) => {
|
|
||||||
acConditionFiles[df] = acConditionFileContents;
|
|
||||||
return acConditionFiles;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
acReadQueue = acReadQueue.then(readAcCondition);
|
|
||||||
});
|
|
||||||
|
|
||||||
return acReadQueue;
|
|
||||||
},
|
|
||||||
|
|
||||||
_parseRules: function _parseRules(acRulesFile, acConditionFiles) {
|
|
||||||
DEBUG && debug("_parseRules: acConditionFiles " + JSON.stringify(acConditionFiles));
|
|
||||||
let rules = [];
|
|
||||||
|
|
||||||
let acRules = SEUtils.ensureIsArray(acRulesFile[GP.TAG_SEQUENCE]);
|
|
||||||
acRules.forEach((ruleEntry) => {
|
|
||||||
DEBUG && debug("Parsing one rule: " + JSON.stringify(ruleEntry));
|
|
||||||
let rule = {};
|
|
||||||
|
|
||||||
// 0xA0 and 0x82 tags as per GPD spec sections C.1 - C.3. 0xA0 means
|
|
||||||
// that rule describes access to one SE applet only (and its AID is
|
|
||||||
// given). 0x82 means that rule describes acccess to all SE applets.
|
|
||||||
let oneApplet = ruleEntry[GP.TAG_GPD_AID];
|
|
||||||
let allApplets = ruleEntry[GP.TAG_GPD_ALL];
|
|
||||||
|
|
||||||
if (oneApplet) {
|
|
||||||
rule.applet = oneApplet[GP.TAG_OCTETSTRING];
|
|
||||||
} else if (allApplets) {
|
|
||||||
rule.applet = Ci.nsIAccessRulesManager.ALL_APPLET;
|
|
||||||
} else {
|
|
||||||
throw Error("Unknown applet definition");
|
|
||||||
}
|
|
||||||
|
|
||||||
let df = ruleEntry[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
|
|
||||||
let condition = acConditionFiles[df];
|
|
||||||
if (condition === null) {
|
|
||||||
rule.application = Ci.nsIAccessRulesManager.DENY_ALL;
|
|
||||||
} else if (condition[GP.TAG_SEQUENCE]) {
|
|
||||||
if (!Array.isArray(condition[GP.TAG_SEQUENCE]) &&
|
|
||||||
!condition[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING]) {
|
|
||||||
rule.application = Ci.nsIAccessRulesManager.ALLOW_ALL;
|
|
||||||
} else {
|
|
||||||
rule.application = SEUtils.ensureIsArray(condition[GP.TAG_SEQUENCE])
|
|
||||||
.map((conditionEntry) => {
|
|
||||||
return conditionEntry[GP.TAG_OCTETSTRING];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw Error("Unknown application definition");
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG && debug("Rule parsed, adding to the list: " + JSON.stringify(rule));
|
|
||||||
rules.push(rule);
|
|
||||||
});
|
|
||||||
|
|
||||||
DEBUG && debug("All rules parsed, we have those in total: " + JSON.stringify(rules));
|
|
||||||
return rules;
|
|
||||||
},
|
|
||||||
|
|
||||||
_parseTLV: function _parseTLV(bytes) {
|
|
||||||
let containerTags = [
|
|
||||||
GP.TAG_SEQUENCE,
|
|
||||||
GP.TAG_FCP,
|
|
||||||
GP.TAG_GPD_AID,
|
|
||||||
GP.TAG_EXTERNALDO,
|
|
||||||
GP.TAG_INDIRECT,
|
|
||||||
GP.TAG_EF_ODF
|
|
||||||
];
|
|
||||||
return SEUtils.parseTLV(bytes, containerTags);
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO consider removing if better format for storing
|
|
||||||
// APDU consts will be introduced
|
|
||||||
_bytesToAPDU: function _bytesToAPDU(arr) {
|
|
||||||
let apdu = {
|
|
||||||
cla: arr[0] & 0xFF,
|
|
||||||
ins: arr[1] & 0xFF,
|
|
||||||
p1: arr[2] & 0xFF,
|
|
||||||
p2: arr[3] & 0xFF,
|
|
||||||
p3: arr[4] & 0xFF,
|
|
||||||
le: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = (apdu.p3 > 0) ? (arr.slice(5)) : [];
|
|
||||||
apdu.data = (data.length) ? SEUtils.byteArrayToHexString(data) : null;
|
|
||||||
return apdu;
|
|
||||||
},
|
|
||||||
|
|
||||||
classID: Components.ID("{3e046b4b-9e66-439a-97e0-98a69f39f55f}"),
|
|
||||||
contractID: "@mozilla.org/secureelement/access-control/rules-manager;1",
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessRulesManager])
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([GPAccessRulesManager]);
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
component {3e046b4b-9e66-439a-97e0-98a69f39f55f} GPAccessRulesManager.js
|
|
||||||
contract @mozilla.org/secureelement/access-control/rules-manager;1 {3e046b4b-9e66-439a-97e0-98a69f39f55f}
|
|
||||||
|
|
@ -1,514 +0,0 @@
|
||||||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Copyright © 2014, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/* globals dump, Components, XPCOMUtils, SE, Services, UiccConnector,
|
|
||||||
SEUtils, ppmm, gMap, UUIDGenerator */
|
|
||||||
|
|
||||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
Cu.import("resource://gre/modules/systemlibs.js");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "SE", () => {
|
|
||||||
let obj = {};
|
|
||||||
Cu.import("resource://gre/modules/se_consts.js", obj);
|
|
||||||
return obj;
|
|
||||||
});
|
|
||||||
|
|
||||||
// set to true in se_consts.js to see debug messages
|
|
||||||
var DEBUG = SE.DEBUG_SE;
|
|
||||||
function debug(s) {
|
|
||||||
if (DEBUG) {
|
|
||||||
dump("-*- SecureElement: " + s + "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SE_IPC_SECUREELEMENT_MSG_NAMES = [
|
|
||||||
"SE:GetSEReaders",
|
|
||||||
"SE:OpenChannel",
|
|
||||||
"SE:CloseChannel",
|
|
||||||
"SE:TransmitAPDU"
|
|
||||||
];
|
|
||||||
|
|
||||||
const SECUREELEMENTMANAGER_CONTRACTID =
|
|
||||||
"@mozilla.org/secureelement/parent-manager;1";
|
|
||||||
const SECUREELEMENTMANAGER_CID =
|
|
||||||
Components.ID("{48f4e650-28d2-11e4-8c21-0800200c9a66}");
|
|
||||||
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
|
||||||
"@mozilla.org/parentprocessmessagemanager;1",
|
|
||||||
"nsIMessageBroadcaster");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
|
|
||||||
"@mozilla.org/uuid-generator;1",
|
|
||||||
"nsIUUIDGenerator");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
|
|
||||||
"resource://gre/modules/SEUtils.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "UiccConnector", () => {
|
|
||||||
let uiccClass = Cc["@mozilla.org/secureelement/connector/uicc;1"];
|
|
||||||
return uiccClass ? uiccClass.getService(Ci.nsISecureElementConnector) : null;
|
|
||||||
});
|
|
||||||
|
|
||||||
function getConnector(type) {
|
|
||||||
switch (type) {
|
|
||||||
case SE.TYPE_UICC:
|
|
||||||
return UiccConnector;
|
|
||||||
case SE.TYPE_ESE:
|
|
||||||
default:
|
|
||||||
debug("Unsupported SEConnector : " + type);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'gMap' is a nested dictionary object that manages all the information
|
|
||||||
* pertaining to channels for a given application (appId). It manages the
|
|
||||||
* relationship between given application and its opened channels.
|
|
||||||
*/
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "gMap", function() {
|
|
||||||
return {
|
|
||||||
// example structure of AppInfoMap
|
|
||||||
// {
|
|
||||||
// "appId1": {
|
|
||||||
// target: target1,
|
|
||||||
// channels: {
|
|
||||||
// "channelToken1": {
|
|
||||||
// seType: "uicc",
|
|
||||||
// aid: "aid1",
|
|
||||||
// channelNumber: 1
|
|
||||||
// },
|
|
||||||
// "channelToken2": { ... }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// "appId2": { ... }
|
|
||||||
// }
|
|
||||||
appInfoMap: {},
|
|
||||||
|
|
||||||
registerSecureElementTarget: function(appId, target) {
|
|
||||||
if (this.isAppIdRegistered(appId)) {
|
|
||||||
debug("AppId: " + appId + "already registered");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.appInfoMap[appId] = {
|
|
||||||
target: target,
|
|
||||||
channels: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
debug("Registered a new SE target " + appId);
|
|
||||||
},
|
|
||||||
|
|
||||||
unregisterSecureElementTarget: function(target) {
|
|
||||||
let appId = Object.keys(this.appInfoMap).find((id) => {
|
|
||||||
return this.appInfoMap[id].target === target;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!appId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("Unregistered SE Target for AppId: " + appId);
|
|
||||||
delete this.appInfoMap[appId];
|
|
||||||
},
|
|
||||||
|
|
||||||
isAppIdRegistered: function(appId) {
|
|
||||||
return this.appInfoMap[appId] !== undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
getChannelCountByAppIdType: function(appId, type) {
|
|
||||||
return Object.keys(this.appInfoMap[appId].channels)
|
|
||||||
.reduce((cnt, ch) => ch.type === type ? ++cnt : cnt, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Add channel to the appId. Upon successfully adding the entry
|
|
||||||
// this function will return the 'token'
|
|
||||||
addChannel: function(appId, type, aid, channelNumber) {
|
|
||||||
let token = UUIDGenerator.generateUUID().toString();
|
|
||||||
this.appInfoMap[appId].channels[token] = {
|
|
||||||
seType: type,
|
|
||||||
aid: aid,
|
|
||||||
channelNumber: channelNumber
|
|
||||||
};
|
|
||||||
return token;
|
|
||||||
},
|
|
||||||
|
|
||||||
removeChannel: function(appId, channelToken) {
|
|
||||||
if (this.appInfoMap[appId].channels[channelToken]) {
|
|
||||||
debug("Deleting channel with token : " + channelToken);
|
|
||||||
delete this.appInfoMap[appId].channels[channelToken];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getChannel: function(appId, channelToken) {
|
|
||||||
if (!this.appInfoMap[appId].channels[channelToken]) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.appInfoMap[appId].channels[channelToken];
|
|
||||||
},
|
|
||||||
|
|
||||||
getChannelsByTarget: function(target) {
|
|
||||||
let appId = Object.keys(this.appInfoMap).find((id) => {
|
|
||||||
return this.appInfoMap[id].target === target;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!appId) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.keys(this.appInfoMap[appId].channels)
|
|
||||||
.map(token => this.appInfoMap[appId].channels[token]);
|
|
||||||
},
|
|
||||||
|
|
||||||
getTargets: function() {
|
|
||||||
return Object.keys(this.appInfoMap)
|
|
||||||
.map(appId => this.appInfoMap[appId].target);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'SecureElementManager' is the main object that handles IPC messages from
|
|
||||||
* child process. It interacts with other objects such as 'gMap' & 'Connector
|
|
||||||
* instances (UiccConnector, eSEConnector)' to perform various
|
|
||||||
* SE-related (open, close, transmit) operations.
|
|
||||||
* @TODO: Bug 1118097 Support slot based SE/reader names
|
|
||||||
* @TODO: Bug 1118101 Introduce SE type specific permissions
|
|
||||||
*/
|
|
||||||
function SecureElementManager() {
|
|
||||||
this._registerMessageListeners();
|
|
||||||
this._registerSEListeners();
|
|
||||||
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
|
||||||
this._acEnforcer =
|
|
||||||
Cc["@mozilla.org/secureelement/access-control/ace;1"]
|
|
||||||
.getService(Ci.nsIAccessControlEnforcer);
|
|
||||||
}
|
|
||||||
|
|
||||||
SecureElementManager.prototype = {
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([
|
|
||||||
Ci.nsIMessageListener,
|
|
||||||
Ci.nsISEListener,
|
|
||||||
Ci.nsIObserver]),
|
|
||||||
classID: SECUREELEMENTMANAGER_CID,
|
|
||||||
classInfo: XPCOMUtils.generateCI({
|
|
||||||
classID: SECUREELEMENTMANAGER_CID,
|
|
||||||
classDescription: "SecureElementManager",
|
|
||||||
interfaces: [Ci.nsIMessageListener,
|
|
||||||
Ci.nsISEListener,
|
|
||||||
Ci.nsIObserver]
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Stores information about supported SE types and their presence.
|
|
||||||
// key: secure element type, value: (Boolean) is present/accessible
|
|
||||||
_sePresence: {},
|
|
||||||
|
|
||||||
_acEnforcer: null,
|
|
||||||
|
|
||||||
_shutdown: function() {
|
|
||||||
this._acEnforcer = null;
|
|
||||||
this.secureelement = null;
|
|
||||||
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
|
||||||
this._unregisterMessageListeners();
|
|
||||||
this._unregisterSEListeners();
|
|
||||||
},
|
|
||||||
|
|
||||||
_registerMessageListeners: function() {
|
|
||||||
ppmm.addMessageListener("child-process-shutdown", this);
|
|
||||||
for (let msgname of SE_IPC_SECUREELEMENT_MSG_NAMES) {
|
|
||||||
ppmm.addMessageListener(msgname, this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_unregisterMessageListeners: function() {
|
|
||||||
ppmm.removeMessageListener("child-process-shutdown", this);
|
|
||||||
for (let msgname of SE_IPC_SECUREELEMENT_MSG_NAMES) {
|
|
||||||
ppmm.removeMessageListener(msgname, this);
|
|
||||||
}
|
|
||||||
ppmm = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_registerSEListeners: function() {
|
|
||||||
let connector = getConnector(SE.TYPE_UICC);
|
|
||||||
if (!connector) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._sePresence[SE.TYPE_UICC] = false;
|
|
||||||
connector.registerListener(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
_unregisterSEListeners: function() {
|
|
||||||
Object.keys(this._sePresence).forEach((type) => {
|
|
||||||
let connector = getConnector(type);
|
|
||||||
if (connector) {
|
|
||||||
connector.unregisterListener(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._sePresence = {};
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySEPresenceChanged: function(type, isPresent) {
|
|
||||||
// we need to notify all targets, even those without open channels,
|
|
||||||
// app could've stored the reader without actually using it
|
|
||||||
debug("notifying DOM about SE state change");
|
|
||||||
this._sePresence[type] = isPresent;
|
|
||||||
gMap.getTargets().forEach(target => {
|
|
||||||
let result = { type: type, isPresent: isPresent };
|
|
||||||
target.sendAsyncMessage("SE:ReaderPresenceChanged", { result: result });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_canOpenChannel: function(appId, type) {
|
|
||||||
let opened = gMap.getChannelCountByAppIdType(appId, type);
|
|
||||||
let limit = SE.MAX_CHANNELS_ALLOWED_PER_SESSION;
|
|
||||||
// UICC basic channel is not accessible see comment in se_consts.js
|
|
||||||
limit = type === SE.TYPE_UICC ? limit - 1 : limit;
|
|
||||||
return opened < limit;
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleOpenChannel: function(msg, callback) {
|
|
||||||
if (!this._canOpenChannel(msg.appId, msg.type)) {
|
|
||||||
debug("Max channels per session exceed");
|
|
||||||
callback({ error: SE.ERROR_GENERIC });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let connector = getConnector(msg.type);
|
|
||||||
if (!connector) {
|
|
||||||
debug("No SE connector available");
|
|
||||||
callback({ error: SE.ERROR_NOTPRESENT });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._acEnforcer.isAccessAllowed(msg.appId, msg.type, msg.aid)
|
|
||||||
.then((allowed) => {
|
|
||||||
if (!allowed) {
|
|
||||||
callback({ error: SE.ERROR_SECURITY });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connector.openChannel(SEUtils.byteArrayToHexString(msg.aid), {
|
|
||||||
|
|
||||||
notifyOpenChannelSuccess: (channelNumber, openResponse) => {
|
|
||||||
// Add the new 'channel' to the map upon success
|
|
||||||
let channelToken =
|
|
||||||
gMap.addChannel(msg.appId, msg.type, msg.aid, channelNumber);
|
|
||||||
if (channelToken) {
|
|
||||||
callback({
|
|
||||||
error: SE.ERROR_NONE,
|
|
||||||
channelToken: channelToken,
|
|
||||||
isBasicChannel: (channelNumber === SE.BASIC_CHANNEL),
|
|
||||||
openResponse: SEUtils.hexStringToByteArray(openResponse)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback({ error: SE.ERROR_GENERIC });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: (reason) => {
|
|
||||||
debug("Failed to open the channel to AID : " +
|
|
||||||
SEUtils.byteArrayToHexString(msg.aid) +
|
|
||||||
", Rejected with Reason : " + reason);
|
|
||||||
callback({ error: SE.ERROR_GENERIC, reason: reason, response: [] });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
debug("Failed to get info from accessControlEnforcer " + error);
|
|
||||||
callback({ error: SE.ERROR_SECURITY });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleTransmit: function(msg, callback) {
|
|
||||||
let channel = gMap.getChannel(msg.appId, msg.channelToken);
|
|
||||||
if (!channel) {
|
|
||||||
debug("Invalid token:" + msg.channelToken + ", appId: " + msg.appId);
|
|
||||||
callback({ error: SE.ERROR_GENERIC });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let connector = getConnector(channel.seType);
|
|
||||||
if (!connector) {
|
|
||||||
debug("No SE connector available");
|
|
||||||
callback({ error: SE.ERROR_NOTPRESENT });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bug 1137533 - ACE GPAccessRulesManager APDU filters
|
|
||||||
connector.exchangeAPDU(channel.channelNumber, msg.apdu.cla, msg.apdu.ins,
|
|
||||||
msg.apdu.p1, msg.apdu.p2,
|
|
||||||
SEUtils.byteArrayToHexString(msg.apdu.data),
|
|
||||||
msg.apdu.le, {
|
|
||||||
notifyExchangeAPDUResponse: (sw1, sw2, response) => {
|
|
||||||
callback({
|
|
||||||
error: SE.ERROR_NONE,
|
|
||||||
sw1: sw1,
|
|
||||||
sw2: sw2,
|
|
||||||
response: SEUtils.hexStringToByteArray(response)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: (reason) => {
|
|
||||||
debug("Transmit failed, rejected with Reason : " + reason);
|
|
||||||
callback({ error: SE.ERROR_INVALIDAPPLICATION, reason: reason });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleCloseChannel: function(msg, callback) {
|
|
||||||
let channel = gMap.getChannel(msg.appId, msg.channelToken);
|
|
||||||
if (!channel) {
|
|
||||||
debug("Invalid token:" + msg.channelToken + ", appId:" + msg.appId);
|
|
||||||
callback({ error: SE.ERROR_GENERIC });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let connector = getConnector(channel.seType);
|
|
||||||
if (!connector) {
|
|
||||||
debug("No SE connector available");
|
|
||||||
callback({ error: SE.ERROR_NOTPRESENT });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
connector.closeChannel(channel.channelNumber, {
|
|
||||||
notifyCloseChannelSuccess: () => {
|
|
||||||
gMap.removeChannel(msg.appId, msg.channelToken);
|
|
||||||
callback({ error: SE.ERROR_NONE });
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: (reason) => {
|
|
||||||
debug("Failed to close channel with token: " + msg.channelToken +
|
|
||||||
", reason: "+ reason);
|
|
||||||
callback({ error: SE.ERROR_BADSTATE, reason: reason });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleGetSEReadersRequest: function(msg, target, callback) {
|
|
||||||
gMap.registerSecureElementTarget(msg.appId, target);
|
|
||||||
let readers = Object.keys(this._sePresence).map(type => {
|
|
||||||
return { type: type, isPresent: this._sePresence[type] };
|
|
||||||
});
|
|
||||||
callback({ readers: readers, error: SE.ERROR_NONE });
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleChildProcessShutdown: function(target) {
|
|
||||||
let channels = gMap.getChannelsByTarget(target);
|
|
||||||
|
|
||||||
let createCb = (seType, channelNumber) => {
|
|
||||||
return {
|
|
||||||
notifyCloseChannelSuccess: () => {
|
|
||||||
debug("closed " + seType + ", channel " + channelNumber);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: (reason) => {
|
|
||||||
debug("Failed to close " + seType + " channel " +
|
|
||||||
channelNumber + ", reason: " + reason);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
channels.forEach((channel) => {
|
|
||||||
let connector = getConnector(channel.seType);
|
|
||||||
if (!connector) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
connector.closeChannel(channel.channelNumber,
|
|
||||||
createCb(channel.seType, channel.channelNumber));
|
|
||||||
});
|
|
||||||
|
|
||||||
gMap.unregisterSecureElementTarget(target);
|
|
||||||
},
|
|
||||||
|
|
||||||
_sendSEResponse: function(msg, result) {
|
|
||||||
let promiseStatus = (result.error === SE.ERROR_NONE) ? "Resolved" : "Rejected";
|
|
||||||
result.resolverId = msg.data.resolverId;
|
|
||||||
msg.target.sendAsyncMessage(msg.name + promiseStatus, {result: result});
|
|
||||||
},
|
|
||||||
|
|
||||||
_isValidMessage: function(msg) {
|
|
||||||
let appIdValid = gMap.isAppIdRegistered(msg.data.appId);
|
|
||||||
return msg.name === "SE:GetSEReaders" ? true : appIdValid;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsIMessageListener interface methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
receiveMessage: function(msg) {
|
|
||||||
DEBUG && debug("Received '" + msg.name + "' message from content process" +
|
|
||||||
": " + JSON.stringify(msg.data));
|
|
||||||
|
|
||||||
if (msg.name === "child-process-shutdown") {
|
|
||||||
this._handleChildProcessShutdown(msg.target);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SE_IPC_SECUREELEMENT_MSG_NAMES.indexOf(msg.name) !== -1) {
|
|
||||||
if (!msg.target.assertPermission("secureelement-manage")) {
|
|
||||||
debug("SecureElement message " + msg.name + " from a content process " +
|
|
||||||
"with no 'secureelement-manage' privileges.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug("Ignoring unknown message type: " + msg.name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let callback = (result) => this._sendSEResponse(msg, result);
|
|
||||||
if (!this._isValidMessage(msg)) {
|
|
||||||
debug("Message not valid");
|
|
||||||
callback({ error: SE.ERROR_GENERIC });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (msg.name) {
|
|
||||||
case "SE:GetSEReaders":
|
|
||||||
this._handleGetSEReadersRequest(msg.data, msg.target, callback);
|
|
||||||
break;
|
|
||||||
case "SE:OpenChannel":
|
|
||||||
this._handleOpenChannel(msg.data, callback);
|
|
||||||
break;
|
|
||||||
case "SE:CloseChannel":
|
|
||||||
this._handleCloseChannel(msg.data, callback);
|
|
||||||
break;
|
|
||||||
case "SE:TransmitAPDU":
|
|
||||||
this._handleTransmit(msg.data, callback);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsIObserver interface methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
observe: function(subject, topic, data) {
|
|
||||||
if (topic === NS_XPCOM_SHUTDOWN_OBSERVER_ID) {
|
|
||||||
this._shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SecureElementManager]);
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
# SecureElementManager
|
|
||||||
component {48f4e650-28d2-11e4-8c21-0800200c9a66} SecureElement.js
|
|
||||||
contract @mozilla.org/secureelement/parent-manager;1 {48f4e650-28d2-11e4-8c21-0800200c9a66}
|
|
||||||
category profile-after-change SecureElementManager @mozilla.org/secureelement/parent-manager;1
|
|
||||||
|
|
@ -1,360 +0,0 @@
|
||||||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Copyright © 2014, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/* globals Components, XPCOMUtils, SE, dump, libcutils, Services,
|
|
||||||
iccService, SEUtils */
|
|
||||||
|
|
||||||
const { interfaces: Ci, utils: Cu, results: Cr } = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
Cu.import("resource://gre/modules/systemlibs.js");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "SE", function() {
|
|
||||||
let obj = {};
|
|
||||||
Cu.import("resource://gre/modules/se_consts.js", obj);
|
|
||||||
return obj;
|
|
||||||
});
|
|
||||||
|
|
||||||
// set to true in se_consts.js to see debug messages
|
|
||||||
var DEBUG = SE.DEBUG_CONNECTOR;
|
|
||||||
function debug(s) {
|
|
||||||
if (DEBUG) {
|
|
||||||
dump("-*- UiccConnector: " + s + "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
|
|
||||||
"resource://gre/modules/SEUtils.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "iccService",
|
|
||||||
"@mozilla.org/icc/iccservice;1",
|
|
||||||
"nsIIccService");
|
|
||||||
|
|
||||||
const UICCCONNECTOR_CONTRACTID =
|
|
||||||
"@mozilla.org/secureelement/connector/uicc;1";
|
|
||||||
const UICCCONNECTOR_CID =
|
|
||||||
Components.ID("{8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4}");
|
|
||||||
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
|
||||||
|
|
||||||
// TODO: Bug 1118099 - Add multi-sim support.
|
|
||||||
// In the Multi-sim, there is more than one client.
|
|
||||||
// For now, use default clientID as 0. Ideally, SE parent process would like to
|
|
||||||
// know which clients (uicc slot) are connected to CLF over SWP interface.
|
|
||||||
const PREFERRED_UICC_CLIENTID =
|
|
||||||
libcutils.property_get("ro.moz.se.def_client_id", "0");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'UiccConnector' object is a wrapper over iccService's channel management
|
|
||||||
* related interfaces that implements nsISecureElementConnector interface.
|
|
||||||
*/
|
|
||||||
function UiccConnector() {
|
|
||||||
this._init();
|
|
||||||
}
|
|
||||||
|
|
||||||
UiccConnector.prototype = {
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISecureElementConnector,
|
|
||||||
Ci.nsIIccListener]),
|
|
||||||
classID: UICCCONNECTOR_CID,
|
|
||||||
classInfo: XPCOMUtils.generateCI({
|
|
||||||
classID: UICCCONNECTOR_CID,
|
|
||||||
contractID: UICCCONNECTOR_CONTRACTID,
|
|
||||||
classDescription: "UiccConnector",
|
|
||||||
interfaces: [Ci.nsISecureElementConnector,
|
|
||||||
Ci.nsIIccListener,
|
|
||||||
Ci.nsIObserver]
|
|
||||||
}),
|
|
||||||
|
|
||||||
_SEListeners: [],
|
|
||||||
_isPresent: false,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
|
||||||
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
|
|
||||||
icc.registerListener(this);
|
|
||||||
|
|
||||||
// Update the state in order to avoid race condition.
|
|
||||||
// By this time, 'notifyCardStateChanged (with proper card state)'
|
|
||||||
// may have occurred already before this module initialization.
|
|
||||||
this._updatePresenceState();
|
|
||||||
},
|
|
||||||
|
|
||||||
_shutdown: function() {
|
|
||||||
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
|
||||||
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
|
|
||||||
icc.unregisterListener(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updatePresenceState: function() {
|
|
||||||
let uiccNotReadyStates = [
|
|
||||||
Ci.nsIIcc.CARD_STATE_UNKNOWN,
|
|
||||||
Ci.nsIIcc.CARD_STATE_ILLEGAL,
|
|
||||||
Ci.nsIIcc.CARD_STATE_PERSONALIZATION_IN_PROGRESS,
|
|
||||||
Ci.nsIIcc.CARD_STATE_PERMANENT_BLOCKED,
|
|
||||||
Ci.nsIIcc.CARD_STATE_UNDETECTED
|
|
||||||
];
|
|
||||||
|
|
||||||
let cardState = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID).cardState;
|
|
||||||
let uiccPresent = cardState !== null &&
|
|
||||||
uiccNotReadyStates.indexOf(cardState) == -1;
|
|
||||||
|
|
||||||
if (this._isPresent === uiccPresent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("Uicc presence changed " + this._isPresent + " -> " + uiccPresent);
|
|
||||||
this._isPresent = uiccPresent;
|
|
||||||
this._SEListeners.forEach((listener) => {
|
|
||||||
listener.notifySEPresenceChanged(SE.TYPE_UICC, this._isPresent);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// See GP Spec, 11.1.4 Class Byte Coding
|
|
||||||
_setChannelToCLAByte: function(cla, channel) {
|
|
||||||
if (channel < SE.LOGICAL_CHANNEL_NUMBER_LIMIT) {
|
|
||||||
// b7 = 0 indicates the first interindustry class byte coding
|
|
||||||
cla = (cla & 0x9C) & 0xFF | channel;
|
|
||||||
} else if (channel < SE.SUPPLEMENTARY_LOGICAL_CHANNEL_NUMBER_LIMIT) {
|
|
||||||
// b7 = 1 indicates the further interindustry class byte coding
|
|
||||||
cla = (cla & 0xB0) & 0xFF | 0x40 | (channel - SE.LOGICAL_CHANNEL_NUMBER_LIMIT);
|
|
||||||
} else {
|
|
||||||
debug("Channel number must be within [0..19]");
|
|
||||||
return SE.ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
return cla;
|
|
||||||
},
|
|
||||||
|
|
||||||
_doGetOpenResponse: function(channel, length, callback) {
|
|
||||||
// Le value is set. It means that this is a request for all available
|
|
||||||
// response bytes.
|
|
||||||
let cla = this._setChannelToCLAByte(SE.CLA_GET_RESPONSE, channel);
|
|
||||||
this.exchangeAPDU(channel, cla, SE.INS_GET_RESPONSE, 0x00, 0x00,
|
|
||||||
null, length, {
|
|
||||||
notifyExchangeAPDUResponse: function(sw1, sw2, response) {
|
|
||||||
debug("GET Response : " + response);
|
|
||||||
if (callback) {
|
|
||||||
callback({
|
|
||||||
error: SE.ERROR_NONE,
|
|
||||||
sw1: sw1,
|
|
||||||
sw2: sw2,
|
|
||||||
response: response
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: function(reason) {
|
|
||||||
debug("Failed to get open response: " +
|
|
||||||
", Rejected with Reason : " + reason);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: SE.ERROR_INVALIDAPPLICATION, reason: reason });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_doIccExchangeAPDU: function(channel, cla, ins, p1, p2, p3,
|
|
||||||
data, appendResp, callback) {
|
|
||||||
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
|
|
||||||
icc.iccExchangeAPDU(channel, cla & 0xFC, ins, p1, p2, p3, data, {
|
|
||||||
notifyExchangeAPDUResponse: (sw1, sw2, response) => {
|
|
||||||
debug("sw1 : " + sw1 + ", sw2 : " + sw2 + ", response : " + response);
|
|
||||||
|
|
||||||
// According to ETSI TS 102 221 , Section 7.2.2.3.1,
|
|
||||||
// Enforce 'Procedure bytes' checks before notifying the callback.
|
|
||||||
// Note that 'Procedure bytes'are special cases.
|
|
||||||
// There is no need to handle '0x60' procedure byte as it implies
|
|
||||||
// no-action from SE stack perspective. This procedure byte is not
|
|
||||||
// notified to application layer.
|
|
||||||
if (sw1 === 0x6C) {
|
|
||||||
// Use the previous command header with length as second procedure
|
|
||||||
// byte (SW2) as received and repeat the procedure.
|
|
||||||
|
|
||||||
// Recursive! and Pass empty response '' as args, since '0x6C'
|
|
||||||
// procedure does not have to deal with appended responses.
|
|
||||||
this._doIccExchangeAPDU(channel, cla, ins, p1, p2,
|
|
||||||
sw2, data, "", callback);
|
|
||||||
} else if (sw1 === 0x61) {
|
|
||||||
// Since the terminal waited for a second procedure byte and
|
|
||||||
// received it (sw2), send a GET RESPONSE command header to the UICC
|
|
||||||
// with a maximum length of 'XX', where 'XX' is the value of the
|
|
||||||
// second procedure byte (SW2).
|
|
||||||
|
|
||||||
let claWithChannel = this._setChannelToCLAByte(SE.CLA_GET_RESPONSE,
|
|
||||||
channel);
|
|
||||||
|
|
||||||
// Recursive, with GET RESPONSE bytes and '0x61' procedure IS interested
|
|
||||||
// in appended responses. Pass appended response and note that p3=sw2.
|
|
||||||
this._doIccExchangeAPDU(channel, claWithChannel, SE.INS_GET_RESPONSE,
|
|
||||||
0x00, 0x00, sw2, null,
|
|
||||||
(response ? response + appendResp : appendResp),
|
|
||||||
callback);
|
|
||||||
} else if (callback) {
|
|
||||||
callback.notifyExchangeAPDUResponse(sw1, sw2, response);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: (reason) => {
|
|
||||||
debug("Failed to trasmit C-APDU over the channel # : " + channel +
|
|
||||||
", Rejected with Reason : " + reason);
|
|
||||||
if (callback) {
|
|
||||||
callback.notifyError(reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsISecureElementConnector interface methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens a channel on a default clientId
|
|
||||||
*/
|
|
||||||
openChannel: function(aid, callback) {
|
|
||||||
if (!this._isPresent) {
|
|
||||||
callback.notifyError(SE.ERROR_NOTPRESENT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Bug 1118106: Handle Resource management / leaks by persisting
|
|
||||||
// the newly opened channel in some persistent storage so that when this
|
|
||||||
// module gets restarted (say after opening a channel) in the event of
|
|
||||||
// some erroneous conditions such as gecko restart /, crash it can read
|
|
||||||
// the persistent storage to check if there are any held resources
|
|
||||||
// (opened channels) and close them.
|
|
||||||
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
|
|
||||||
icc.iccOpenChannel(aid, {
|
|
||||||
notifyOpenChannelSuccess: (channel) => {
|
|
||||||
this._doGetOpenResponse(channel, 0x00, function(result) {
|
|
||||||
if (callback) {
|
|
||||||
callback.notifyOpenChannelSuccess(channel, result.response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: (reason) => {
|
|
||||||
debug("Failed to open the channel to AID : " + aid +
|
|
||||||
", Rejected with Reason : " + reason);
|
|
||||||
if (callback) {
|
|
||||||
callback.notifyError(reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transmit the C-APDU (command) on default clientId.
|
|
||||||
*/
|
|
||||||
exchangeAPDU: function(channel, cla, ins, p1, p2, data, le, callback) {
|
|
||||||
if (!this._isPresent) {
|
|
||||||
callback.notifyError(SE.ERROR_NOTPRESENT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data && data.length % 2 !== 0) {
|
|
||||||
callback.notifyError("Data should be a hex string with length % 2 === 0");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cla = this._setChannelToCLAByte(cla, channel);
|
|
||||||
let lc = data ? data.length / 2 : 0;
|
|
||||||
let p3 = lc || le;
|
|
||||||
|
|
||||||
if (lc && (le !== -1)) {
|
|
||||||
data += SEUtils.byteArrayToHexString([le]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass empty response '' as args as we are not interested in appended
|
|
||||||
// responses yet!
|
|
||||||
debug("exchangeAPDU on Channel # " + channel);
|
|
||||||
this._doIccExchangeAPDU(channel, cla, ins, p1, p2, p3, data, "",
|
|
||||||
callback);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the channel on default clientId.
|
|
||||||
*/
|
|
||||||
closeChannel: function(channel, callback) {
|
|
||||||
if (!this._isPresent) {
|
|
||||||
callback.notifyError(SE.ERROR_NOTPRESENT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
|
|
||||||
icc.iccCloseChannel(channel, {
|
|
||||||
notifyCloseChannelSuccess: function() {
|
|
||||||
debug("closeChannel successfully closed the channel # : " + channel);
|
|
||||||
if (callback) {
|
|
||||||
callback.notifyCloseChannelSuccess();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: function(reason) {
|
|
||||||
debug("Failed to close the channel # : " + channel +
|
|
||||||
", Rejected with Reason : " + reason);
|
|
||||||
if (callback) {
|
|
||||||
callback.notifyError(reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
registerListener: function(listener) {
|
|
||||||
if (this._SEListeners.indexOf(listener) !== -1) {
|
|
||||||
throw Cr.NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._SEListeners.push(listener);
|
|
||||||
// immediately notify listener about the current state
|
|
||||||
listener.notifySEPresenceChanged(SE.TYPE_UICC, this._isPresent);
|
|
||||||
},
|
|
||||||
|
|
||||||
unregisterListener: function(listener) {
|
|
||||||
let idx = this._SEListeners.indexOf(listener);
|
|
||||||
if (idx !== -1) {
|
|
||||||
this._SEListeners.splice(idx, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsIIccListener interface methods.
|
|
||||||
*/
|
|
||||||
notifyStkCommand: function() {},
|
|
||||||
|
|
||||||
notifyStkSessionEnd: function() {},
|
|
||||||
|
|
||||||
notifyIccInfoChanged: function() {},
|
|
||||||
|
|
||||||
notifyCardStateChanged: function() {
|
|
||||||
debug("Card state changed, updating UICC presence.");
|
|
||||||
this._updatePresenceState();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsIObserver interface methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
observe: function(subject, topic, data) {
|
|
||||||
if (topic === NS_XPCOM_SHUTDOWN_OBSERVER_ID) {
|
|
||||||
this._shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UiccConnector]);
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
# Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
# UiccConnector
|
|
||||||
component {8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4} UiccConnector.js
|
|
||||||
contract @mozilla.org/secureelement/connector/uicc;1 {8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* Copyright © 2015, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
/* Object Directory File (ODF) is an elementary file which contain
|
|
||||||
pointers to other EFs. It is specified in PKCS#15 section 6.7. */
|
|
||||||
this.ODF_DF = [0x50, 0x31];
|
|
||||||
|
|
||||||
/* ISO 7816-4: secure messaging */
|
|
||||||
this.CLA_SM = 0x00;
|
|
||||||
|
|
||||||
/* ISO 7816-4, 5.4.1 table 11 */
|
|
||||||
this.INS_SF = 0xA4; // select file
|
|
||||||
this.INS_GR = 0xC0; // get response
|
|
||||||
this.INS_RB = 0xB0; // read binary
|
|
||||||
|
|
||||||
/* ISO 7816-4: select file, see 6.11.3, table 58 & 59 */
|
|
||||||
this.P1_SF_DF = 0x00; // select DF
|
|
||||||
this.P2_SF_FCP = 0x04; // return FCP
|
|
||||||
|
|
||||||
/* ISO 7816-4: read binary, 6.1.3. P1 and P2 describe offset of the first byte
|
|
||||||
to be read. We always read the whole files at the moment. */
|
|
||||||
this.P1_RB = 0x00;
|
|
||||||
this.P2_RB = 0x00;
|
|
||||||
|
|
||||||
/* ISO 7816-4: get response, 7.1.3 table 74, P1-P2 '0000' (other values RFU) */
|
|
||||||
this.P1_GR = 0x00;
|
|
||||||
this.P2_GR = 0x00;
|
|
||||||
|
|
||||||
/* ISO 7816-4: 5.1.5 File Control Information, Table 1. For FCP and FMD. */
|
|
||||||
this.TAG_PROPRIETARY = 0x00;
|
|
||||||
this.TAG_NON_TLV = 0x53;
|
|
||||||
this.TAG_BER_TLV = 0x73;
|
|
||||||
|
|
||||||
/* ASN.1 tags */
|
|
||||||
this.TAG_SEQUENCE = 0x30;
|
|
||||||
this.TAG_OCTETSTRING = 0x04;
|
|
||||||
this.TAG_OID = 0x06; // Object Identifier
|
|
||||||
|
|
||||||
/* ISO 7816-4: 5.1.5 File Control Information, Templates. */
|
|
||||||
this.TAG_FCP = 0x62; // File control parameters template
|
|
||||||
this.TAG_FMD = 0x64; // File management data template
|
|
||||||
this.TAG_FCI = 0x6F; // File control information template
|
|
||||||
|
|
||||||
/* EF_DIR tags */
|
|
||||||
this.TAG_APPLTEMPLATE = 0x61;
|
|
||||||
this.TAG_APPLIDENTIFIER = 0x4F;
|
|
||||||
this.TAG_APPLLABEL = 0x50;
|
|
||||||
this.TAG_APPLPATH = 0x51;
|
|
||||||
|
|
||||||
this.TAG_GPD_ALL = 0x82; // EF-ACRules - GPD spec. "all applets"
|
|
||||||
|
|
||||||
/* Generic TLVs that are parsed */
|
|
||||||
this.TAG_GPD_AID = 0xA0; // AID in the EF-ACRules - GPD spec, "one applet"
|
|
||||||
this.TAG_EXTERNALDO = 0xA1; // External data objects - PKCS#15
|
|
||||||
this.TAG_INDIRECT = 0xA5; // Indirect value.
|
|
||||||
this.TAG_EF_ODF = 0xA7; // Elemenetary File Object Directory File
|
|
||||||
|
|
||||||
// Allow this file to be imported via Components.utils.import().
|
|
||||||
this.EXPORTED_SYMBOLS = Object.keys(this);
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* Copyright © 2015, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
#include "nsISupports.idl"
|
|
||||||
|
|
||||||
interface nsIVariant;
|
|
||||||
|
|
||||||
[scriptable, uuid(4994a960-26d9-4d71-82dd-4505bd97bf2a)]
|
|
||||||
interface nsIAccessControlEnforcer : nsISupports
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determines whether application identified by its ID should be allowed
|
|
||||||
* to access Secure Element's applet identified by its AID. Decision
|
|
||||||
* is made according to the GPD specification.
|
|
||||||
*
|
|
||||||
* @param localId
|
|
||||||
* ID of an application accessing SE
|
|
||||||
* @param seType
|
|
||||||
* Type of the SE.
|
|
||||||
* @param aid
|
|
||||||
* AID of a SE applet
|
|
||||||
* @return Promise which is resolved to true if access should be allowed,
|
|
||||||
* false otherwise, and rejected if the application contains
|
|
||||||
* no developer certificate.
|
|
||||||
*/
|
|
||||||
jsval isAccessAllowed(in unsigned long localId,
|
|
||||||
in DOMString seType,
|
|
||||||
in DOMString aid);
|
|
||||||
};
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* Copyright © 2015, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
#include "nsISupports.idl"
|
|
||||||
|
|
||||||
[scriptable, uuid(7baedd2a-3189-4b03-b2a3-34016043b5e2)]
|
|
||||||
interface nsIAccessRulesManager : nsISupports
|
|
||||||
{
|
|
||||||
/* Wildcard: rule allows all applications to access an SE applet */
|
|
||||||
const unsigned short ALLOW_ALL = 1;
|
|
||||||
/* Wildcard: rule denies all applications to access an SE applet */
|
|
||||||
const unsigned short DENY_ALL = 2;
|
|
||||||
/* Wildcard: rule allows application(s) access to all SE applets */
|
|
||||||
const unsigned short ALL_APPLET = 3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initiates Access Rules Manager, this should perform the initial
|
|
||||||
* reading of rules from access rule source
|
|
||||||
* @return Promise which is resolved if init is successful or rejected
|
|
||||||
* otherwise
|
|
||||||
*/
|
|
||||||
jsval init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves all access rules.
|
|
||||||
*
|
|
||||||
* Rules are stored in an array. Each rule contains the following properties:
|
|
||||||
* - applet - describes an SE applet referenced by this rule. Might equal
|
|
||||||
* to an applet AID (as a byte array), or to a wildcard "all"
|
|
||||||
* meaning all applets.
|
|
||||||
* - application - describes an application referenced by this rule. Might
|
|
||||||
* be an array of developer certificate hashes (each as
|
|
||||||
* a byte array) in which case it lists all applications
|
|
||||||
* allowed access. Alternatively, might equal to wildcard
|
|
||||||
* "allowed-all" or "denied-all".
|
|
||||||
*
|
|
||||||
* Example rule format:
|
|
||||||
* [{ applet: ALL_APPLET,
|
|
||||||
* application: [[0x01, 0x02, ..., 0x20],
|
|
||||||
* [0x20, 0x19, ...., 0x01]],
|
|
||||||
* { applet: [0x00, 0x01, ..., 0x05],
|
|
||||||
* application: ALLOW_ALL}}]
|
|
||||||
*
|
|
||||||
* @return Promise which resolves with Array containing parsed access rules
|
|
||||||
*/
|
|
||||||
jsval getAccessRules();
|
|
||||||
};
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
/* 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(1ff3f35a-1b6f-4e65-a89e-a363b8604cd7)]
|
|
||||||
interface nsISEChannelCallback : nsISupports
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Callback function to notify on successfully opening a logical channel.
|
|
||||||
*
|
|
||||||
* @param channel
|
|
||||||
* The Channel Number/Handle that is successfully opened.
|
|
||||||
* @param openResponse
|
|
||||||
* Response from SE for OpenChannel operation.
|
|
||||||
*/
|
|
||||||
void notifyOpenChannelSuccess(in long channel, in DOMString openResponse);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function to notify on successfully closing the logical channel.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void notifyCloseChannelSuccess();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function to notify the status of 'seExchangeAPDU' command.
|
|
||||||
*
|
|
||||||
* @param sw1
|
|
||||||
* Response's First Status Byte
|
|
||||||
* @param sw2
|
|
||||||
* Response's Second Status Byte
|
|
||||||
* @param data
|
|
||||||
* Response's data
|
|
||||||
*/
|
|
||||||
void notifyExchangeAPDUResponse(in octet sw1,
|
|
||||||
in octet sw2,
|
|
||||||
in DOMString data);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function to notify error
|
|
||||||
*
|
|
||||||
* @param error
|
|
||||||
* Error describing the reason for failure.
|
|
||||||
*/
|
|
||||||
void notifyError(in DOMString error);
|
|
||||||
};
|
|
||||||
|
|
||||||
[scriptable, uuid(417f59ee-f582-45b9-9a4e-e9dcefecb4f7)]
|
|
||||||
interface nsISEListener : nsISupports
|
|
||||||
{
|
|
||||||
void notifySEPresenceChanged(in DOMString seType, in boolean isPresent);
|
|
||||||
};
|
|
||||||
|
|
||||||
[scriptable, uuid(3cef313a-1d01-432d-9cd2-6610a80911f3)]
|
|
||||||
interface nsISecureElementConnector : nsISupports
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Open a logical communication channel with the specific secure element type
|
|
||||||
*
|
|
||||||
* @param aid
|
|
||||||
* Application Identifier of the Card Applet on the secure element.
|
|
||||||
* @param callback
|
|
||||||
* callback to notify the result of the operation.
|
|
||||||
*/
|
|
||||||
void openChannel(in DOMString aid,
|
|
||||||
in nsISEChannelCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exchanges APDU channel with the specific secure element type
|
|
||||||
*
|
|
||||||
* @param channel
|
|
||||||
* Channel on which C-APDU to be transmitted.
|
|
||||||
* @param cla
|
|
||||||
Class Byte.
|
|
||||||
* @param ins
|
|
||||||
Instruction Byte
|
|
||||||
* @param p1
|
|
||||||
Reference parameter first byte
|
|
||||||
* @param p2
|
|
||||||
Reference parameter second byte
|
|
||||||
* Refer to 3G TS 31.101 , 10.2 'Command APDU Structure' for all the cases.
|
|
||||||
* @param data
|
|
||||||
Sequence of C-APDU data octets
|
|
||||||
* @param le [optional]
|
|
||||||
* le is the length of expected response. If the response is not expected,
|
|
||||||
it should be explicitly set to -1.
|
|
||||||
* @param callback
|
|
||||||
* callback to notify the result of the operation.
|
|
||||||
*/
|
|
||||||
void exchangeAPDU(in long channel,
|
|
||||||
in octet cla,
|
|
||||||
in octet ins,
|
|
||||||
in octet p1,
|
|
||||||
in octet p2,
|
|
||||||
in DOMString data,
|
|
||||||
in short le,
|
|
||||||
in nsISEChannelCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the logical communication channel to the specific secure element type
|
|
||||||
*
|
|
||||||
* @param channel
|
|
||||||
* Channel to be closed.
|
|
||||||
* @param callback
|
|
||||||
* callback to notify the result of the operation.
|
|
||||||
*/
|
|
||||||
void closeChannel(in long channel,
|
|
||||||
in nsISEChannelCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a Secure Element listener
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
void registerListener(in nsISEListener listener);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregister a Secure Element listener
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
void unregisterListener(in nsISEListener listener);
|
|
||||||
};
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Copyright © 2014, Deutsche Telekom, Inc. */
|
|
||||||
|
|
||||||
// Set to true to debug SecureElement (SE) stack
|
|
||||||
this.DEBUG_ALL = false;
|
|
||||||
|
|
||||||
// Set individually to debug specific layers
|
|
||||||
this.DEBUG_CONNECTOR = DEBUG_ALL || false;
|
|
||||||
this.DEBUG_ACE = DEBUG_ALL || false ;
|
|
||||||
this.DEBUG_SE = DEBUG_ALL || false ;
|
|
||||||
|
|
||||||
// Maximun logical channels per session.
|
|
||||||
// For 'uicc' SE type this value is 3, as opening a basic channel' : 0
|
|
||||||
// is not allowed for security reasons. In such scenarios, possible
|
|
||||||
// supplementary logical channels available are : [1, 2, or 3].
|
|
||||||
// However,Other SE types may support upto max 4 (including '0').
|
|
||||||
this.MAX_CHANNELS_ALLOWED_PER_SESSION = 4;
|
|
||||||
|
|
||||||
this.BASIC_CHANNEL = 0;
|
|
||||||
|
|
||||||
// According GPCardSpec 2.2
|
|
||||||
this.MAX_APDU_LEN = 255; // including APDU header
|
|
||||||
|
|
||||||
// CLA (1 byte) + INS (1 byte) + P1 (1 byte) + P2 (1 byte)
|
|
||||||
this.APDU_HEADER_LEN = 4;
|
|
||||||
|
|
||||||
this.LOGICAL_CHANNEL_NUMBER_LIMIT = 4;
|
|
||||||
this.SUPPLEMENTARY_LOGICAL_CHANNEL_NUMBER_LIMIT = 20;
|
|
||||||
|
|
||||||
this.MIN_AID_LEN = 5;
|
|
||||||
this.MAX_AID_LEN = 16;
|
|
||||||
|
|
||||||
this.CLA_GET_RESPONSE = 0x00;
|
|
||||||
|
|
||||||
this.INS_SELECT = 0xA4;
|
|
||||||
this.INS_MANAGE_CHANNEL = 0x70;
|
|
||||||
this.INS_GET_RESPONSE = 0xC0;
|
|
||||||
|
|
||||||
// Match the following errors with SecureElement.webidl's SEError enum values
|
|
||||||
this.ERROR_NONE = "";
|
|
||||||
this.ERROR_SECURITY = "SESecurityError";
|
|
||||||
this.ERROR_IO = "SEIoError";
|
|
||||||
this.ERROR_BADSTATE = "SEBadStateError";
|
|
||||||
this.ERROR_INVALIDCHANNEL = "SEInvalidChannelError";
|
|
||||||
this.ERROR_INVALIDAPPLICATION = "SEInvalidApplicationError";
|
|
||||||
this.ERROR_GENERIC = "SEGenericError";
|
|
||||||
this.ERROR_NOTPRESENT = "SENotPresentError";
|
|
||||||
this.ERROR_ILLEGALPARAMETER = "SEIllegalParameterError";
|
|
||||||
|
|
||||||
this.TYPE_UICC = "uicc";
|
|
||||||
this.TYPE_ESE = "eSE";
|
|
||||||
|
|
||||||
// Allow this file to be imported via Components.utils.import().
|
|
||||||
this.EXPORTED_SYMBOLS = Object.keys(this);
|
|
||||||
|
|
@ -11,30 +11,6 @@ if CONFIG['MOZ_SECUREELEMENT']:
|
||||||
'DOMSecureElement.manifest',
|
'DOMSecureElement.manifest',
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_SECUREELEMENT']:
|
|
||||||
EXTRA_COMPONENTS += [
|
|
||||||
'gonk/ACEService.js',
|
|
||||||
'gonk/ACEService.manifest',
|
|
||||||
'gonk/GPAccessRulesManager.js',
|
|
||||||
'gonk/GPAccessRulesManager.manifest',
|
|
||||||
'gonk/SecureElement.js',
|
|
||||||
'gonk/SecureElement.manifest',
|
|
||||||
]
|
|
||||||
XPIDL_MODULE = 'dom_secureelement'
|
|
||||||
XPIDL_SOURCES += [
|
|
||||||
'gonk/nsIAccessControlEnforcer.idl',
|
|
||||||
'gonk/nsIAccessRulesManager.idl',
|
|
||||||
'gonk/nsISecureElementConnector.idl',
|
|
||||||
]
|
|
||||||
EXTRA_JS_MODULES += [
|
|
||||||
'gonk/gp_consts.js',
|
|
||||||
'gonk/se_consts.js',
|
|
||||||
'SEUtils.jsm'
|
|
||||||
]
|
|
||||||
XPCSHELL_TESTS_MANIFESTS += [
|
|
||||||
'tests/unit/xpcshell.ini'
|
|
||||||
]
|
|
||||||
|
|
||||||
include('/ipc/chromium/chromium-config.mozbuild')
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
|
||||||
|
|
@ -1,181 +0,0 @@
|
||||||
/* 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 "nsIDocument.h"
|
|
||||||
#include "nsIDOMClassInfo.h"
|
|
||||||
#include "nsIDOMEvent.h"
|
|
||||||
#include "nsIDOMEventListener.h"
|
|
||||||
#include "nsPIDOMWindow.h"
|
|
||||||
#include "nsIDocShell.h"
|
|
||||||
#include "nsIPermissionManager.h"
|
|
||||||
#include "nsIInterfaceRequestorUtils.h"
|
|
||||||
#include "AudioChannelManager.h"
|
|
||||||
#include "mozilla/dom/AudioChannelManagerBinding.h"
|
|
||||||
#include "mozilla/dom/nsBrowserElement.h"
|
|
||||||
#include "mozilla/Services.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace dom {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
NS_IMPL_QUERY_INTERFACE_INHERITED(AudioChannelManager, DOMEventTargetHelper,
|
|
||||||
nsIDOMEventListener)
|
|
||||||
NS_IMPL_ADDREF_INHERITED(AudioChannelManager, DOMEventTargetHelper)
|
|
||||||
NS_IMPL_RELEASE_INHERITED(AudioChannelManager, DOMEventTargetHelper)
|
|
||||||
|
|
||||||
AudioChannelManager::AudioChannelManager()
|
|
||||||
: mVolumeChannel(-1)
|
|
||||||
{
|
|
||||||
hal::RegisterSwitchObserver(hal::SWITCH_HEADPHONES, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioChannelManager::~AudioChannelManager()
|
|
||||||
{
|
|
||||||
hal::UnregisterSwitchObserver(hal::SWITCH_HEADPHONES, this);
|
|
||||||
|
|
||||||
nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner());
|
|
||||||
NS_ENSURE_TRUE_VOID(target);
|
|
||||||
|
|
||||||
target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
|
||||||
this,
|
|
||||||
/* useCapture = */ true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
AudioChannelManager::Init(nsPIDOMWindowInner* aWindow)
|
|
||||||
{
|
|
||||||
BindToOwner(aWindow);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
|
|
||||||
NS_ENSURE_TRUE_VOID(target);
|
|
||||||
|
|
||||||
target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
|
||||||
this,
|
|
||||||
/* useCapture = */ true,
|
|
||||||
/* wantsUntrusted = */ false);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSObject*
|
|
||||||
AudioChannelManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
||||||
{
|
|
||||||
return AudioChannelManagerBinding::Wrap(aCx, this, aGivenProto);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
AudioChannelManager::Notify(const hal::SwitchEvent& aEvent)
|
|
||||||
{
|
|
||||||
mState = Some(aEvent.status());
|
|
||||||
|
|
||||||
DispatchTrustedEvent(NS_LITERAL_STRING("headphoneschange"));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
AudioChannelManager::SetVolumeControlChannel(const nsAString& aChannel)
|
|
||||||
{
|
|
||||||
if (aChannel.EqualsASCII("publicnotification")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioChannel newChannel = AudioChannelService::GetAudioChannel(aChannel);
|
|
||||||
|
|
||||||
// Only normal channel doesn't need permission.
|
|
||||||
if (newChannel != AudioChannel::Normal) {
|
|
||||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
|
||||||
services::GetPermissionManager();
|
|
||||||
if (!permissionManager) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
|
|
||||||
permissionManager->TestPermissionFromWindow(GetOwner(),
|
|
||||||
nsCString(NS_LITERAL_CSTRING("audio-channel-") +
|
|
||||||
NS_ConvertUTF16toUTF8(aChannel)).get(), &perm);
|
|
||||||
if (perm != nsIPermissionManager::ALLOW_ACTION) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mVolumeChannel == (int32_t)newChannel) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mVolumeChannel = (int32_t)newChannel;
|
|
||||||
|
|
||||||
NotifyVolumeControlChannelChanged();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
AudioChannelManager::GetVolumeControlChannel(nsAString & aChannel)
|
|
||||||
{
|
|
||||||
if (mVolumeChannel >= 0) {
|
|
||||||
AudioChannelService::GetAudioChannelString(
|
|
||||||
static_cast<AudioChannel>(mVolumeChannel),
|
|
||||||
aChannel);
|
|
||||||
} else {
|
|
||||||
aChannel.AssignASCII("");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
AudioChannelManager::NotifyVolumeControlChannelChanged()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
|
|
||||||
NS_ENSURE_TRUE_VOID(docshell);
|
|
||||||
|
|
||||||
bool isActive = false;
|
|
||||||
docshell->GetIsActive(&isActive);
|
|
||||||
|
|
||||||
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
|
|
||||||
if (!service) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isActive) {
|
|
||||||
service->SetDefaultVolumeControlChannel(mVolumeChannel, isActive);
|
|
||||||
} else {
|
|
||||||
service->SetDefaultVolumeControlChannel(-1, isActive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
AudioChannelManager::HandleEvent(nsIDOMEvent* aEvent)
|
|
||||||
{
|
|
||||||
nsAutoString type;
|
|
||||||
aEvent->GetType(type);
|
|
||||||
|
|
||||||
if (type.EqualsLiteral("visibilitychange")) {
|
|
||||||
NotifyVolumeControlChannelChanged();
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
AudioChannelManager::GetAllowedAudioChannels(
|
|
||||||
nsTArray<RefPtr<BrowserElementAudioChannel>>& aAudioChannels,
|
|
||||||
ErrorResult& aRv)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aAudioChannels.IsEmpty());
|
|
||||||
|
|
||||||
// Only main process is supported.
|
|
||||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
|
||||||
aRv.Throw(NS_ERROR_FAILURE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
|
|
||||||
if (NS_WARN_IF(!window)) {
|
|
||||||
aRv.Throw(NS_ERROR_FAILURE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsBrowserElement::GenerateAllowedAudioChannels(window, nullptr, nullptr,
|
|
||||||
aAudioChannels, aRv);
|
|
||||||
NS_WARNING_ASSERTION(!aRv.Failed(), "GenerateAllowedAudioChannels failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace dom
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
/* 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_system_AudioChannelManager_h
|
|
||||||
#define mozilla_dom_system_AudioChannelManager_h
|
|
||||||
|
|
||||||
#include "mozilla/dom/BrowserElementAudioChannel.h"
|
|
||||||
#include "mozilla/DOMEventTargetHelper.h"
|
|
||||||
#include "mozilla/Hal.h"
|
|
||||||
#include "mozilla/HalTypes.h"
|
|
||||||
#include "mozilla/Maybe.h"
|
|
||||||
#include "AudioChannelService.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace hal {
|
|
||||||
class SwitchEvent;
|
|
||||||
typedef Observer<SwitchEvent> SwitchObserver;
|
|
||||||
} // namespace hal
|
|
||||||
|
|
||||||
namespace dom {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
class AudioChannelManager final
|
|
||||||
: public DOMEventTargetHelper
|
|
||||||
, public hal::SwitchObserver
|
|
||||||
, public nsIDOMEventListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AudioChannelManager();
|
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS_INHERITED
|
|
||||||
NS_DECL_NSIDOMEVENTLISTENER
|
|
||||||
|
|
||||||
void Notify(const hal::SwitchEvent& aEvent);
|
|
||||||
|
|
||||||
void Init(nsPIDOMWindowInner* aWindow);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebIDL Interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
nsPIDOMWindowInner* GetParentObject() const
|
|
||||||
{
|
|
||||||
return GetOwner();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
|
||||||
|
|
||||||
bool Headphones()
|
|
||||||
{
|
|
||||||
// Bug 929139 - Remove the assert check for SWITCH_STATE_UNKNOWN.
|
|
||||||
// If any devices (ex: emulator) didn't have the corresponding sys node for
|
|
||||||
// headset switch state then GonkSwitch will report the unknown state.
|
|
||||||
// So it is possible to get unknown state here.
|
|
||||||
if (mState.isNothing()) {
|
|
||||||
mState = Some(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
|
|
||||||
}
|
|
||||||
return mState.value() != hal::SWITCH_STATE_OFF &&
|
|
||||||
mState.value() != hal::SWITCH_STATE_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SetVolumeControlChannel(const nsAString& aChannel);
|
|
||||||
|
|
||||||
bool GetVolumeControlChannel(nsAString& aChannel);
|
|
||||||
|
|
||||||
IMPL_EVENT_HANDLER(headphoneschange)
|
|
||||||
|
|
||||||
void GetAllowedAudioChannels(
|
|
||||||
nsTArray<RefPtr<mozilla::dom::BrowserElementAudioChannel>>& aAudioChannels,
|
|
||||||
mozilla::ErrorResult& aRv);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~AudioChannelManager();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void NotifyVolumeControlChannelChanged();
|
|
||||||
|
|
||||||
Maybe<hal::SwitchState> mState;
|
|
||||||
int32_t mVolumeChannel;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace dom
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_dom_system_AudioChannelManager_h
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,180 +0,0 @@
|
||||||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef mozilla_dom_system_b2g_audiomanager_h__
|
|
||||||
#define mozilla_dom_system_b2g_audiomanager_h__
|
|
||||||
|
|
||||||
#include "mozilla/HalTypes.h"
|
|
||||||
#include "mozilla/Observer.h"
|
|
||||||
#include "mozilla/UniquePtr.h"
|
|
||||||
#include "nsAutoPtr.h"
|
|
||||||
#include "nsDataHashtable.h"
|
|
||||||
#include "nsIAudioManager.h"
|
|
||||||
#include "nsIObserver.h"
|
|
||||||
#include "android_audio/AudioSystem.h"
|
|
||||||
|
|
||||||
// {b2b51423-502d-4d77-89b3-7786b562b084}
|
|
||||||
#define NS_AUDIOMANAGER_CID {0x94f6fd70, 0x7615, 0x4af9, \
|
|
||||||
{0x89, 0x10, 0xf9, 0x3c, 0x55, 0xe6, 0x62, 0xec}}
|
|
||||||
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
|
|
||||||
|
|
||||||
class nsISettingsServiceLock;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace hal {
|
|
||||||
class SwitchEvent;
|
|
||||||
typedef Observer<SwitchEvent> SwitchObserver;
|
|
||||||
} // namespace hal
|
|
||||||
|
|
||||||
namespace dom {
|
|
||||||
namespace gonk {
|
|
||||||
|
|
||||||
class VolumeInitCallback;
|
|
||||||
|
|
||||||
class AudioManager final : public nsIAudioManager
|
|
||||||
, public nsIObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static already_AddRefed<AudioManager> GetInstance();
|
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIAUDIOMANAGER
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
|
|
||||||
// Validate whether the volume index is within the range
|
|
||||||
nsresult ValidateVolumeIndex(int32_t aStream, uint32_t aIndex) const;
|
|
||||||
|
|
||||||
// Called when android AudioFlinger in mediaserver is died
|
|
||||||
void HandleAudioFlingerDied();
|
|
||||||
|
|
||||||
void HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent);
|
|
||||||
|
|
||||||
class VolumeStreamState {
|
|
||||||
public:
|
|
||||||
explicit VolumeStreamState(AudioManager& aManager, int32_t aStreamType);
|
|
||||||
int32_t GetStreamType()
|
|
||||||
{
|
|
||||||
return mStreamType;
|
|
||||||
}
|
|
||||||
bool IsDevicesChanged(bool aFromCache = true);
|
|
||||||
void ClearDevicesChanged();
|
|
||||||
uint32_t GetLastDevices()
|
|
||||||
{
|
|
||||||
return mLastDevices;
|
|
||||||
}
|
|
||||||
bool IsVolumeIndexesChanged();
|
|
||||||
void ClearVolumeIndexesChanged();
|
|
||||||
void InitStreamVolume();
|
|
||||||
uint32_t GetMaxIndex();
|
|
||||||
uint32_t GetDefaultIndex();
|
|
||||||
uint32_t GetVolumeIndex();
|
|
||||||
uint32_t GetVolumeIndex(uint32_t aDevice);
|
|
||||||
void ClearCurrentVolumeUpdated();
|
|
||||||
// Set volume index to all active devices.
|
|
||||||
// Active devices are chosen by android AudioPolicyManager.
|
|
||||||
nsresult SetVolumeIndexToActiveDevices(uint32_t aIndex);
|
|
||||||
// Set volume index to all alias streams for device. Alias streams have same volume.
|
|
||||||
nsresult SetVolumeIndexToAliasStreams(uint32_t aIndex, uint32_t aDevice);
|
|
||||||
nsresult SetVolumeIndexToConsistentDeviceIfNeeded(uint32_t aIndex, uint32_t aDevice);
|
|
||||||
nsresult SetVolumeIndex(uint32_t aIndex, uint32_t aDevice, bool aUpdateCache = true);
|
|
||||||
// Restore volume index to all devices. Called when AudioFlinger is restarted.
|
|
||||||
void RestoreVolumeIndexToAllDevices();
|
|
||||||
private:
|
|
||||||
AudioManager& mManager;
|
|
||||||
const int32_t mStreamType;
|
|
||||||
uint32_t mLastDevices;
|
|
||||||
bool mIsDevicesChanged;
|
|
||||||
bool mIsVolumeIndexesChanged;
|
|
||||||
nsDataHashtable<nsUint32HashKey, uint32_t> mVolumeIndexes;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
int32_t mPhoneState;
|
|
||||||
|
|
||||||
bool mIsVolumeInited;
|
|
||||||
|
|
||||||
// A bitwise variable for volume update of audio output devices,
|
|
||||||
// clear it after store the value into database.
|
|
||||||
uint32_t mAudioOutDevicesUpdated;
|
|
||||||
|
|
||||||
// Connected devices that are controlled by setDeviceConnectionState()
|
|
||||||
nsDataHashtable<nsUint32HashKey, nsCString> mConnectedDevices;
|
|
||||||
|
|
||||||
nsDataHashtable<nsUint32HashKey, uint32_t> mAudioDeviceTableIdMaps;
|
|
||||||
|
|
||||||
bool mSwitchDone;
|
|
||||||
|
|
||||||
#if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
|
|
||||||
bool mBluetoothA2dpEnabled;
|
|
||||||
#endif
|
|
||||||
#ifdef MOZ_B2G_BT
|
|
||||||
bool mA2dpSwitchDone;
|
|
||||||
#endif
|
|
||||||
nsTArray<UniquePtr<VolumeStreamState> > mStreamStates;
|
|
||||||
uint32_t mLastChannelVolume[AUDIO_STREAM_CNT];
|
|
||||||
|
|
||||||
bool IsFmOutConnected();
|
|
||||||
|
|
||||||
nsresult SetStreamVolumeForDevice(int32_t aStream,
|
|
||||||
uint32_t aIndex,
|
|
||||||
uint32_t aDevice);
|
|
||||||
nsresult SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex);
|
|
||||||
nsresult GetStreamVolumeIndex(int32_t aStream, uint32_t* aIndex);
|
|
||||||
|
|
||||||
void UpdateCachedActiveDevicesForStreams();
|
|
||||||
uint32_t GetDevicesForStream(int32_t aStream, bool aFromCache = true);
|
|
||||||
uint32_t GetDeviceForStream(int32_t aStream);
|
|
||||||
// Choose one device as representative of active devices.
|
|
||||||
static uint32_t SelectDeviceFromDevices(uint32_t aOutDevices);
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsAutoPtr<mozilla::hal::SwitchObserver> mObserver;
|
|
||||||
|
|
||||||
void HandleBluetoothStatusChanged(nsISupports* aSubject,
|
|
||||||
const char* aTopic,
|
|
||||||
const nsCString aAddress);
|
|
||||||
void HandleAudioChannelProcessChanged();
|
|
||||||
|
|
||||||
// Append the audio output device to the volume setting string.
|
|
||||||
nsAutoCString AppendDeviceToVolumeSetting(const char* aName,
|
|
||||||
uint32_t aDevice);
|
|
||||||
|
|
||||||
// We store the volume setting in the database, these are related functions.
|
|
||||||
void InitVolumeFromDatabase();
|
|
||||||
void MaybeUpdateVolumeSettingToDatabase(bool aForce = false);
|
|
||||||
|
|
||||||
// Promise functions.
|
|
||||||
void InitDeviceVolumeSucceeded();
|
|
||||||
void InitDeviceVolumeFailed(const char* aError);
|
|
||||||
|
|
||||||
void AudioOutDeviceUpdated(uint32_t aDevice);
|
|
||||||
|
|
||||||
void UpdateHeadsetConnectionState(hal::SwitchState aState);
|
|
||||||
void UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName);
|
|
||||||
void SetAllDeviceConnectionStates();
|
|
||||||
|
|
||||||
AudioManager();
|
|
||||||
~AudioManager();
|
|
||||||
|
|
||||||
friend class VolumeInitCallback;
|
|
||||||
friend class VolumeStreamState;
|
|
||||||
friend class GonkAudioPortCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace gonk */
|
|
||||||
} /* namespace dom */
|
|
||||||
} /* namespace mozilla */
|
|
||||||
|
|
||||||
#endif // mozilla_dom_system_b2g_audiomanager_h__
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,101 +0,0 @@
|
||||||
/* 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_system_automounter_h__
|
|
||||||
#define mozilla_system_automounter_h__
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
class nsCString;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
// AutoMounter modes
|
|
||||||
#define AUTOMOUNTER_DISABLE 0
|
|
||||||
#define AUTOMOUNTER_ENABLE_UMS 1
|
|
||||||
#define AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED 2
|
|
||||||
#define AUTOMOUNTER_ENABLE_MTP 3
|
|
||||||
|
|
||||||
// Automounter statuses
|
|
||||||
#define AUTOMOUNTER_STATUS_DISABLED 0
|
|
||||||
#define AUTOMOUNTER_STATUS_ENABLED 1
|
|
||||||
#define AUTOMOUNTER_STATUS_FILES_OPEN 2
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the automounter. This causes some of the phone's
|
|
||||||
* directories to show up on the host when the phone is plugged
|
|
||||||
* into the host via USB.
|
|
||||||
*
|
|
||||||
* When the AutoMounter starts, it will poll the current state
|
|
||||||
* of affairs (usb cable plugged in, automounter enabled, etc)
|
|
||||||
* and try to make the state of the volumes match.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
InitAutoMounter();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the enabled state of the automounter.
|
|
||||||
*
|
|
||||||
* This will in turn cause the automounter to re-evaluate
|
|
||||||
* whether it should mount/unmount/share/unshare volumes.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
SetAutoMounterMode(int32_t aMode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reports the status of the automounter.
|
|
||||||
*/
|
|
||||||
int32_t
|
|
||||||
GetAutoMounterStatus();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sharing mode of an individual volume.
|
|
||||||
*
|
|
||||||
* If a volume is enabled for sharing, and the autmounter
|
|
||||||
* is in a state to share, then the volume will be shared
|
|
||||||
* with the PC.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the volume with specified volume name.
|
|
||||||
*
|
|
||||||
* If the volume is ready to format, automounter
|
|
||||||
* will unmount it, format it and then mount it again.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
AutoMounterFormatVolume(const nsCString& aVolumeName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mounts the volume with specified volume name.
|
|
||||||
*
|
|
||||||
* If the volume is already unmounted, automounter
|
|
||||||
* will mount it. Otherwise automounter will skip this.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
AutoMounterMountVolume(const nsCString& aVolumeName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unmounts the volume with specified volume name.
|
|
||||||
*
|
|
||||||
* If the volume is already mounted, automounter
|
|
||||||
* will unmount it. Otherwise automounter will skip this.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
AutoMounterUnmountVolume(const nsCString& aVolumeName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shuts down the automounter.
|
|
||||||
*
|
|
||||||
* This leaves the volumes in whatever state they're in.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ShutdownAutoMounter();
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_automounter_h__
|
|
||||||
|
|
@ -1,284 +0,0 @@
|
||||||
/* 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 "AutoMounter.h"
|
|
||||||
#include "AutoMounterSetting.h"
|
|
||||||
|
|
||||||
#include "base/message_loop.h"
|
|
||||||
#include "jsapi.h"
|
|
||||||
#include "mozilla/Services.h"
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsContentUtils.h"
|
|
||||||
#include "nsDebug.h"
|
|
||||||
#include "nsIObserverService.h"
|
|
||||||
#include "nsISettingsService.h"
|
|
||||||
#include "nsJSUtils.h"
|
|
||||||
#include "nsPrintfCString.h"
|
|
||||||
#include "nsServiceManagerUtils.h"
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "nsThreadUtils.h"
|
|
||||||
#include "xpcpublic.h"
|
|
||||||
#include "mozilla/dom/ScriptSettings.h"
|
|
||||||
#include "mozilla/Attributes.h"
|
|
||||||
#include "mozilla/dom/BindingUtils.h"
|
|
||||||
#include "mozilla/dom/SettingChangeNotificationBinding.h"
|
|
||||||
|
|
||||||
#undef LOG
|
|
||||||
#undef ERR
|
|
||||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounterSetting" , ## args)
|
|
||||||
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounterSetting" , ## args)
|
|
||||||
|
|
||||||
#define UMS_MODE "ums.mode"
|
|
||||||
#define UMS_STATUS "ums.status"
|
|
||||||
#define UMS_VOLUME_ENABLED_PREFIX "ums.volume."
|
|
||||||
#define UMS_VOLUME_ENABLED_SUFFIX ".enabled"
|
|
||||||
#define MOZSETTINGS_CHANGED "mozsettings-changed"
|
|
||||||
|
|
||||||
using namespace mozilla::dom;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
class SettingsServiceCallback final : public nsISettingsServiceCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
|
||||||
|
|
||||||
SettingsServiceCallback() {}
|
|
||||||
|
|
||||||
NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
|
|
||||||
{
|
|
||||||
if (aResult.isInt32()) {
|
|
||||||
int32_t mode = aResult.toInt32();
|
|
||||||
SetAutoMounterMode(mode);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD HandleError(const nsAString& aName)
|
|
||||||
{
|
|
||||||
ERR("SettingsCallback::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
~SettingsServiceCallback() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(SettingsServiceCallback, nsISettingsServiceCallback)
|
|
||||||
|
|
||||||
class CheckVolumeSettingsCallback final : public nsISettingsServiceCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
|
||||||
|
|
||||||
CheckVolumeSettingsCallback(const nsACString& aVolumeName)
|
|
||||||
: mVolumeName(aVolumeName) {}
|
|
||||||
|
|
||||||
NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
|
|
||||||
{
|
|
||||||
if (aResult.isBoolean()) {
|
|
||||||
bool isSharingEnabled = aResult.toBoolean();
|
|
||||||
SetAutoMounterSharingMode(mVolumeName, isSharingEnabled);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD HandleError(const nsAString& aName)
|
|
||||||
{
|
|
||||||
ERR("CheckVolumeSettingsCallback::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
~CheckVolumeSettingsCallback() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsCString mVolumeName;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(CheckVolumeSettingsCallback, nsISettingsServiceCallback)
|
|
||||||
|
|
||||||
AutoMounterSetting::AutoMounterSetting()
|
|
||||||
: mStatus(AUTOMOUNTER_STATUS_DISABLED)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
// Setup an observer to watch changes to the setting
|
|
||||||
nsCOMPtr<nsIObserverService> observerService =
|
|
||||||
mozilla::services::GetObserverService();
|
|
||||||
if (!observerService) {
|
|
||||||
ERR("GetObserverService failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nsresult rv;
|
|
||||||
rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
ERR("AddObserver failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force ums.mode to be 0 initially. We do this because settings are persisted.
|
|
||||||
// We don't want UMS to be enabled until such time as the phone is unlocked,
|
|
||||||
// and gaia/apps/system/js/storage.js takes care of detecting when the phone
|
|
||||||
// becomes unlocked and changes ums.mode appropriately.
|
|
||||||
nsCOMPtr<nsISettingsService> settingsService =
|
|
||||||
do_GetService("@mozilla.org/settingsService;1");
|
|
||||||
if (!settingsService) {
|
|
||||||
ERR("Failed to get settingsLock service!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nsCOMPtr<nsISettingsServiceLock> lock;
|
|
||||||
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
|
|
||||||
nsCOMPtr<nsISettingsServiceCallback> callback = new SettingsServiceCallback();
|
|
||||||
JS::Rooted<JS::Value> value(RootingCx());
|
|
||||||
value.setInt32(AUTOMOUNTER_DISABLE);
|
|
||||||
lock->Set(UMS_MODE, value, callback, nullptr);
|
|
||||||
value.setInt32(mStatus);
|
|
||||||
lock->Set(UMS_STATUS, value, nullptr, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoMounterSetting::~AutoMounterSetting()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIObserverService> observerService =
|
|
||||||
mozilla::services::GetObserverService();
|
|
||||||
if (observerService) {
|
|
||||||
observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(AutoMounterSetting, nsIObserver)
|
|
||||||
|
|
||||||
const char *
|
|
||||||
AutoMounterSetting::StatusStr(int32_t aStatus)
|
|
||||||
{
|
|
||||||
switch (aStatus) {
|
|
||||||
case AUTOMOUNTER_STATUS_DISABLED: return "Disabled";
|
|
||||||
case AUTOMOUNTER_STATUS_ENABLED: return "Enabled";
|
|
||||||
case AUTOMOUNTER_STATUS_FILES_OPEN: return "FilesOpen";
|
|
||||||
}
|
|
||||||
return "??? Unknown ???";
|
|
||||||
}
|
|
||||||
|
|
||||||
class CheckVolumeSettingsRunnable : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CheckVolumeSettingsRunnable(const nsACString& aVolumeName)
|
|
||||||
: mVolumeName(aVolumeName) {}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
nsCOMPtr<nsISettingsService> settingsService =
|
|
||||||
do_GetService("@mozilla.org/settingsService;1");
|
|
||||||
NS_ENSURE_TRUE(settingsService, NS_ERROR_FAILURE);
|
|
||||||
nsCOMPtr<nsISettingsServiceLock> lock;
|
|
||||||
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
|
|
||||||
nsCOMPtr<nsISettingsServiceCallback> callback =
|
|
||||||
new CheckVolumeSettingsCallback(mVolumeName);
|
|
||||||
nsPrintfCString setting(UMS_VOLUME_ENABLED_PREFIX "%s" UMS_VOLUME_ENABLED_SUFFIX,
|
|
||||||
mVolumeName.get());
|
|
||||||
lock->Get(setting.get(), callback);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsCString mVolumeName;
|
|
||||||
};
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
AutoMounterSetting::CheckVolumeSettings(const nsACString& aVolumeName)
|
|
||||||
{
|
|
||||||
NS_DispatchToMainThread(new CheckVolumeSettingsRunnable(aVolumeName));
|
|
||||||
}
|
|
||||||
|
|
||||||
class SetStatusRunnable : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SetStatusRunnable(int32_t aStatus) : mStatus(aStatus) {}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
nsCOMPtr<nsISettingsService> settingsService =
|
|
||||||
do_GetService("@mozilla.org/settingsService;1");
|
|
||||||
NS_ENSURE_TRUE(settingsService, NS_ERROR_FAILURE);
|
|
||||||
nsCOMPtr<nsISettingsServiceLock> lock;
|
|
||||||
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
|
|
||||||
// lock may be null if this gets called during shutdown.
|
|
||||||
if (lock) {
|
|
||||||
JS::Rooted<JS::Value> value(RootingCx(),
|
|
||||||
JS::Int32Value(mStatus));
|
|
||||||
lock->Set(UMS_STATUS, value, nullptr, nullptr);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int32_t mStatus;
|
|
||||||
};
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
AutoMounterSetting::SetStatus(int32_t aStatus)
|
|
||||||
{
|
|
||||||
if (aStatus != mStatus) {
|
|
||||||
LOG("Changing status from '%s' to '%s'",
|
|
||||||
StatusStr(mStatus), StatusStr(aStatus));
|
|
||||||
mStatus = aStatus;
|
|
||||||
NS_DispatchToMainThread(new SetStatusRunnable(aStatus));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
AutoMounterSetting::Observe(nsISupports* aSubject,
|
|
||||||
const char* aTopic,
|
|
||||||
const char16_t* aData)
|
|
||||||
{
|
|
||||||
if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that this function gets called for any and all settings changes,
|
|
||||||
// so we need to carefully check if we have the one we're interested in.
|
|
||||||
//
|
|
||||||
// The string that we're interested in will be a JSON string that looks like:
|
|
||||||
// {"key":"ums.autoMount","value":true}
|
|
||||||
|
|
||||||
RootedDictionary<SettingChangeNotification> setting(RootingCx());
|
|
||||||
if (!WrappedJSToDictionary(aSubject, setting)) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for ums.mode changes
|
|
||||||
if (setting.mKey.EqualsASCII(UMS_MODE)) {
|
|
||||||
if (!setting.mValue.isInt32()) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
int32_t mode = setting.mValue.toInt32();
|
|
||||||
SetAutoMounterMode(mode);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for ums.volume.NAME.enabled
|
|
||||||
if (StringBeginsWith(setting.mKey, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_PREFIX)) &&
|
|
||||||
StringEndsWith(setting.mKey, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_SUFFIX))) {
|
|
||||||
if (!setting.mValue.isBoolean()) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
const size_t prefixLen = sizeof(UMS_VOLUME_ENABLED_PREFIX) - 1;
|
|
||||||
const size_t suffixLen = sizeof(UMS_VOLUME_ENABLED_SUFFIX) - 1;
|
|
||||||
nsDependentSubstring volumeName =
|
|
||||||
Substring(setting.mKey, prefixLen, setting.mKey.Length() - prefixLen - suffixLen);
|
|
||||||
bool isSharingEnabled = setting.mValue.toBoolean();
|
|
||||||
SetAutoMounterSharingMode(NS_LossyConvertUTF16toASCII(volumeName), isSharingEnabled);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
/* 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_system_automountersetting_h__
|
|
||||||
#define mozilla_system_automountersetting_h__
|
|
||||||
|
|
||||||
#include "nsIObserver.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
class AutoMounterSetting : public nsIObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
|
|
||||||
AutoMounterSetting();
|
|
||||||
|
|
||||||
static void CheckVolumeSettings(const nsACString& aVolumeName);
|
|
||||||
|
|
||||||
int32_t GetStatus() { return mStatus; }
|
|
||||||
void SetStatus(int32_t aStatus);
|
|
||||||
const char *StatusStr(int32_t aStatus);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~AutoMounterSetting();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int32_t mStatus;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_automountersetting_h__
|
|
||||||
|
|
||||||
|
|
@ -1,276 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
|
|
||||||
const DATACALLINTERFACE_CONTRACTID = "@mozilla.org/datacall/interface;1";
|
|
||||||
const DATACALLINTERFACESERVICE_CONTRACTID =
|
|
||||||
"@mozilla.org/datacall/interfaceservice;1";
|
|
||||||
const DATACALLINTERFACE_CID =
|
|
||||||
Components.ID("{ff669306-4390-462a-989b-ba37fc42153f}");
|
|
||||||
const DATACALLINTERFACESERVICE_CID =
|
|
||||||
Components.ID("{e23e9337-592d-40b9-8cef-7bd47c28b72e}");
|
|
||||||
|
|
||||||
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
|
|
||||||
const TOPIC_PREF_CHANGED = "nsPref:changed";
|
|
||||||
const PREF_RIL_DEBUG_ENABLED = "ril.debugging.enabled";
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "RIL", function () {
|
|
||||||
let obj = {};
|
|
||||||
Cu.import("resource://gre/modules/ril_consts.js", obj);
|
|
||||||
return obj;
|
|
||||||
});
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gRil",
|
|
||||||
"@mozilla.org/ril;1",
|
|
||||||
"nsIRadioInterfaceLayer");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
|
|
||||||
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
|
|
||||||
"nsIMobileConnectionService");
|
|
||||||
|
|
||||||
var DEBUG = RIL.DEBUG_RIL;
|
|
||||||
|
|
||||||
function updateDebugFlag() {
|
|
||||||
// Read debug setting from pref
|
|
||||||
let debugPref;
|
|
||||||
try {
|
|
||||||
debugPref = Services.prefs.getBoolPref(PREF_RIL_DEBUG_ENABLED);
|
|
||||||
} catch (e) {
|
|
||||||
debugPref = false;
|
|
||||||
}
|
|
||||||
DEBUG = debugPref || RIL.DEBUG_RIL;
|
|
||||||
}
|
|
||||||
updateDebugFlag();
|
|
||||||
|
|
||||||
function DataCall(aAttributes) {
|
|
||||||
for (let key in aAttributes) {
|
|
||||||
if (key === "pdpType") {
|
|
||||||
// Convert pdp type into constant int value.
|
|
||||||
this[key] = RIL.RIL_DATACALL_PDP_TYPES.indexOf(aAttributes[key]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this[key] = aAttributes[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataCall.prototype = {
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCall]),
|
|
||||||
|
|
||||||
failCause: Ci.nsIDataCallInterface.DATACALL_FAIL_NONE,
|
|
||||||
suggestedRetryTime: -1,
|
|
||||||
cid: -1,
|
|
||||||
active: -1,
|
|
||||||
pdpType: -1,
|
|
||||||
ifname: null,
|
|
||||||
addreses: null,
|
|
||||||
dnses: null,
|
|
||||||
gateways: null,
|
|
||||||
pcscf: null,
|
|
||||||
mtu: -1
|
|
||||||
};
|
|
||||||
|
|
||||||
function DataCallInterfaceService() {
|
|
||||||
this._dataCallInterfaces = [];
|
|
||||||
|
|
||||||
let numClients = gRil.numRadioInterfaces;
|
|
||||||
for (let i = 0; i < numClients; i++) {
|
|
||||||
this._dataCallInterfaces.push(new DataCallInterface(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
|
|
||||||
Services.prefs.addObserver(PREF_RIL_DEBUG_ENABLED, this, false);
|
|
||||||
}
|
|
||||||
DataCallInterfaceService.prototype = {
|
|
||||||
classID: DATACALLINTERFACESERVICE_CID,
|
|
||||||
classInfo: XPCOMUtils.generateCI({
|
|
||||||
classID: DATACALLINTERFACESERVICE_CID,
|
|
||||||
contractID: DATACALLINTERFACESERVICE_CONTRACTID,
|
|
||||||
classDescription: "Data Call Interface Service",
|
|
||||||
interfaces: [Ci.nsIDataCallInterfaceService,
|
|
||||||
Ci.nsIGonkDataCallInterfaceService]
|
|
||||||
}),
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInterfaceService,
|
|
||||||
Ci.nsIGonkDataCallInterfaceService],
|
|
||||||
Ci.nsIObserver),
|
|
||||||
|
|
||||||
// An array of DataCallInterface instances.
|
|
||||||
_dataCallInterfaces: null,
|
|
||||||
|
|
||||||
debug: function(aMessage) {
|
|
||||||
dump("-*- DataCallInterfaceService: " + aMessage + "\n");
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIDataCallInterfaceService
|
|
||||||
|
|
||||||
getDataCallInterface: function(aClientId) {
|
|
||||||
let dataCallInterface = this._dataCallInterfaces[aClientId];
|
|
||||||
if (!dataCallInterface) {
|
|
||||||
throw Cr.NS_ERROR_UNEXPECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataCallInterface;
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIGonkDataCallInterfaceService
|
|
||||||
|
|
||||||
notifyDataCallListChanged: function(aClientId, aCount, aDataCalls) {
|
|
||||||
let dataCallInterface = this.getDataCallInterface(aClientId);
|
|
||||||
dataCallInterface.handleDataCallListChanged(aCount, aDataCalls);
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIObserver
|
|
||||||
|
|
||||||
observe: function(aSubject, aTopic, aData) {
|
|
||||||
switch (aTopic) {
|
|
||||||
case TOPIC_PREF_CHANGED:
|
|
||||||
if (aData === PREF_RIL_DEBUG_ENABLED) {
|
|
||||||
updateDebugFlag();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TOPIC_XPCOM_SHUTDOWN:
|
|
||||||
Services.prefs.removeObserver(PREF_RIL_DEBUG_ENABLED, this);
|
|
||||||
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function DataCallInterface(aClientId) {
|
|
||||||
this._clientId = aClientId;
|
|
||||||
this._radioInterface = gRil.getRadioInterface(aClientId);
|
|
||||||
this._listeners = [];
|
|
||||||
|
|
||||||
if (DEBUG) this.debug("DataCallInterface: " + aClientId);
|
|
||||||
}
|
|
||||||
DataCallInterface.prototype = {
|
|
||||||
classID: DATACALLINTERFACE_CID,
|
|
||||||
classInfo: XPCOMUtils.generateCI({classID: DATACALLINTERFACE_CID,
|
|
||||||
contractID: DATACALLINTERFACE_CONTRACTID,
|
|
||||||
classDescription: "Data Call Interface",
|
|
||||||
interfaces: [Ci.nsIDataCallInterface]}),
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInterface]),
|
|
||||||
|
|
||||||
debug: function(aMessage) {
|
|
||||||
dump("-*- DataCallInterface[" + this._clientId + "]: " + aMessage + "\n");
|
|
||||||
},
|
|
||||||
|
|
||||||
_clientId: -1,
|
|
||||||
|
|
||||||
_radioInterface: null,
|
|
||||||
|
|
||||||
_listeners: null,
|
|
||||||
|
|
||||||
// nsIDataCallInterface
|
|
||||||
|
|
||||||
setupDataCall: function(aApn, aUsername, aPassword, aAuthType, aPdpType,
|
|
||||||
aCallback) {
|
|
||||||
let connection =
|
|
||||||
gMobileConnectionService.getItemByServiceId(this._clientId);
|
|
||||||
let dataInfo = connection && connection.data;
|
|
||||||
let radioTechType = dataInfo.type;
|
|
||||||
let radioTechnology = RIL.GECKO_RADIO_TECH.indexOf(radioTechType);
|
|
||||||
// Convert pdp type into string value.
|
|
||||||
let pdpType = RIL.RIL_DATACALL_PDP_TYPES[aPdpType];
|
|
||||||
|
|
||||||
this._radioInterface.sendWorkerMessage("setupDataCall", {
|
|
||||||
radioTech: radioTechnology,
|
|
||||||
apn: aApn,
|
|
||||||
user: aUsername,
|
|
||||||
passwd: aPassword,
|
|
||||||
chappap: aAuthType,
|
|
||||||
pdptype: pdpType
|
|
||||||
}, (aResponse) => {
|
|
||||||
if (aResponse.errorMsg) {
|
|
||||||
aCallback.notifyError(aResponse.errorMsg);
|
|
||||||
} else {
|
|
||||||
let dataCall = new DataCall(aResponse);
|
|
||||||
aCallback.notifySetupDataCallSuccess(dataCall);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
deactivateDataCall: function(aCid, aReason, aCallback) {
|
|
||||||
this._radioInterface.sendWorkerMessage("deactivateDataCall", {
|
|
||||||
cid: aCid,
|
|
||||||
reason: aReason
|
|
||||||
}, (aResponse) => {
|
|
||||||
if (aResponse.errorMsg) {
|
|
||||||
aCallback.notifyError(aResponse.errorMsg);
|
|
||||||
} else {
|
|
||||||
aCallback.notifySuccess();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getDataCallList: function(aCallback) {
|
|
||||||
this._radioInterface.sendWorkerMessage("getDataCallList", null,
|
|
||||||
(aResponse) => {
|
|
||||||
if (aResponse.errorMsg) {
|
|
||||||
aCallback.notifyError(aResponse.errorMsg);
|
|
||||||
} else {
|
|
||||||
let dataCalls = aResponse.datacalls.map(
|
|
||||||
dataCall => new DataCall(dataCall));
|
|
||||||
aCallback.notifyGetDataCallListSuccess(dataCalls.length, dataCalls);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setDataRegistration: function(aAttach, aCallback) {
|
|
||||||
this._radioInterface.sendWorkerMessage("setDataRegistration", {
|
|
||||||
attach: aAttach
|
|
||||||
}, (aResponse) => {
|
|
||||||
if (aResponse.errorMsg) {
|
|
||||||
aCallback.notifyError(aResponse.errorMsg);
|
|
||||||
} else {
|
|
||||||
aCallback.notifySuccess();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDataCallListChanged: function(aCount, aDataCalls) {
|
|
||||||
this._notifyAllListeners("notifyDataCallListChanged", [aCount, aDataCalls]);
|
|
||||||
},
|
|
||||||
|
|
||||||
_notifyAllListeners: function(aMethodName, aArgs) {
|
|
||||||
let listeners = this._listeners.slice();
|
|
||||||
for (let listener of listeners) {
|
|
||||||
if (this._listeners.indexOf(listener) == -1) {
|
|
||||||
// Listener has been unregistered in previous run.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let handler = listener[aMethodName];
|
|
||||||
try {
|
|
||||||
handler.apply(listener, aArgs);
|
|
||||||
} catch (e) {
|
|
||||||
if (DEBUG) {
|
|
||||||
this.debug("listener for " + aMethodName + " threw an exception: " + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
registerListener: function(aListener) {
|
|
||||||
if (this._listeners.indexOf(aListener) >= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._listeners.push(aListener);
|
|
||||||
},
|
|
||||||
|
|
||||||
unregisterListener: function(aListener) {
|
|
||||||
let index = this._listeners.indexOf(aListener);
|
|
||||||
if (index >= 0) {
|
|
||||||
this._listeners.splice(index, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataCallInterfaceService]);
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# 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/.
|
|
||||||
|
|
||||||
component {e23e9337-592d-40b9-8cef-7bd47c28b72e} DataCallInterfaceService.js
|
|
||||||
contract @mozilla.org/datacall/interfaceservice;1 {e23e9337-592d-40b9-8cef-7bd47c28b72e}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +0,0 @@
|
||||||
# DataCallManager.js
|
|
||||||
component {35b9efa2-e42c-45ce-8210-0a13e6f4aadc} DataCallManager.js
|
|
||||||
contract @mozilla.org/datacall/manager;1 {35b9efa2-e42c-45ce-8210-0a13e6f4aadc}
|
|
||||||
category profile-after-change DataCallManager @mozilla.org/datacall/manager;1
|
|
||||||
|
|
@ -1,28 +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 "GeolocationUtil.h"
|
|
||||||
|
|
||||||
double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon)
|
|
||||||
{
|
|
||||||
// Use spherical law of cosines to calculate difference
|
|
||||||
// Not quite as correct as the Haversine but simpler and cheaper
|
|
||||||
const double radsInDeg = M_PI / 180.0;
|
|
||||||
const double rNewLat = aLat * radsInDeg;
|
|
||||||
const double rNewLon = aLon * radsInDeg;
|
|
||||||
const double rOldLat = aLastLat * radsInDeg;
|
|
||||||
const double rOldLon = aLastLon * radsInDeg;
|
|
||||||
// WGS84 equatorial radius of earth = 6378137m
|
|
||||||
double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
|
|
||||||
(cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
|
|
||||||
if (cosDelta > 1.0) {
|
|
||||||
cosDelta = 1.0;
|
|
||||||
} else if (cosDelta < -1.0) {
|
|
||||||
cosDelta = -1.0;
|
|
||||||
}
|
|
||||||
return acos(cosDelta) * 6378137;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,13 +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 GEOLOCATIONUTIL_H
|
|
||||||
#define GEOLOCATIONUTIL_H
|
|
||||||
|
|
||||||
double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
@ -1,706 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "GonkGPSGeolocationProvider.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <hardware/gps.h>
|
|
||||||
|
|
||||||
#include "base/task.h"
|
|
||||||
#include "GeolocationUtil.h"
|
|
||||||
#include "mozstumbler/MozStumbler.h"
|
|
||||||
#include "mozilla/Preferences.h"
|
|
||||||
#include "mozilla/Services.h"
|
|
||||||
#include "nsContentUtils.h"
|
|
||||||
#include "nsGeoPosition.h"
|
|
||||||
#include "nsIInterfaceRequestorUtils.h"
|
|
||||||
#include "nsINetworkInterface.h"
|
|
||||||
#include "nsIObserverService.h"
|
|
||||||
#include "nsJSUtils.h"
|
|
||||||
#include "nsPrintfCString.h"
|
|
||||||
#include "nsServiceManagerUtils.h"
|
|
||||||
#include "nsThreadUtils.h"
|
|
||||||
#include "prtime.h"
|
|
||||||
#include "mozilla/dom/BindingUtils.h"
|
|
||||||
#include "mozilla/dom/ScriptSettings.h"
|
|
||||||
#include "mozilla/dom/SettingChangeNotificationBinding.h"
|
|
||||||
|
|
||||||
#ifdef AGPS_TYPE_INVALID
|
|
||||||
#define AGPS_HAVE_DUAL_APN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FLUSH_AIDE_DATA 0
|
|
||||||
|
|
||||||
#undef LOG
|
|
||||||
#undef ERR
|
|
||||||
#undef DBG
|
|
||||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkGPSGeolocationProvider", ## args)
|
|
||||||
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "GonkGPSGeolocationProvider", ## args)
|
|
||||||
#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "GonkGPSGeolocationProvider" , ## args)
|
|
||||||
|
|
||||||
using namespace mozilla;
|
|
||||||
using namespace mozilla::dom;
|
|
||||||
|
|
||||||
static const int kDefaultPeriod = 1000; // ms
|
|
||||||
static bool gDebug_isLoggingEnabled = false;
|
|
||||||
static bool gDebug_isGPSLocationIgnored = false;
|
|
||||||
static const char* kMozSettingsChangedTopic = "mozsettings-changed";
|
|
||||||
// Both of these settings can be toggled in the Gaia Developer settings screen.
|
|
||||||
static const char* kSettingDebugEnabled = "geolocation.debugging.enabled";
|
|
||||||
static const char* kSettingDebugGpsIgnored = "geolocation.debugging.gps-locations-ignored";
|
|
||||||
|
|
||||||
// While most methods of GonkGPSGeolocationProvider should only be
|
|
||||||
// called from main thread, we deliberately put the Init and ShutdownGPS
|
|
||||||
// methods off main thread to avoid blocking.
|
|
||||||
NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider,
|
|
||||||
nsIGeolocationProvider,
|
|
||||||
nsIObserver,
|
|
||||||
nsISettingsServiceCallback)
|
|
||||||
|
|
||||||
/* static */ GonkGPSGeolocationProvider* GonkGPSGeolocationProvider::sSingleton = nullptr;
|
|
||||||
GpsCallbacks GonkGPSGeolocationProvider::mCallbacks;
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
|
|
||||||
{
|
|
||||||
if (gDebug_isGPSLocationIgnored) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateLocationEvent : public Runnable {
|
|
||||||
public:
|
|
||||||
UpdateLocationEvent(nsGeoPosition* aPosition)
|
|
||||||
: mPosition(aPosition)
|
|
||||||
{}
|
|
||||||
NS_IMETHOD Run() override {
|
|
||||||
RefPtr<GonkGPSGeolocationProvider> provider =
|
|
||||||
GonkGPSGeolocationProvider::GetSingleton();
|
|
||||||
nsCOMPtr<nsIGeolocationUpdate> callback = provider->mLocationCallback;
|
|
||||||
provider->mLastGPSPosition = mPosition;
|
|
||||||
if (callback) {
|
|
||||||
callback->Update(mPosition);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
RefPtr<nsGeoPosition> mPosition;
|
|
||||||
};
|
|
||||||
|
|
||||||
MOZ_ASSERT(location);
|
|
||||||
|
|
||||||
const float kImpossibleAccuracy_m = 0.001;
|
|
||||||
if (location->accuracy < kImpossibleAccuracy_m) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<nsGeoPosition> somewhere = new nsGeoPosition(location->latitude,
|
|
||||||
location->longitude,
|
|
||||||
location->altitude,
|
|
||||||
location->accuracy,
|
|
||||||
location->accuracy,
|
|
||||||
location->bearing,
|
|
||||||
location->speed,
|
|
||||||
PR_Now() / PR_USEC_PER_MSEC);
|
|
||||||
// Note above: Can't use location->timestamp as the time from the satellite is a
|
|
||||||
// minimum of 16 secs old (see http://leapsecond.com/java/gpsclock.htm).
|
|
||||||
// All code from this point on expects the gps location to be timestamped with the
|
|
||||||
// current time, most notably: the geolocation service which respects maximumAge
|
|
||||||
// set in the DOM JS.
|
|
||||||
|
|
||||||
if (gDebug_isLoggingEnabled) {
|
|
||||||
DBG("geo: GPS got a fix (%f, %f). accuracy: %f",
|
|
||||||
location->latitude,
|
|
||||||
location->longitude,
|
|
||||||
location->accuracy);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<UpdateLocationEvent> event = new UpdateLocationEvent(somewhere);
|
|
||||||
NS_DispatchToMainThread(event);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class NotifyObserversGPSTask final : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit NotifyObserversGPSTask(const char16_t* aData)
|
|
||||||
: mData(aData)
|
|
||||||
{}
|
|
||||||
NS_IMETHOD Run() override {
|
|
||||||
RefPtr<nsIGeolocationProvider> provider =
|
|
||||||
GonkGPSGeolocationProvider::GetSingleton();
|
|
||||||
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
|
|
||||||
obsService->NotifyObservers(provider, "geolocation-device-events", mData);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
const char16_t* mData;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status)
|
|
||||||
{
|
|
||||||
const char* msgStream=0;
|
|
||||||
switch (status->status) {
|
|
||||||
case GPS_STATUS_NONE:
|
|
||||||
msgStream = "geo: GPS_STATUS_NONE\n";
|
|
||||||
break;
|
|
||||||
case GPS_STATUS_SESSION_BEGIN:
|
|
||||||
msgStream = "geo: GPS_STATUS_SESSION_BEGIN\n";
|
|
||||||
break;
|
|
||||||
case GPS_STATUS_SESSION_END:
|
|
||||||
msgStream = "geo: GPS_STATUS_SESSION_END\n";
|
|
||||||
break;
|
|
||||||
case GPS_STATUS_ENGINE_ON:
|
|
||||||
msgStream = "geo: GPS_STATUS_ENGINE_ON\n";
|
|
||||||
NS_DispatchToMainThread(new NotifyObserversGPSTask(u"GPSStarting"));
|
|
||||||
break;
|
|
||||||
case GPS_STATUS_ENGINE_OFF:
|
|
||||||
msgStream = "geo: GPS_STATUS_ENGINE_OFF\n";
|
|
||||||
NS_DispatchToMainThread(new NotifyObserversGPSTask(u"GPSShutdown"));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
msgStream = "geo: Unknown GPS status\n";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (gDebug_isLoggingEnabled){
|
|
||||||
DBG("%s", msgStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::SvStatusCallback(GpsSvStatus* sv_info)
|
|
||||||
{
|
|
||||||
if (gDebug_isLoggingEnabled) {
|
|
||||||
static int numSvs = 0;
|
|
||||||
static uint32_t numEphemeris = 0;
|
|
||||||
static uint32_t numAlmanac = 0;
|
|
||||||
static uint32_t numUsedInFix = 0;
|
|
||||||
|
|
||||||
unsigned int i = 1;
|
|
||||||
uint32_t svAlmanacCount = 0;
|
|
||||||
for (i = 1; i > 0; i <<= 1) {
|
|
||||||
if (i & sv_info->almanac_mask) {
|
|
||||||
svAlmanacCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t svEphemerisCount = 0;
|
|
||||||
for (i = 1; i > 0; i <<= 1) {
|
|
||||||
if (i & sv_info->ephemeris_mask) {
|
|
||||||
svEphemerisCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t svUsedCount = 0;
|
|
||||||
for (i = 1; i > 0; i <<= 1) {
|
|
||||||
if (i & sv_info->used_in_fix_mask) {
|
|
||||||
svUsedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the message only if the the status changed.
|
|
||||||
if (sv_info->num_svs != numSvs ||
|
|
||||||
svAlmanacCount != numAlmanac ||
|
|
||||||
svEphemerisCount != numEphemeris ||
|
|
||||||
svUsedCount != numUsedInFix) {
|
|
||||||
|
|
||||||
LOG(
|
|
||||||
"geo: Number of SVs have (visibility, almanac, ephemeris): (%d, %d, %d)."
|
|
||||||
" %d of these SVs were used in fix.\n",
|
|
||||||
sv_info->num_svs, svAlmanacCount, svEphemerisCount, svUsedCount);
|
|
||||||
|
|
||||||
numSvs = sv_info->num_svs;
|
|
||||||
numAlmanac = svAlmanacCount;
|
|
||||||
numEphemeris = svEphemerisCount;
|
|
||||||
numUsedInFix = svUsedCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::NmeaCallback(GpsUtcTime timestamp, const char* nmea, int length)
|
|
||||||
{
|
|
||||||
if (gDebug_isLoggingEnabled) {
|
|
||||||
DBG("NMEA: timestamp:\t%lld, length: %d, %s", timestamp, length, nmea);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::SetCapabilitiesCallback(uint32_t capabilities)
|
|
||||||
{
|
|
||||||
class UpdateCapabilitiesEvent : public Runnable {
|
|
||||||
public:
|
|
||||||
UpdateCapabilitiesEvent(uint32_t aCapabilities)
|
|
||||||
: mCapabilities(aCapabilities)
|
|
||||||
{}
|
|
||||||
NS_IMETHOD Run() override {
|
|
||||||
RefPtr<GonkGPSGeolocationProvider> provider =
|
|
||||||
GonkGPSGeolocationProvider::GetSingleton();
|
|
||||||
|
|
||||||
provider->mSupportsScheduling = mCapabilities & GPS_CAPABILITY_SCHEDULING;
|
|
||||||
provider->mSupportsSingleShot = mCapabilities & GPS_CAPABILITY_SINGLE_SHOT;
|
|
||||||
#ifdef GPS_CAPABILITY_ON_DEMAND_TIME
|
|
||||||
provider->mSupportsTimeInjection = mCapabilities & GPS_CAPABILITY_ON_DEMAND_TIME;
|
|
||||||
#endif
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
uint32_t mCapabilities;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_DispatchToMainThread(new UpdateCapabilitiesEvent(capabilities));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::AcquireWakelockCallback()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::ReleaseWakelockCallback()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void *(*pthread_func)(void *);
|
|
||||||
|
|
||||||
/** Callback for creating a thread that can call into the JS codes.
|
|
||||||
*/
|
|
||||||
pthread_t
|
|
||||||
GonkGPSGeolocationProvider::CreateThreadCallback(const char* name, void (*start)(void *), void* arg)
|
|
||||||
{
|
|
||||||
pthread_t thread;
|
|
||||||
pthread_attr_t attr;
|
|
||||||
|
|
||||||
pthread_attr_init(&attr);
|
|
||||||
|
|
||||||
/* Unfortunately pthread_create and the callback disagreed on what
|
|
||||||
* start function should return.
|
|
||||||
*/
|
|
||||||
pthread_create(&thread, &attr, reinterpret_cast<pthread_func>(start), arg);
|
|
||||||
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::RequestUtcTimeCallback()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GonkGPSGeolocationProvider::GonkGPSGeolocationProvider()
|
|
||||||
: mStarted(false)
|
|
||||||
, mSupportsScheduling(false)
|
|
||||||
, mObservingSettingsChange(false)
|
|
||||||
, mSupportsSingleShot(false)
|
|
||||||
, mSupportsTimeInjection(false)
|
|
||||||
, mGpsInterface(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GonkGPSGeolocationProvider::~GonkGPSGeolocationProvider()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
MOZ_ASSERT(!mStarted, "Must call Shutdown before destruction");
|
|
||||||
|
|
||||||
sSingleton = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<GonkGPSGeolocationProvider>
|
|
||||||
GonkGPSGeolocationProvider::GetSingleton()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
if (!sSingleton)
|
|
||||||
sSingleton = new GonkGPSGeolocationProvider();
|
|
||||||
|
|
||||||
RefPtr<GonkGPSGeolocationProvider> provider = sSingleton;
|
|
||||||
return provider.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
const GpsInterface*
|
|
||||||
GonkGPSGeolocationProvider::GetGPSInterface()
|
|
||||||
{
|
|
||||||
hw_module_t* module;
|
|
||||||
|
|
||||||
if (hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
hw_device_t* device;
|
|
||||||
if (module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
gps_device_t* gps_device = (gps_device_t *)device;
|
|
||||||
const GpsInterface* result = gps_device->get_gps_interface(gps_device);
|
|
||||||
|
|
||||||
if (result->size != sizeof(GpsInterface)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::RequestSettingValue(const char* aKey)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aKey);
|
|
||||||
nsCOMPtr<nsISettingsService> ss = do_GetService("@mozilla.org/settingsService;1");
|
|
||||||
if (!ss) {
|
|
||||||
MOZ_ASSERT(ss);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsISettingsServiceLock> lock;
|
|
||||||
nsresult rv = ss->CreateLock(nullptr, getter_AddRefs(lock));
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
ERR("error while createLock setting '%s': %d\n", aKey, uint32_t(rv));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = lock->Get(aKey, this);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
ERR("error while get setting '%s': %d\n", aKey, uint32_t(rv));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::InjectLocation(double latitude,
|
|
||||||
double longitude,
|
|
||||||
float accuracy)
|
|
||||||
{
|
|
||||||
if (gDebug_isLoggingEnabled) {
|
|
||||||
DBG("injecting location (%f, %f) accuracy: %f", latitude, longitude, accuracy);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
if (!mGpsInterface) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mGpsInterface->inject_location(latitude, longitude, accuracy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::Init()
|
|
||||||
{
|
|
||||||
// Must not be main thread. Some GPS driver's first init takes very long.
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
|
|
||||||
mGpsInterface = GetGPSInterface();
|
|
||||||
if (!mGpsInterface) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mCallbacks.size) {
|
|
||||||
mCallbacks.size = sizeof(GpsCallbacks);
|
|
||||||
mCallbacks.location_cb = LocationCallback;
|
|
||||||
mCallbacks.status_cb = StatusCallback;
|
|
||||||
mCallbacks.sv_status_cb = SvStatusCallback;
|
|
||||||
mCallbacks.nmea_cb = NmeaCallback;
|
|
||||||
mCallbacks.set_capabilities_cb = SetCapabilitiesCallback;
|
|
||||||
mCallbacks.acquire_wakelock_cb = AcquireWakelockCallback;
|
|
||||||
mCallbacks.release_wakelock_cb = ReleaseWakelockCallback;
|
|
||||||
mCallbacks.create_thread_cb = CreateThreadCallback;
|
|
||||||
|
|
||||||
#ifdef GPS_CAPABILITY_ON_DEMAND_TIME
|
|
||||||
mCallbacks.request_utc_time_cb = RequestUtcTimeCallback;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mGpsInterface->init(&mCallbacks) != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_DispatchToMainThread(NewRunnableMethod(this, &GonkGPSGeolocationProvider::StartGPS));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::StartGPS()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
MOZ_ASSERT(mGpsInterface);
|
|
||||||
|
|
||||||
int32_t update = Preferences::GetInt("geo.default.update", kDefaultPeriod);
|
|
||||||
|
|
||||||
int positionMode = GPS_POSITION_MODE_STANDALONE;
|
|
||||||
|
|
||||||
if (!mSupportsScheduling) {
|
|
||||||
update = kDefaultPeriod;
|
|
||||||
}
|
|
||||||
|
|
||||||
mGpsInterface->set_position_mode(positionMode,
|
|
||||||
GPS_POSITION_RECURRENCE_PERIODIC,
|
|
||||||
update, 0, 0);
|
|
||||||
#if FLUSH_AIDE_DATA
|
|
||||||
// Delete cached data
|
|
||||||
mGpsInterface->delete_aiding_data(GPS_DELETE_ALL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mGpsInterface->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider::NetworkLocationUpdate,
|
|
||||||
nsIGeolocationUpdate)
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::NetworkLocationUpdate::Update(nsIDOMGeoPosition *position)
|
|
||||||
{
|
|
||||||
RefPtr<GonkGPSGeolocationProvider> provider =
|
|
||||||
GonkGPSGeolocationProvider::GetSingleton();
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
|
|
||||||
position->GetCoords(getter_AddRefs(coords));
|
|
||||||
if (!coords) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
double lat, lon, acc;
|
|
||||||
coords->GetLatitude(&lat);
|
|
||||||
coords->GetLongitude(&lon);
|
|
||||||
coords->GetAccuracy(&acc);
|
|
||||||
|
|
||||||
double delta = -1.0;
|
|
||||||
|
|
||||||
static double sLastMLSPosLat = 0;
|
|
||||||
static double sLastMLSPosLon = 0;
|
|
||||||
|
|
||||||
if (0 != sLastMLSPosLon || 0 != sLastMLSPosLat) {
|
|
||||||
delta = CalculateDeltaInMeter(lat, lon, sLastMLSPosLat, sLastMLSPosLon);
|
|
||||||
}
|
|
||||||
|
|
||||||
sLastMLSPosLat = lat;
|
|
||||||
sLastMLSPosLon = lon;
|
|
||||||
|
|
||||||
// if the MLS coord change is smaller than this arbitrarily small value
|
|
||||||
// assume the MLS coord is unchanged, and stick with the GPS location
|
|
||||||
const double kMinMLSCoordChangeInMeters = 10;
|
|
||||||
|
|
||||||
DOMTimeStamp time_ms = 0;
|
|
||||||
if (provider->mLastGPSPosition) {
|
|
||||||
provider->mLastGPSPosition->GetTimestamp(&time_ms);
|
|
||||||
}
|
|
||||||
const int64_t diff_ms = (PR_Now() / PR_USEC_PER_MSEC) - time_ms;
|
|
||||||
|
|
||||||
// We want to distinguish between the GPS being inactive completely
|
|
||||||
// and temporarily inactive. In the former case, we would use a low
|
|
||||||
// accuracy network location; in the latter, we only want a network
|
|
||||||
// location that appears to updating with movement.
|
|
||||||
|
|
||||||
const bool isGPSFullyInactive = diff_ms > 1000 * 60 * 2; // two mins
|
|
||||||
const bool isGPSTempInactive = diff_ms > 1000 * 10; // 10 secs
|
|
||||||
|
|
||||||
if (provider->mLocationCallback) {
|
|
||||||
if (isGPSFullyInactive ||
|
|
||||||
(isGPSTempInactive && delta > kMinMLSCoordChangeInMeters))
|
|
||||||
{
|
|
||||||
if (gDebug_isLoggingEnabled) {
|
|
||||||
DBG("Using MLS, GPS age:%fs, MLS Delta:%fm\n", diff_ms / 1000.0, delta);
|
|
||||||
}
|
|
||||||
provider->mLocationCallback->Update(position);
|
|
||||||
} else if (provider->mLastGPSPosition) {
|
|
||||||
if (gDebug_isLoggingEnabled) {
|
|
||||||
DBG("Using old GPS age:%fs\n", diff_ms / 1000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a fallback case so that the GPS provider responds with its last
|
|
||||||
// location rather than waiting for a more recent GPS or network location.
|
|
||||||
// The service decides if the location is too old, not the provider.
|
|
||||||
provider->mLocationCallback->Update(provider->mLastGPSPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
provider->InjectLocation(lat, lon, acc);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::NetworkLocationUpdate::NotifyError(uint16_t error)
|
|
||||||
{
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::Startup()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
if (mStarted) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestSettingValue(kSettingDebugEnabled);
|
|
||||||
RequestSettingValue(kSettingDebugGpsIgnored);
|
|
||||||
|
|
||||||
// Setup an observer to watch changes to the setting.
|
|
||||||
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
|
||||||
if (observerService) {
|
|
||||||
MOZ_ASSERT(!mObservingSettingsChange);
|
|
||||||
nsresult rv = observerService->AddObserver(this, kMozSettingsChangedTopic, false);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("geo: Gonk GPS AddObserver failed");
|
|
||||||
} else {
|
|
||||||
mObservingSettingsChange = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mInitThread) {
|
|
||||||
nsresult rv = NS_NewThread(getter_AddRefs(mInitThread));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
mInitThread->Dispatch(NewRunnableMethod(this, &GonkGPSGeolocationProvider::Init),
|
|
||||||
NS_DISPATCH_NORMAL);
|
|
||||||
|
|
||||||
mNetworkLocationProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
|
|
||||||
if (mNetworkLocationProvider) {
|
|
||||||
nsresult rv = mNetworkLocationProvider->Startup();
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
RefPtr<NetworkLocationUpdate> update = new NetworkLocationUpdate();
|
|
||||||
mNetworkLocationProvider->Watch(update);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mStarted = true;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
mLocationCallback = aCallback;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::Shutdown()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
if (!mStarted) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
mStarted = false;
|
|
||||||
if (mNetworkLocationProvider) {
|
|
||||||
mNetworkLocationProvider->Shutdown();
|
|
||||||
mNetworkLocationProvider = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
||||||
if (obs) {
|
|
||||||
nsresult rv;
|
|
||||||
rv = obs->RemoveObserver(this, kMozSettingsChangedTopic);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("geo: Gonk GPS mozsettings RemoveObserver failed");
|
|
||||||
} else {
|
|
||||||
mObservingSettingsChange = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mInitThread->Dispatch(NewRunnableMethod(this, &GonkGPSGeolocationProvider::ShutdownGPS),
|
|
||||||
NS_DISPATCH_NORMAL);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkGPSGeolocationProvider::ShutdownGPS()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!mStarted, "Should only be called after Shutdown");
|
|
||||||
|
|
||||||
if (mGpsInterface) {
|
|
||||||
mGpsInterface->stop();
|
|
||||||
mGpsInterface->cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::SetHighAccuracy(bool)
|
|
||||||
{
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int
|
|
||||||
ConvertToGpsNetworkType(int aNetworkInterfaceType)
|
|
||||||
{
|
|
||||||
switch (aNetworkInterfaceType) {
|
|
||||||
case nsINetworkInfo::NETWORK_TYPE_WIFI:
|
|
||||||
return AGPS_RIL_NETWORK_TYPE_WIFI;
|
|
||||||
case nsINetworkInfo::NETWORK_TYPE_MOBILE:
|
|
||||||
return AGPS_RIL_NETWORK_TYPE_MOBILE;
|
|
||||||
case nsINetworkInfo::NETWORK_TYPE_MOBILE_MMS:
|
|
||||||
return AGPS_RIL_NETWORK_TYPE_MOBILE_MMS;
|
|
||||||
case nsINetworkInfo::NETWORK_TYPE_MOBILE_SUPL:
|
|
||||||
return AGPS_RIL_NETWORK_TYPE_MOBILE_SUPL;
|
|
||||||
case nsINetworkInfo::NETWORK_TYPE_MOBILE_DUN:
|
|
||||||
return AGPS_RIL_NETWORK_TTYPE_MOBILE_DUN;
|
|
||||||
default:
|
|
||||||
NS_WARNING(nsPrintfCString("Unknown network type mapping %d",
|
|
||||||
aNetworkInterfaceType).get());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::Observe(nsISupports* aSubject,
|
|
||||||
const char* aTopic,
|
|
||||||
const char16_t* aData)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
if (!strcmp(aTopic, kMozSettingsChangedTopic)) {
|
|
||||||
// Read changed setting value
|
|
||||||
RootedDictionary<SettingChangeNotification> setting(RootingCx());
|
|
||||||
if (!WrappedJSToDictionary(aSubject, setting)) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setting.mKey.EqualsASCII(kSettingDebugGpsIgnored)) {
|
|
||||||
LOG("received mozsettings-changed: ignoring\n");
|
|
||||||
gDebug_isGPSLocationIgnored =
|
|
||||||
setting.mValue.isBoolean() ? setting.mValue.toBoolean() : false;
|
|
||||||
if (gDebug_isLoggingEnabled) {
|
|
||||||
DBG("GPS ignored %d\n", gDebug_isGPSLocationIgnored);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
} else if (setting.mKey.EqualsASCII(kSettingDebugEnabled)) {
|
|
||||||
LOG("received mozsettings-changed: logging\n");
|
|
||||||
gDebug_isLoggingEnabled =
|
|
||||||
setting.mValue.isBoolean() ? setting.mValue.toBoolean() : false;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** nsISettingsServiceCallback **/
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::Handle(const nsAString& aName,
|
|
||||||
JS::Handle<JS::Value> aResult)
|
|
||||||
{
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
GonkGPSGeolocationProvider::HandleError(const nsAString& aErrorMessage)
|
|
||||||
{
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GonkGPSGeolocationProvider_h
|
|
||||||
#define GonkGPSGeolocationProvider_h
|
|
||||||
|
|
||||||
#include <hardware/gps.h> // for GpsInterface
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsIGeolocationProvider.h"
|
|
||||||
#include "nsIObserver.h"
|
|
||||||
#include "nsIDOMGeoPosition.h"
|
|
||||||
#include "nsISettingsService.h"
|
|
||||||
|
|
||||||
class nsIThread;
|
|
||||||
|
|
||||||
#define GONK_GPS_GEOLOCATION_PROVIDER_CID \
|
|
||||||
{ 0x48525ec5, 0x5a7f, 0x490a, { 0x92, 0x77, 0xba, 0x66, 0xe0, 0xd2, 0x2c, 0x8b } }
|
|
||||||
|
|
||||||
#define GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID \
|
|
||||||
"@mozilla.org/gonk-gps-geolocation-provider;1"
|
|
||||||
|
|
||||||
class GonkGPSGeolocationProvider : public nsIGeolocationProvider
|
|
||||||
, public nsIObserver
|
|
||||||
, public nsISettingsServiceCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
|
||||||
NS_DECL_NSIGEOLOCATIONPROVIDER
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
NS_DECL_NSISETTINGSSERVICECALLBACK
|
|
||||||
|
|
||||||
static already_AddRefed<GonkGPSGeolocationProvider> GetSingleton();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/* Client should use GetSingleton() to get the provider instance. */
|
|
||||||
GonkGPSGeolocationProvider();
|
|
||||||
GonkGPSGeolocationProvider(const GonkGPSGeolocationProvider &);
|
|
||||||
GonkGPSGeolocationProvider & operator = (const GonkGPSGeolocationProvider &);
|
|
||||||
virtual ~GonkGPSGeolocationProvider();
|
|
||||||
|
|
||||||
static void LocationCallback(GpsLocation* location);
|
|
||||||
static void StatusCallback(GpsStatus* status);
|
|
||||||
static void SvStatusCallback(GpsSvStatus* sv_info);
|
|
||||||
static void NmeaCallback(GpsUtcTime timestamp, const char* nmea, int length);
|
|
||||||
static void SetCapabilitiesCallback(uint32_t capabilities);
|
|
||||||
static void AcquireWakelockCallback();
|
|
||||||
static void ReleaseWakelockCallback();
|
|
||||||
static pthread_t CreateThreadCallback(const char* name, void (*start)(void*), void* arg);
|
|
||||||
static void RequestUtcTimeCallback();
|
|
||||||
|
|
||||||
static GpsCallbacks mCallbacks;
|
|
||||||
|
|
||||||
void Init();
|
|
||||||
void StartGPS();
|
|
||||||
void ShutdownGPS();
|
|
||||||
void InjectLocation(double latitude, double longitude, float accuracy);
|
|
||||||
void RequestSettingValue(const char* aKey);
|
|
||||||
|
|
||||||
const GpsInterface* GetGPSInterface();
|
|
||||||
|
|
||||||
static GonkGPSGeolocationProvider* sSingleton;
|
|
||||||
|
|
||||||
bool mStarted;
|
|
||||||
|
|
||||||
bool mSupportsScheduling;
|
|
||||||
bool mObservingSettingsChange;
|
|
||||||
bool mSupportsSingleShot;
|
|
||||||
bool mSupportsTimeInjection;
|
|
||||||
|
|
||||||
const GpsInterface* mGpsInterface;
|
|
||||||
nsCOMPtr<nsIGeolocationUpdate> mLocationCallback;
|
|
||||||
nsCOMPtr<nsIThread> mInitThread;
|
|
||||||
nsCOMPtr<nsIGeolocationProvider> mNetworkLocationProvider;
|
|
||||||
nsCOMPtr<nsIDOMGeoPosition> mLastGPSPosition;
|
|
||||||
|
|
||||||
class NetworkLocationUpdate : public nsIGeolocationUpdate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIGEOLOCATIONUPDATE
|
|
||||||
|
|
||||||
NetworkLocationUpdate() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual ~NetworkLocationUpdate() {}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* GonkGPSGeolocationProvider_h */
|
|
||||||
|
|
@ -1,56 +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_system_mozmtpcommon_h__
|
|
||||||
#define mozilla_system_mozmtpcommon_h__
|
|
||||||
|
|
||||||
#include "mozilla/Types.h"
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
#define USE_DEBUG 0
|
|
||||||
|
|
||||||
#if USE_DEBUG
|
|
||||||
#define MTP_DBG(msg, ...) \
|
|
||||||
__android_log_print(ANDROID_LOG_DEBUG, "MozMtp", \
|
|
||||||
"%s: " msg, __FUNCTION__, ##__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define MTP_DBG(msg, ...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MTP_LOG(msg, ...) \
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, "MozMtp", \
|
|
||||||
"%s: " msg, __FUNCTION__, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#define MTP_ERR(msg, ...) \
|
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "MozMtp", \
|
|
||||||
"%s: " msg, __FUNCTION__, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#define BEGIN_MTP_NAMESPACE \
|
|
||||||
namespace mozilla { namespace system { namespace mtp {
|
|
||||||
#define END_MTP_NAMESPACE \
|
|
||||||
} /* namespace mtp */ } /* namespace system */ } /* namespace mozilla */
|
|
||||||
#define USING_MTP_NAMESPACE \
|
|
||||||
using namespace mozilla::system::mtp;
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
class MOZ_EXPORT MtpServer;
|
|
||||||
class MOZ_EXPORT MtpStorage;
|
|
||||||
class MOZ_EXPORT MtpStringBuffer;
|
|
||||||
class MOZ_EXPORT MtpDatabase;
|
|
||||||
class MOZ_EXPORT MtpDataPacket;
|
|
||||||
class MOZ_EXPORT MtpProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <mtp.h>
|
|
||||||
#include <MtpDatabase.h>
|
|
||||||
#include <MtpObjectInfo.h>
|
|
||||||
#include <MtpProperty.h>
|
|
||||||
#include <MtpServer.h>
|
|
||||||
#include <MtpStorage.h>
|
|
||||||
#include <MtpStringBuffer.h>
|
|
||||||
#include <MtpTypes.h>
|
|
||||||
|
|
||||||
#endif // mozilla_system_mtpcommon_h__
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,288 +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_system_mozmtpdatabase_h__
|
|
||||||
#define mozilla_system_mozmtpdatabase_h__
|
|
||||||
|
|
||||||
#include "MozMtpCommon.h"
|
|
||||||
|
|
||||||
#include "mozilla/Mutex.h"
|
|
||||||
#include "mozilla/RefPtr.h"
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "nsIThread.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
|
|
||||||
class DeviceStorageFile;
|
|
||||||
|
|
||||||
BEGIN_MTP_NAMESPACE // mozilla::system::mtp
|
|
||||||
|
|
||||||
class RefCountedMtpServer;
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
|
|
||||||
class MozMtpDatabase final : public MtpDatabase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpDatabase)
|
|
||||||
|
|
||||||
MozMtpDatabase();
|
|
||||||
|
|
||||||
// called from SendObjectInfo to reserve a database entry for the incoming file
|
|
||||||
virtual MtpObjectHandle beginSendObject(const char* aPath,
|
|
||||||
MtpObjectFormat aFormat,
|
|
||||||
MtpObjectHandle aParent,
|
|
||||||
MtpStorageID aStorageID,
|
|
||||||
uint64_t aSize,
|
|
||||||
time_t aModified);
|
|
||||||
|
|
||||||
// called to report success or failure of the SendObject file transfer
|
|
||||||
// success should signal a notification of the new object's creation,
|
|
||||||
// failure should remove the database entry created in beginSendObject
|
|
||||||
virtual void endSendObject(const char* aPath,
|
|
||||||
MtpObjectHandle aHandle,
|
|
||||||
MtpObjectFormat aFormat,
|
|
||||||
bool aSucceeded);
|
|
||||||
|
|
||||||
virtual MtpObjectHandleList* getObjectList(MtpStorageID aStorageID,
|
|
||||||
MtpObjectFormat aFormat,
|
|
||||||
MtpObjectHandle aParent);
|
|
||||||
|
|
||||||
virtual int getNumObjects(MtpStorageID aStorageID,
|
|
||||||
MtpObjectFormat aFormat,
|
|
||||||
MtpObjectHandle aParent);
|
|
||||||
|
|
||||||
virtual MtpObjectFormatList* getSupportedPlaybackFormats();
|
|
||||||
|
|
||||||
virtual MtpObjectFormatList* getSupportedCaptureFormats();
|
|
||||||
|
|
||||||
virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat aFormat);
|
|
||||||
|
|
||||||
virtual MtpDevicePropertyList* getSupportedDeviceProperties();
|
|
||||||
|
|
||||||
virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle aHandle,
|
|
||||||
MtpObjectProperty aProperty,
|
|
||||||
MtpDataPacket& aPacket);
|
|
||||||
|
|
||||||
virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle aHandle,
|
|
||||||
MtpObjectProperty aProperty,
|
|
||||||
MtpDataPacket& aPacket);
|
|
||||||
|
|
||||||
virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty aProperty,
|
|
||||||
MtpDataPacket& aPacket);
|
|
||||||
|
|
||||||
virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty aProperty,
|
|
||||||
MtpDataPacket& aPacket);
|
|
||||||
|
|
||||||
virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty aProperty);
|
|
||||||
|
|
||||||
virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle aHandle,
|
|
||||||
uint32_t aFormat,
|
|
||||||
uint32_t aProperty,
|
|
||||||
int aGroupCode,
|
|
||||||
int aDepth,
|
|
||||||
MtpDataPacket& aPacket);
|
|
||||||
|
|
||||||
virtual MtpResponseCode getObjectInfo(MtpObjectHandle aHandle,
|
|
||||||
MtpObjectInfo& aInfo);
|
|
||||||
|
|
||||||
virtual void* getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize);
|
|
||||||
|
|
||||||
virtual MtpResponseCode getObjectFilePath(MtpObjectHandle aHandle,
|
|
||||||
MtpString& aOutFilePath,
|
|
||||||
int64_t& aOutFileLength,
|
|
||||||
MtpObjectFormat& aOutFormat);
|
|
||||||
|
|
||||||
virtual MtpResponseCode deleteFile(MtpObjectHandle aHandle);
|
|
||||||
|
|
||||||
virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle aHandle);
|
|
||||||
|
|
||||||
virtual MtpResponseCode setObjectReferences(MtpObjectHandle aHandle,
|
|
||||||
MtpObjectHandleList* aReferences);
|
|
||||||
|
|
||||||
virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty aProperty,
|
|
||||||
MtpObjectFormat aFormat);
|
|
||||||
|
|
||||||
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty aProperty);
|
|
||||||
|
|
||||||
virtual void sessionStarted();
|
|
||||||
|
|
||||||
virtual void sessionEnded();
|
|
||||||
|
|
||||||
void AddStorage(MtpStorageID aStorageID, const char* aPath, const char *aName);
|
|
||||||
void RemoveStorage(MtpStorageID aStorageID);
|
|
||||||
|
|
||||||
void MtpWatcherUpdate(RefCountedMtpServer* aMtpServer,
|
|
||||||
DeviceStorageFile* aFile,
|
|
||||||
const nsACString& aEventType);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~MozMtpDatabase();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct DbEntry final
|
|
||||||
{
|
|
||||||
DbEntry()
|
|
||||||
: mHandle(0),
|
|
||||||
mStorageID(0),
|
|
||||||
mObjectFormat(MTP_FORMAT_DEFINED),
|
|
||||||
mParent(0),
|
|
||||||
mObjectSize(0),
|
|
||||||
mDateCreated(0),
|
|
||||||
mDateModified(0),
|
|
||||||
mDateAdded(0) {}
|
|
||||||
|
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DbEntry)
|
|
||||||
|
|
||||||
MtpObjectHandle mHandle; // uint32_t
|
|
||||||
MtpStorageID mStorageID; // uint32_t
|
|
||||||
nsCString mObjectName;
|
|
||||||
MtpObjectFormat mObjectFormat; // uint16_t
|
|
||||||
MtpObjectHandle mParent; // uint32_t
|
|
||||||
uint64_t mObjectSize;
|
|
||||||
nsCString mDisplayName;
|
|
||||||
nsCString mPath;
|
|
||||||
time_t mDateCreated;
|
|
||||||
time_t mDateModified;
|
|
||||||
time_t mDateAdded;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
~DbEntry() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
class ProtectedTArray : private nsTArray<T>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef T elem_type;
|
|
||||||
typedef typename nsTArray<T>::size_type size_type;
|
|
||||||
typedef typename nsTArray<T>::index_type index_type;
|
|
||||||
typedef nsTArray<T> base_type;
|
|
||||||
|
|
||||||
static const index_type NoIndex = base_type::NoIndex;
|
|
||||||
|
|
||||||
ProtectedTArray(mozilla::Mutex& aMutex)
|
|
||||||
: mMutex(aMutex)
|
|
||||||
{}
|
|
||||||
|
|
||||||
size_type Length() const
|
|
||||||
{
|
|
||||||
// GRR - This assert prints to stderr and won't show up in logcat.
|
|
||||||
mMutex.AssertCurrentThreadOwns();
|
|
||||||
return base_type::Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Item>
|
|
||||||
elem_type* AppendElement(const Item& aItem)
|
|
||||||
{
|
|
||||||
mMutex.AssertCurrentThreadOwns();
|
|
||||||
return base_type::AppendElement(aItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear()
|
|
||||||
{
|
|
||||||
mMutex.AssertCurrentThreadOwns();
|
|
||||||
base_type::Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoveElementAt(index_type aIndex)
|
|
||||||
{
|
|
||||||
mMutex.AssertCurrentThreadOwns();
|
|
||||||
base_type::RemoveElementAt(aIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
elem_type& operator[](index_type aIndex)
|
|
||||||
{
|
|
||||||
mMutex.AssertCurrentThreadOwns();
|
|
||||||
return base_type::ElementAt(aIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
const elem_type& operator[](index_type aIndex) const
|
|
||||||
{
|
|
||||||
mMutex.AssertCurrentThreadOwns();
|
|
||||||
return base_type::ElementAt(aIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
mozilla::Mutex& mMutex;
|
|
||||||
};
|
|
||||||
typedef nsTArray<RefPtr<DbEntry> > UnprotectedDbArray;
|
|
||||||
typedef ProtectedTArray<RefPtr<DbEntry> > ProtectedDbArray;
|
|
||||||
|
|
||||||
struct StorageEntry final
|
|
||||||
{
|
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StorageEntry)
|
|
||||||
|
|
||||||
MtpStorageID mStorageID;
|
|
||||||
nsCString mStoragePath;
|
|
||||||
nsCString mStorageName;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
~StorageEntry() {}
|
|
||||||
};
|
|
||||||
typedef ProtectedTArray<RefPtr<StorageEntry> > StorageArray;
|
|
||||||
|
|
||||||
enum MatchType
|
|
||||||
{
|
|
||||||
MatchAll,
|
|
||||||
MatchHandle,
|
|
||||||
MatchParent,
|
|
||||||
MatchFormat,
|
|
||||||
MatchHandleFormat,
|
|
||||||
MatchParentFormat,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool IsValidHandle(MtpObjectHandle aHandle)
|
|
||||||
{
|
|
||||||
return aHandle > 0 && aHandle < mDb.Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddEntry(DbEntry* aEntry);
|
|
||||||
void AddEntryAndNotify(DbEntry* aEntr, RefCountedMtpServer* aMtpServer);
|
|
||||||
void DumpEntries(const char* aLabel);
|
|
||||||
MtpObjectHandle FindEntryByPath(const nsACString& aPath);
|
|
||||||
already_AddRefed<DbEntry> GetEntry(MtpObjectHandle aHandle);
|
|
||||||
void RemoveEntry(MtpObjectHandle aHandle);
|
|
||||||
void RemoveEntryAndNotify(MtpObjectHandle aHandle, RefCountedMtpServer* aMtpServer);
|
|
||||||
void UpdateEntry(MtpObjectHandle aHandle, DeviceStorageFile* aFile);
|
|
||||||
void UpdateEntryAndNotify(MtpObjectHandle aHandle, DeviceStorageFile* aFile,
|
|
||||||
RefCountedMtpServer* aMtpServer);
|
|
||||||
void QueryEntries(MatchType aMatchType, uint32_t aMatchField1,
|
|
||||||
uint32_t aMatchField2, UnprotectedDbArray& aResult);
|
|
||||||
|
|
||||||
nsCString BaseName(const nsCString& aPath);
|
|
||||||
|
|
||||||
|
|
||||||
MtpObjectHandle GetNextHandle()
|
|
||||||
{
|
|
||||||
return mDb.Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddDirectory(MtpStorageID aStorageID, const char *aPath, MtpObjectHandle aParent);
|
|
||||||
|
|
||||||
void CreateEntryForFileAndNotify(const nsACString& aPath,
|
|
||||||
DeviceStorageFile* aFile,
|
|
||||||
RefCountedMtpServer* aMtpServer);
|
|
||||||
|
|
||||||
StorageArray::index_type FindStorage(MtpStorageID aStorageID);
|
|
||||||
MtpStorageID FindStorageIDFor(const nsACString& aPath, nsCSubstring& aRemainder);
|
|
||||||
void MtpWatcherNotify(DbEntry* aEntry, const char* aEventType);
|
|
||||||
|
|
||||||
// We need a mutex to protext mDb and mStorage. The MTP server runs on a
|
|
||||||
// dedicated thread, and it updates/accesses mDb. When files are updated
|
|
||||||
// through DeviceStorage, we need to update/access mDb and mStorage as well
|
|
||||||
// (from a non-MTP server thread).
|
|
||||||
mozilla::Mutex mMutex;
|
|
||||||
ProtectedDbArray mDb;
|
|
||||||
StorageArray mStorage;
|
|
||||||
|
|
||||||
bool mBeginSendObjectCalled;
|
|
||||||
};
|
|
||||||
|
|
||||||
END_MTP_NAMESPACE
|
|
||||||
|
|
||||||
#endif // mozilla_system_mozmtpdatabase_h__
|
|
||||||
|
|
@ -1,263 +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 "MozMtpServer.h"
|
|
||||||
#include "MozMtpDatabase.h"
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <cutils/properties.h>
|
|
||||||
#include <private/android_filesystem_config.h>
|
|
||||||
|
|
||||||
#include "base/message_loop.h"
|
|
||||||
#include "DeviceStorage.h"
|
|
||||||
#include "mozilla/LazyIdleThread.h"
|
|
||||||
#include "mozilla/Scoped.h"
|
|
||||||
#include "mozilla/Services.h"
|
|
||||||
#include "mozilla/StaticPtr.h"
|
|
||||||
#include "nsIObserver.h"
|
|
||||||
#include "nsIObserverService.h"
|
|
||||||
#include "nsISupportsImpl.h"
|
|
||||||
#include "nsThreadUtils.h"
|
|
||||||
#include "nsXULAppAPI.h"
|
|
||||||
|
|
||||||
#include "Volume.h"
|
|
||||||
|
|
||||||
#define DEFAULT_THREAD_TIMEOUT_MS 30000
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
using namespace mozilla;
|
|
||||||
BEGIN_MTP_NAMESPACE
|
|
||||||
|
|
||||||
static const char* kMtpWatcherUpdate = "mtp-watcher-update";
|
|
||||||
|
|
||||||
class MtpWatcherUpdateRunnable final : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MtpWatcherUpdateRunnable(MozMtpDatabase* aMozMtpDatabase,
|
|
||||||
RefCountedMtpServer* aMtpServer,
|
|
||||||
DeviceStorageFile* aFile,
|
|
||||||
const nsACString& aEventType)
|
|
||||||
: mMozMtpDatabase(aMozMtpDatabase),
|
|
||||||
mMtpServer(aMtpServer),
|
|
||||||
mFile(aFile),
|
|
||||||
mEventType(aEventType)
|
|
||||||
{}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
// Runs on the MtpWatcherUpdate->mIOThread
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
|
|
||||||
mMozMtpDatabase->MtpWatcherUpdate(mMtpServer, mFile, mEventType);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
RefPtr<MozMtpDatabase> mMozMtpDatabase;
|
|
||||||
RefPtr<RefCountedMtpServer> mMtpServer;
|
|
||||||
RefPtr<DeviceStorageFile> mFile;
|
|
||||||
nsCString mEventType;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The MtpWatcherUpdate class listens for mtp-watcher-update events
|
|
||||||
// and tells the MtpServer about changes made in device storage.
|
|
||||||
class MtpWatcherUpdate final : public nsIObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
|
||||||
|
|
||||||
MtpWatcherUpdate(MozMtpServer* aMozMtpServer)
|
|
||||||
: mMozMtpServer(aMozMtpServer)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
mIOThread = new LazyIdleThread(
|
|
||||||
DEFAULT_THREAD_TIMEOUT_MS,
|
|
||||||
NS_LITERAL_CSTRING("MtpWatcherUpdate"));
|
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
||||||
obs->AddObserver(this, kMtpWatcherUpdate, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD
|
|
||||||
Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
if (strcmp(aTopic, kMtpWatcherUpdate)) {
|
|
||||||
// We're only interested in mtp-watcher-update events
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_ConvertUTF16toUTF8 eventType(aData);
|
|
||||||
if (!eventType.EqualsLiteral("modified") && !eventType.EqualsLiteral("deleted")) {
|
|
||||||
// Bug 1074604: Needn't handle "created" event, once file operation
|
|
||||||
// finished, it would trigger "modified" event.
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
|
|
||||||
file->Dump(kMtpWatcherUpdate);
|
|
||||||
MTP_LOG("%s: file %s %s", kMtpWatcherUpdate,
|
|
||||||
NS_LossyConvertUTF16toASCII(file->mPath).get(),
|
|
||||||
eventType.get());
|
|
||||||
|
|
||||||
RefPtr<MozMtpDatabase> mozMtpDatabase = mMozMtpServer->GetMozMtpDatabase();
|
|
||||||
RefPtr<RefCountedMtpServer> mtpServer = mMozMtpServer->GetMtpServer();
|
|
||||||
|
|
||||||
// We're not supposed to perform I/O on the main thread, so punt the
|
|
||||||
// notification (which will write to /dev/mtp_usb) to an I/O Thread.
|
|
||||||
|
|
||||||
RefPtr<MtpWatcherUpdateRunnable> r =
|
|
||||||
new MtpWatcherUpdateRunnable(mozMtpDatabase, mtpServer, file, eventType);
|
|
||||||
mIOThread->Dispatch(r, NS_DISPATCH_NORMAL);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
~MtpWatcherUpdate()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
||||||
obs->RemoveObserver(this, kMtpWatcherUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
RefPtr<MozMtpServer> mMozMtpServer;
|
|
||||||
nsCOMPtr<nsIThread> mIOThread;
|
|
||||||
};
|
|
||||||
NS_IMPL_ISUPPORTS(MtpWatcherUpdate, nsIObserver)
|
|
||||||
static StaticRefPtr<MtpWatcherUpdate> sMtpWatcherUpdate;
|
|
||||||
|
|
||||||
class AllocMtpWatcherUpdateRunnable final : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AllocMtpWatcherUpdateRunnable(MozMtpServer* aMozMtpServer)
|
|
||||||
: mMozMtpServer(aMozMtpServer)
|
|
||||||
{}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
sMtpWatcherUpdate = new MtpWatcherUpdate(mMozMtpServer);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
RefPtr<MozMtpServer> mMozMtpServer;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FreeMtpWatcherUpdateRunnable final : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FreeMtpWatcherUpdateRunnable(MozMtpServer* aMozMtpServer)
|
|
||||||
: mMozMtpServer(aMozMtpServer)
|
|
||||||
{}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
sMtpWatcherUpdate = nullptr;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
RefPtr<MozMtpServer> mMozMtpServer;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MtpServerRunnable : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MtpServerRunnable(int aMtpUsbFd, MozMtpServer* aMozMtpServer)
|
|
||||||
: mMozMtpServer(aMozMtpServer),
|
|
||||||
mMtpUsbFd(aMtpUsbFd)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult Run()
|
|
||||||
{
|
|
||||||
RefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
|
|
||||||
|
|
||||||
DebugOnly<nsresult> rv =
|
|
||||||
NS_DispatchToMainThread(new AllocMtpWatcherUpdateRunnable(mMozMtpServer));
|
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
||||||
|
|
||||||
MTP_LOG("MozMtpServer started");
|
|
||||||
server->run();
|
|
||||||
MTP_LOG("MozMtpServer finished");
|
|
||||||
|
|
||||||
// server->run will have closed the file descriptor.
|
|
||||||
mMtpUsbFd.forget();
|
|
||||||
|
|
||||||
rv = NS_DispatchToMainThread(new FreeMtpWatcherUpdateRunnable(mMozMtpServer));
|
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
RefPtr<MozMtpServer> mMozMtpServer;
|
|
||||||
ScopedClose mMtpUsbFd; // We want to hold this open while the server runs
|
|
||||||
};
|
|
||||||
|
|
||||||
already_AddRefed<RefCountedMtpServer>
|
|
||||||
MozMtpServer::GetMtpServer()
|
|
||||||
{
|
|
||||||
RefPtr<RefCountedMtpServer> server = mMtpServer;
|
|
||||||
return server.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<MozMtpDatabase>
|
|
||||||
MozMtpServer::GetMozMtpDatabase()
|
|
||||||
{
|
|
||||||
RefPtr<MozMtpDatabase> db = mMozMtpDatabase;
|
|
||||||
return db.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
MozMtpServer::Init()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
const char *mtpUsbFilename = "/dev/mtp_usb";
|
|
||||||
mMtpUsbFd = open(mtpUsbFilename, O_RDWR);
|
|
||||||
if (mMtpUsbFd.get() < 0) {
|
|
||||||
MTP_ERR("open of '%s' failed((%s))", mtpUsbFilename, strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
MTP_LOG("Opened '%s' fd %d", mtpUsbFilename, mMtpUsbFd.get());
|
|
||||||
|
|
||||||
mMozMtpDatabase = new MozMtpDatabase();
|
|
||||||
mMtpServer = new RefCountedMtpServer(mMtpUsbFd.get(), // fd
|
|
||||||
mMozMtpDatabase.get(), // MtpDatabase
|
|
||||||
false, // ptp?
|
|
||||||
AID_MEDIA_RW, // file group
|
|
||||||
0664, // file permissions
|
|
||||||
0775); // dir permissions
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MozMtpServer::Run()
|
|
||||||
{
|
|
||||||
nsresult rv = NS_NewNamedThread("MtpServer", getter_AddRefs(mServerThread));
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(mServerThread);
|
|
||||||
mServerThread->Dispatch(new MtpServerRunnable(mMtpUsbFd.forget(), this), NS_DISPATCH_NORMAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
END_MTP_NAMESPACE
|
|
||||||
|
|
@ -1,61 +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_system_mozmtpserver_h__
|
|
||||||
#define mozilla_system_mozmtpserver_h__
|
|
||||||
|
|
||||||
#include "MozMtpCommon.h"
|
|
||||||
#include "MozMtpDatabase.h"
|
|
||||||
|
|
||||||
#include "mozilla/FileUtils.h"
|
|
||||||
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsIThread.h"
|
|
||||||
|
|
||||||
BEGIN_MTP_NAMESPACE
|
|
||||||
using namespace android;
|
|
||||||
|
|
||||||
class RefCountedMtpServer : public MtpServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMtpServer)
|
|
||||||
|
|
||||||
RefCountedMtpServer(int aFd, MtpDatabase* aDatabase, bool aPtp,
|
|
||||||
int aFileGroup, int aFilePerm, int aDirectoryPerm)
|
|
||||||
: MtpServer(aFd, aDatabase, aPtp, aFileGroup, aFilePerm, aDirectoryPerm)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~RefCountedMtpServer() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class MozMtpServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpServer)
|
|
||||||
|
|
||||||
bool Init();
|
|
||||||
void Run();
|
|
||||||
|
|
||||||
already_AddRefed<RefCountedMtpServer> GetMtpServer();
|
|
||||||
already_AddRefed<MozMtpDatabase> GetMozMtpDatabase();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~MozMtpServer() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
RefPtr<RefCountedMtpServer> mMtpServer;
|
|
||||||
RefPtr<MozMtpDatabase> mMozMtpDatabase;
|
|
||||||
nsCOMPtr<nsIThread> mServerThread;
|
|
||||||
ScopedClose mMtpUsbFd;
|
|
||||||
};
|
|
||||||
|
|
||||||
END_MTP_NAMESPACE
|
|
||||||
|
|
||||||
#endif // mozilla_system_mozmtpserver_h__
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,135 +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 "MozMtpStorage.h"
|
|
||||||
#include "MozMtpDatabase.h"
|
|
||||||
#include "MozMtpServer.h"
|
|
||||||
|
|
||||||
#include "base/message_loop.h"
|
|
||||||
#include "nsXULAppAPI.h"
|
|
||||||
|
|
||||||
BEGIN_MTP_NAMESPACE
|
|
||||||
using namespace android;
|
|
||||||
|
|
||||||
MozMtpStorage::MozMtpStorage(Volume* aVolume, MozMtpServer* aMozMtpServer)
|
|
||||||
: mMozMtpServer(aMozMtpServer),
|
|
||||||
mVolume(aVolume)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
// The MtpStorageID has the physical volume in the top 16 bits, and the
|
|
||||||
// logical volumein the lower 16 bits. We treat each volume as a separate
|
|
||||||
// phsyical storage;
|
|
||||||
mStorageID = mVolume->Id() << 16 | 1;
|
|
||||||
|
|
||||||
MTP_LOG("Storage constructed for Volume %s mStorageID 0x%08x",
|
|
||||||
aVolume->NameStr(), mStorageID);
|
|
||||||
|
|
||||||
Volume::RegisterVolumeObserver(this, "MozMtpStorage");
|
|
||||||
|
|
||||||
// Get things in sync
|
|
||||||
Notify(mVolume);
|
|
||||||
}
|
|
||||||
|
|
||||||
MozMtpStorage::~MozMtpStorage()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
MTP_LOG("Storage destructed for Volume %s mStorageID 0x%08x",
|
|
||||||
mVolume->NameStr(), mStorageID);
|
|
||||||
|
|
||||||
Volume::UnregisterVolumeObserver(this, "MozMtpStorage");
|
|
||||||
if (mMtpStorage) {
|
|
||||||
StorageUnavailable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// virtual
|
|
||||||
void
|
|
||||||
MozMtpStorage::Notify(Volume* const& aVolume)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
if (aVolume != mVolume) {
|
|
||||||
// Not our volume
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Volume::STATE volState = aVolume->State();
|
|
||||||
|
|
||||||
MTP_LOG("Volume %s mStorageID 0x%08x state changed to %s SharingEnabled: %d",
|
|
||||||
aVolume->NameStr(), mStorageID, aVolume->StateStr(),
|
|
||||||
aVolume->IsSharingEnabled());
|
|
||||||
|
|
||||||
// vol->IsSharingEnabled really only applies to UMS volumes. We assume that
|
|
||||||
// that as long as MTP is enabled, then all volumes will be shared. The UI
|
|
||||||
// currently doesn't give us anything more granular than on/off.
|
|
||||||
|
|
||||||
if (mMtpStorage) {
|
|
||||||
if (volState != nsIVolume::STATE_MOUNTED) {
|
|
||||||
// The volume is no longer accessible. We need to remove this storage
|
|
||||||
// from the MTP server
|
|
||||||
StorageUnavailable();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (volState == nsIVolume::STATE_MOUNTED) {
|
|
||||||
// The volume is accessible. Tell the MTP server.
|
|
||||||
StorageAvailable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MozMtpStorage::StorageAvailable()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
nsCString mountPoint = mVolume->MountPoint();
|
|
||||||
|
|
||||||
MTP_LOG("Adding Volume %s mStorageID 0x%08x mountPoint %s to MozMtpDatabase",
|
|
||||||
mVolume->NameStr(), mStorageID, mountPoint.get());
|
|
||||||
|
|
||||||
RefPtr<MozMtpDatabase> db = mMozMtpServer->GetMozMtpDatabase();
|
|
||||||
db->AddStorage(mStorageID, mountPoint.get(), mVolume->NameStr());
|
|
||||||
|
|
||||||
MOZ_ASSERT(!mMtpStorage);
|
|
||||||
|
|
||||||
//TODO: Figure out what to do about maxFileSize.
|
|
||||||
|
|
||||||
mMtpStorage.reset(new MtpStorage(mStorageID, // id
|
|
||||||
mountPoint.get(), // filePath
|
|
||||||
mVolume->NameStr(), // description
|
|
||||||
1024uLL * 1024uLL, // reserveSpace
|
|
||||||
mVolume->IsHotSwappable(), // removable
|
|
||||||
2uLL * 1024uLL * 1024uLL * 1024uLL)); // maxFileSize
|
|
||||||
RefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
|
|
||||||
|
|
||||||
MTP_LOG("Adding Volume %s mStorageID 0x%08x mountPoint %s to MtpServer",
|
|
||||||
mVolume->NameStr(), mStorageID, mountPoint.get());
|
|
||||||
server->addStorage(mMtpStorage.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MozMtpStorage::StorageUnavailable()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
MOZ_ASSERT(mMtpStorage);
|
|
||||||
|
|
||||||
MTP_LOG("Removing mStorageID 0x%08x from MtpServer", mStorageID);
|
|
||||||
|
|
||||||
RefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
|
|
||||||
server->removeStorage(mMtpStorage.get());
|
|
||||||
|
|
||||||
MTP_LOG("Removing mStorageID 0x%08x from MozMtpDatabse", mStorageID);
|
|
||||||
|
|
||||||
RefPtr<MozMtpDatabase> db = mMozMtpServer->GetMozMtpDatabase();
|
|
||||||
db->RemoveStorage(mStorageID);
|
|
||||||
|
|
||||||
mMtpStorage = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
END_MTP_NAMESPACE
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,47 +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_system_mozmtpstorage_h__
|
|
||||||
#define mozilla_system_mozmtpstorage_h__
|
|
||||||
|
|
||||||
#include "MozMtpCommon.h"
|
|
||||||
|
|
||||||
#include "mozilla/UniquePtr.h"
|
|
||||||
|
|
||||||
#include "Volume.h"
|
|
||||||
|
|
||||||
BEGIN_MTP_NAMESPACE
|
|
||||||
using namespace android;
|
|
||||||
|
|
||||||
class MozMtpServer;
|
|
||||||
|
|
||||||
class MozMtpStorage : public Volume::EventObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpStorage)
|
|
||||||
|
|
||||||
MozMtpStorage(Volume* aVolume, MozMtpServer* aMozMtpServer);
|
|
||||||
|
|
||||||
typedef nsTArray<RefPtr<MozMtpStorage> > Array;
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual ~MozMtpStorage();
|
|
||||||
virtual void Notify(Volume* const& aEvent);
|
|
||||||
|
|
||||||
void StorageAvailable();
|
|
||||||
void StorageUnavailable();
|
|
||||||
|
|
||||||
RefPtr<MozMtpServer> mMozMtpServer;
|
|
||||||
UniquePtr<MtpStorage> mMtpStorage;
|
|
||||||
RefPtr<Volume> mVolume;
|
|
||||||
MtpStorageID mStorageID;
|
|
||||||
};
|
|
||||||
|
|
||||||
END_MTP_NAMESPACE
|
|
||||||
|
|
||||||
#endif // mozilla_system_mozmtpstorage_h__
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
/* 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 "NetIdManager.h"
|
|
||||||
|
|
||||||
NetIdManager::NetIdManager()
|
|
||||||
: mNextNetId(MIN_NET_ID)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int NetIdManager::getNextNetId()
|
|
||||||
{
|
|
||||||
// Modified from
|
|
||||||
// http://androidxref.com/5.0.0_r2/xref/frameworks/base/services/
|
|
||||||
// core/java/com/android/server/ConnectivityService.java#764
|
|
||||||
|
|
||||||
int netId = mNextNetId;
|
|
||||||
if (++mNextNetId > MAX_NET_ID) {
|
|
||||||
mNextNetId = MIN_NET_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return netId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetIdManager::acquire(const nsString& aInterfaceName,
|
|
||||||
NetIdInfo* aNetIdInfo)
|
|
||||||
{
|
|
||||||
// Lookup or create one.
|
|
||||||
if (!mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo)) {
|
|
||||||
aNetIdInfo->mNetId = getNextNetId();
|
|
||||||
aNetIdInfo->mRefCnt = 1;
|
|
||||||
} else {
|
|
||||||
aNetIdInfo->mRefCnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update hash and return.
|
|
||||||
mInterfaceToNetIdHash.Put(aInterfaceName, *aNetIdInfo);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NetIdManager::lookup(const nsString& aInterfaceName,
|
|
||||||
NetIdInfo* aNetIdInfo)
|
|
||||||
{
|
|
||||||
return mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NetIdManager::release(const nsString& aInterfaceName,
|
|
||||||
NetIdInfo* aNetIdInfo)
|
|
||||||
{
|
|
||||||
if (!mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo)) {
|
|
||||||
return false; // No such key.
|
|
||||||
}
|
|
||||||
|
|
||||||
aNetIdInfo->mRefCnt--;
|
|
||||||
|
|
||||||
// Update the hash if still be referenced.
|
|
||||||
if (aNetIdInfo->mRefCnt > 0) {
|
|
||||||
mInterfaceToNetIdHash.Put(aInterfaceName, *aNetIdInfo);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No longer be referenced. Remove the entry.
|
|
||||||
mInterfaceToNetIdHash.Remove(aInterfaceName);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
/* 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 NetIdManager_h
|
|
||||||
#define NetIdManager_h
|
|
||||||
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "nsDataHashtable.h"
|
|
||||||
|
|
||||||
// NetId is a logical network identifier defined by netd.
|
|
||||||
// A network is typically a physical one (i.e. PhysicalNetwork.cpp)
|
|
||||||
// for netd but it could be a virtual network as well.
|
|
||||||
// We currently use physical network only and use one-to-one
|
|
||||||
// network-interface mapping.
|
|
||||||
|
|
||||||
class NetIdManager {
|
|
||||||
public:
|
|
||||||
// keep in sync with system/netd/NetworkController.cpp
|
|
||||||
enum {
|
|
||||||
MIN_NET_ID = 100,
|
|
||||||
MAX_NET_ID = 65535,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We need to count the number of references since different
|
|
||||||
// application like data and mms may use the same interface.
|
|
||||||
struct NetIdInfo {
|
|
||||||
int mNetId;
|
|
||||||
int mRefCnt;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
NetIdManager();
|
|
||||||
|
|
||||||
bool lookup(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
|
|
||||||
void acquire(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
|
|
||||||
bool release(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int getNextNetId();
|
|
||||||
int mNextNetId;
|
|
||||||
nsDataHashtable<nsStringHashKey, NetIdInfo> mInterfaceToNetIdHash;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
|
|
||||||
const NETWORKLISTSERVICE_CONTRACTID =
|
|
||||||
"@mozilla.org/network/interface-list-service;1";
|
|
||||||
const NETWORKLISTSERVICE_CID =
|
|
||||||
Components.ID("{3780be6e-7012-4e53-ade6-15212fb88a0d}");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
|
||||||
"@mozilla.org/childprocessmessagemanager;1",
|
|
||||||
"nsISyncMessageSender");
|
|
||||||
|
|
||||||
function NetworkInterfaceListService () {
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkInterfaceListService.prototype = {
|
|
||||||
classID: NETWORKLISTSERVICE_CID,
|
|
||||||
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterfaceListService]),
|
|
||||||
|
|
||||||
getDataInterfaceList: function(aConditions) {
|
|
||||||
return new NetworkInterfaceList(
|
|
||||||
cpmm.sendSyncMessage(
|
|
||||||
'NetworkInterfaceList:ListInterface',
|
|
||||||
{
|
|
||||||
excludeSupl: (aConditions &
|
|
||||||
Ci.nsINetworkInterfaceListService.
|
|
||||||
LIST_NOT_INCLUDE_SUPL_INTERFACES) != 0,
|
|
||||||
excludeMms: (aConditions &
|
|
||||||
Ci.nsINetworkInterfaceListService.
|
|
||||||
LIST_NOT_INCLUDE_MMS_INTERFACES) != 0,
|
|
||||||
excludeIms: (aConditions &
|
|
||||||
Ci.nsINetworkInterfaceListService.
|
|
||||||
LIST_NOT_INCLUDE_IMS_INTERFACES) != 0,
|
|
||||||
excludeDun: (aConditions &
|
|
||||||
Ci.nsINetworkInterfaceListService.
|
|
||||||
LIST_NOT_INCLUDE_DUN_INTERFACES) != 0,
|
|
||||||
excludeFota: (aConditions &
|
|
||||||
Ci.nsINetworkInterfaceListService.
|
|
||||||
LIST_NOT_INCLUDE_FOTA_INTERFACES) != 0
|
|
||||||
}
|
|
||||||
)[0]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function FakeNetworkInfo(aAttributes) {
|
|
||||||
this.state = aAttributes.state;
|
|
||||||
this.type = aAttributes.type;
|
|
||||||
this.name = aAttributes.name;
|
|
||||||
this.ips = aAttributes.ips;
|
|
||||||
this.prefixLengths = aAttributes.prefixLengths;
|
|
||||||
this.gateways = aAttributes.gateways;
|
|
||||||
this.dnses = aAttributes.dnses;
|
|
||||||
}
|
|
||||||
FakeNetworkInfo.prototype = {
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInfo]),
|
|
||||||
|
|
||||||
getAddresses: function (ips, prefixLengths) {
|
|
||||||
ips.value = this.ips.slice();
|
|
||||||
prefixLengths.value = this.prefixLengths.slice();
|
|
||||||
|
|
||||||
return this.ips.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
getGateways: function (count) {
|
|
||||||
if (count) {
|
|
||||||
count.value = this.gateways.length;
|
|
||||||
}
|
|
||||||
return this.gateways.slice();
|
|
||||||
},
|
|
||||||
|
|
||||||
getDnses: function (count) {
|
|
||||||
if (count) {
|
|
||||||
count.value = this.dnses.length;
|
|
||||||
}
|
|
||||||
return this.dnses.slice();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function NetworkInterfaceList (aInterfaceLiterals) {
|
|
||||||
this._interfaces = [];
|
|
||||||
for (let entry of aInterfaceLiterals) {
|
|
||||||
this._interfaces.push(new FakeNetworkInfo(entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkInterfaceList.prototype = {
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterfaceList]),
|
|
||||||
|
|
||||||
getNumberOfInterface: function() {
|
|
||||||
return this._interfaces.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
getInterfaceInfo: function(index) {
|
|
||||||
if (!this._interfaces) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return this._interfaces[index];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkInterfaceListService]);
|
|
||||||
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
# Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
# NetworkInterfaceListService.js
|
|
||||||
component {3780be6e-7012-4e53-ade6-15212fb88a0d} NetworkInterfaceListService.js
|
|
||||||
contract @mozilla.org/network/interface-list-service;1 {3780be6e-7012-4e53-ade6-15212fb88a0d}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +0,0 @@
|
||||||
# NetworkManager.js
|
|
||||||
component {1ba9346b-53b5-4660-9dc6-58f0b258d0a6} NetworkManager.js
|
|
||||||
contract @mozilla.org/network/manager;1 {1ba9346b-53b5-4660-9dc6-58f0b258d0a6}
|
|
||||||
|
|
@ -1,862 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
|
||||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
|
||||||
|
|
||||||
const NETWORKSERVICE_CONTRACTID = "@mozilla.org/network/service;1";
|
|
||||||
const NETWORKSERVICE_CID = Components.ID("{48c13741-aec9-4a86-8962-432011708261}");
|
|
||||||
|
|
||||||
const TOPIC_PREF_CHANGED = "nsPref:changed";
|
|
||||||
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
|
|
||||||
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkWorker",
|
|
||||||
"@mozilla.org/network/worker;1",
|
|
||||||
"nsINetworkWorker");
|
|
||||||
|
|
||||||
// 1xx - Requested action is proceeding
|
|
||||||
const NETD_COMMAND_PROCEEDING = 100;
|
|
||||||
// 2xx - Requested action has been successfully completed
|
|
||||||
const NETD_COMMAND_OKAY = 200;
|
|
||||||
// 4xx - The command is accepted but the requested action didn't
|
|
||||||
// take place.
|
|
||||||
const NETD_COMMAND_FAIL = 400;
|
|
||||||
// 5xx - The command syntax or parameters error
|
|
||||||
const NETD_COMMAND_ERROR = 500;
|
|
||||||
// 6xx - Unsolicited broadcasts
|
|
||||||
const NETD_COMMAND_UNSOLICITED = 600;
|
|
||||||
|
|
||||||
const WIFI_CTRL_INTERFACE = "wl0.1";
|
|
||||||
|
|
||||||
var debug;
|
|
||||||
function updateDebug() {
|
|
||||||
let debugPref = false; // set default value here.
|
|
||||||
try {
|
|
||||||
debugPref = debugPref || Services.prefs.getBoolPref(PREF_NETWORK_DEBUG_ENABLED);
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
if (debugPref) {
|
|
||||||
debug = function(s) {
|
|
||||||
dump("-*- NetworkService: " + s + "\n");
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
debug = function(s) {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateDebug();
|
|
||||||
|
|
||||||
function netdResponseType(aCode) {
|
|
||||||
return Math.floor(aCode / 100) * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isError(aCode) {
|
|
||||||
let type = netdResponseType(aCode);
|
|
||||||
return (type !== NETD_COMMAND_PROCEEDING && type !== NETD_COMMAND_OKAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Task(aId, aParams, aSetupFunction) {
|
|
||||||
this.id = aId;
|
|
||||||
this.params = aParams;
|
|
||||||
this.setupFunction = aSetupFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
function NetworkWorkerRequestQueue(aNetworkService) {
|
|
||||||
this.networkService = aNetworkService;
|
|
||||||
this.tasks = [];
|
|
||||||
}
|
|
||||||
NetworkWorkerRequestQueue.prototype = {
|
|
||||||
runQueue: function() {
|
|
||||||
if (this.tasks.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let task = this.tasks[0];
|
|
||||||
debug("run task id: " + task.id);
|
|
||||||
|
|
||||||
if (typeof task.setupFunction === 'function') {
|
|
||||||
// If setupFunction returns false, skip sending to Network Worker but call
|
|
||||||
// handleWorkerMessage() directly with task id, as if the response was
|
|
||||||
// returned from Network Worker.
|
|
||||||
if (!task.setupFunction()) {
|
|
||||||
this.networkService.handleWorkerMessage({id: task.id});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gNetworkWorker.postMessage(task.params);
|
|
||||||
},
|
|
||||||
|
|
||||||
enqueue: function(aId, aParams, aSetupFunction) {
|
|
||||||
debug("enqueue id: " + aId);
|
|
||||||
this.tasks.push(new Task(aId, aParams, aSetupFunction));
|
|
||||||
|
|
||||||
if (this.tasks.length === 1) {
|
|
||||||
this.runQueue();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
dequeue: function(aId) {
|
|
||||||
debug("dequeue id: " + aId);
|
|
||||||
|
|
||||||
if (!this.tasks.length || this.tasks[0].id != aId) {
|
|
||||||
debug("Id " + aId + " is not on top of the queue");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.tasks.shift();
|
|
||||||
if (this.tasks.length > 0) {
|
|
||||||
// Run queue on the next tick.
|
|
||||||
Services.tm.currentThread.dispatch(() => {
|
|
||||||
this.runQueue();
|
|
||||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This component watches for network interfaces changing state and then
|
|
||||||
* adjusts routes etc. accordingly.
|
|
||||||
*/
|
|
||||||
function NetworkService() {
|
|
||||||
debug("Starting NetworkService.");
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
if (gNetworkWorker) {
|
|
||||||
let networkListener = {
|
|
||||||
onEvent: function(aEvent) {
|
|
||||||
self.handleWorkerMessage(aEvent);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
gNetworkWorker.start(networkListener);
|
|
||||||
}
|
|
||||||
// Callbacks to invoke when a reply arrives from the net_worker.
|
|
||||||
this.controlCallbacks = Object.create(null);
|
|
||||||
|
|
||||||
this.addedRoutes = new Map();
|
|
||||||
this.netWorkerRequestQueue = new NetworkWorkerRequestQueue(this);
|
|
||||||
this.shutdown = false;
|
|
||||||
|
|
||||||
Services.prefs.addObserver(PREF_NETWORK_DEBUG_ENABLED, this, false);
|
|
||||||
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkService.prototype = {
|
|
||||||
classID: NETWORKSERVICE_CID,
|
|
||||||
classInfo: XPCOMUtils.generateCI({classID: NETWORKSERVICE_CID,
|
|
||||||
contractID: NETWORKSERVICE_CONTRACTID,
|
|
||||||
classDescription: "Network Service",
|
|
||||||
interfaces: [Ci.nsINetworkService]}),
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkService,
|
|
||||||
Ci.nsIObserver]),
|
|
||||||
|
|
||||||
addedRoutes: null,
|
|
||||||
|
|
||||||
shutdown: false,
|
|
||||||
|
|
||||||
// nsIObserver
|
|
||||||
|
|
||||||
observe: function(aSubject, aTopic, aData) {
|
|
||||||
switch (aTopic) {
|
|
||||||
case TOPIC_PREF_CHANGED:
|
|
||||||
if (aData === PREF_NETWORK_DEBUG_ENABLED) {
|
|
||||||
updateDebug();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TOPIC_XPCOM_SHUTDOWN:
|
|
||||||
debug("NetworkService shutdown");
|
|
||||||
this.shutdown = true;
|
|
||||||
if (gNetworkWorker) {
|
|
||||||
gNetworkWorker.shutdown();
|
|
||||||
gNetworkWorker = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
|
|
||||||
Services.prefs.removeObserver(PREF_NETWORK_DEBUG_ENABLED, this);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
|
|
||||||
idgen: 0,
|
|
||||||
controlMessage: function(aParams, aCallback, aSetupFunction) {
|
|
||||||
if (this.shutdown) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = this.idgen++;
|
|
||||||
aParams.id = id;
|
|
||||||
if (aCallback) {
|
|
||||||
this.controlCallbacks[id] = aCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, we use aSetupFunction to determine if this command needs to be
|
|
||||||
// queued or not.
|
|
||||||
if (aSetupFunction) {
|
|
||||||
this.netWorkerRequestQueue.enqueue(id, aParams, aSetupFunction);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gNetworkWorker) {
|
|
||||||
gNetworkWorker.postMessage(aParams);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleWorkerMessage: function(aResponse) {
|
|
||||||
debug("NetworkManager received message from worker: " + JSON.stringify(aResponse));
|
|
||||||
let id = aResponse.id;
|
|
||||||
if (aResponse.broadcast === true) {
|
|
||||||
Services.obs.notifyObservers(null, aResponse.topic, aResponse.reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let callback = this.controlCallbacks[id];
|
|
||||||
if (callback) {
|
|
||||||
callback.call(this, aResponse);
|
|
||||||
delete this.controlCallbacks[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.netWorkerRequestQueue.dequeue(id);
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsINetworkService
|
|
||||||
|
|
||||||
getNetworkInterfaceStats: function(aInterfaceName, aCallback) {
|
|
||||||
debug("getNetworkInterfaceStats for " + aInterfaceName);
|
|
||||||
|
|
||||||
let file = new FileUtils.File("/proc/net/dev");
|
|
||||||
if (!file) {
|
|
||||||
aCallback.networkStatsAvailable(false, 0, 0, Date.now());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NetUtil.asyncFetch({
|
|
||||||
uri: NetUtil.newURI(file),
|
|
||||||
loadUsingSystemPrincipal: true
|
|
||||||
}, function(inputStream, status) {
|
|
||||||
let rxBytes = 0,
|
|
||||||
txBytes = 0,
|
|
||||||
now = Date.now();
|
|
||||||
|
|
||||||
if (Components.isSuccessCode(status)) {
|
|
||||||
// Find record for corresponding interface.
|
|
||||||
let statExpr = /(\S+): +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+/;
|
|
||||||
let data =
|
|
||||||
NetUtil.readInputStreamToString(inputStream, inputStream.available())
|
|
||||||
.split("\n");
|
|
||||||
for (let i = 2; i < data.length; i++) {
|
|
||||||
let parseResult = statExpr.exec(data[i]);
|
|
||||||
if (parseResult && parseResult[1] === aInterfaceName) {
|
|
||||||
rxBytes = parseInt(parseResult[2], 10);
|
|
||||||
txBytes = parseInt(parseResult[3], 10);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// netd always return success even interface doesn't exist.
|
|
||||||
aCallback.networkStatsAvailable(true, rxBytes, txBytes, now);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setNetworkTetheringAlarm(aEnable, aInterface) {
|
|
||||||
// Method called when enabling disabling tethering, it checks if there is
|
|
||||||
// some alarm active and move from interfaceAlarm to globalAlarm because
|
|
||||||
// interfaceAlarm doens't work in tethering scenario due to forwarding.
|
|
||||||
debug("setNetworkTetheringAlarm for tethering" + aEnable);
|
|
||||||
|
|
||||||
let filename = aEnable ? "/proc/net/xt_quota/" + aInterface + "Alert" :
|
|
||||||
"/proc/net/xt_quota/globalAlert";
|
|
||||||
|
|
||||||
let file = new FileUtils.File(filename);
|
|
||||||
if (!file) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NetUtil.asyncFetch({
|
|
||||||
uri: NetUtil.newURI(file),
|
|
||||||
loadUsingSystemPrincipal: true
|
|
||||||
}, (inputStream, status) => {
|
|
||||||
if (Components.isSuccessCode(status)) {
|
|
||||||
let data = NetUtil.readInputStreamToString(inputStream, inputStream.available())
|
|
||||||
.split("\n");
|
|
||||||
if (data) {
|
|
||||||
let threshold = parseInt(data[0], 10);
|
|
||||||
|
|
||||||
this._setNetworkTetheringAlarm(aEnable, aInterface, threshold);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_setNetworkTetheringAlarm(aEnable, aInterface, aThreshold, aCallback) {
|
|
||||||
debug("_setNetworkTetheringAlarm for tethering" + aEnable);
|
|
||||||
|
|
||||||
let cmd = aEnable ? "setTetheringAlarm" : "removeTetheringAlarm";
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
cmd: cmd,
|
|
||||||
ifname: aInterface,
|
|
||||||
threshold: aThreshold,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aData) {
|
|
||||||
let code = aData.resultCode;
|
|
||||||
let reason = aData.resultReason;
|
|
||||||
let enableString = aEnable ? "Enable" : "Disable";
|
|
||||||
debug(enableString + " tethering Alarm result: Code " + code + " reason " + reason);
|
|
||||||
if (aCallback) {
|
|
||||||
aCallback.networkUsageAlarmResult(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setNetworkInterfaceAlarm: function(aInterfaceName, aThreshold, aCallback) {
|
|
||||||
if (!aInterfaceName) {
|
|
||||||
aCallback.networkUsageAlarmResult(-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
this._disableNetworkInterfaceAlarm(aInterfaceName, function(aResult) {
|
|
||||||
if (aThreshold < 0) {
|
|
||||||
if (!isError(aResult.resultCode)) {
|
|
||||||
aCallback.networkUsageAlarmResult(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
aCallback.networkUsageAlarmResult(aResult.reason);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if tethering is enabled
|
|
||||||
let params = {
|
|
||||||
cmd: "getTetheringStatus"
|
|
||||||
};
|
|
||||||
|
|
||||||
self.controlMessage(params, function(aResult) {
|
|
||||||
if (isError(aResult.resultCode)) {
|
|
||||||
aCallback.networkUsageAlarmResult(aResult.reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aResult.resultReason.indexOf('started') == -1) {
|
|
||||||
// Tethering disabled, set interfaceAlarm
|
|
||||||
self._setNetworkInterfaceAlarm(aInterfaceName, aThreshold, aCallback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tethering enabled, set globalAlarm
|
|
||||||
self._setNetworkTetheringAlarm(true, aInterfaceName, aThreshold, aCallback);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_setNetworkInterfaceAlarm: function(aInterfaceName, aThreshold, aCallback) {
|
|
||||||
debug("setNetworkInterfaceAlarm for " + aInterfaceName + " at " + aThreshold + "bytes");
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
cmd: "setNetworkInterfaceAlarm",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
threshold: aThreshold
|
|
||||||
};
|
|
||||||
|
|
||||||
params.report = true;
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
if (!isError(aResult.resultCode)) {
|
|
||||||
aCallback.networkUsageAlarmResult(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._enableNetworkInterfaceAlarm(aInterfaceName, aThreshold, aCallback);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_enableNetworkInterfaceAlarm: function(aInterfaceName, aThreshold, aCallback) {
|
|
||||||
debug("enableNetworkInterfaceAlarm for " + aInterfaceName + " at " + aThreshold + "bytes");
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
cmd: "enableNetworkInterfaceAlarm",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
threshold: aThreshold
|
|
||||||
};
|
|
||||||
|
|
||||||
params.report = true;
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
if (!isError(aResult.resultCode)) {
|
|
||||||
aCallback.networkUsageAlarmResult(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
aCallback.networkUsageAlarmResult(aResult.reason);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_disableNetworkInterfaceAlarm: function(aInterfaceName, aCallback) {
|
|
||||||
debug("disableNetworkInterfaceAlarm for " + aInterfaceName);
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
cmd: "disableNetworkInterfaceAlarm",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
};
|
|
||||||
|
|
||||||
params.report = true;
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback(aResult);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setWifiOperationMode: function(aInterfaceName, aMode, aCallback) {
|
|
||||||
debug("setWifiOperationMode on " + aInterfaceName + " to " + aMode);
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
cmd: "setWifiOperationMode",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
mode: aMode
|
|
||||||
};
|
|
||||||
|
|
||||||
params.report = true;
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
if (isError(aResult.resultCode)) {
|
|
||||||
aCallback.wifiOperationModeResult("netd command error");
|
|
||||||
} else {
|
|
||||||
aCallback.wifiOperationModeResult(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
resetRoutingTable: function(aInterfaceName, aCallback) {
|
|
||||||
let options = {
|
|
||||||
cmd: "removeNetworkRoute",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(options, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setDNS: function(aInterfaceName, aDnsesCount, aDnses, aGatewaysCount,
|
|
||||||
aGateways, aCallback) {
|
|
||||||
debug("Going to set DNS to " + aInterfaceName);
|
|
||||||
let options = {
|
|
||||||
cmd: "setDNS",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
domain: "mozilla." + aInterfaceName + ".domain",
|
|
||||||
dnses: aDnses,
|
|
||||||
gateways: aGateways
|
|
||||||
};
|
|
||||||
this.controlMessage(options, function(aResult) {
|
|
||||||
aCallback.setDnsResult(aResult.success ? null : aResult.reason);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setDefaultRoute: function(aInterfaceName, aCount, aGateways, aCallback) {
|
|
||||||
debug("Going to change default route to " + aInterfaceName);
|
|
||||||
let options = {
|
|
||||||
cmd: "setDefaultRoute",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
gateways: aGateways
|
|
||||||
};
|
|
||||||
this.controlMessage(options, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
removeDefaultRoute: function(aInterfaceName, aCount, aGateways, aCallback) {
|
|
||||||
debug("Remove default route for " + aInterfaceName);
|
|
||||||
let options = {
|
|
||||||
cmd: "removeDefaultRoute",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
gateways: aGateways
|
|
||||||
};
|
|
||||||
this.controlMessage(options, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_routeToString: function(aInterfaceName, aHost, aPrefixLength, aGateway) {
|
|
||||||
return aHost + "-" + aPrefixLength + "-" + aGateway + "-" + aInterfaceName;
|
|
||||||
},
|
|
||||||
|
|
||||||
modifyRoute: function(aAction, aInterfaceName, aHost, aPrefixLength, aGateway) {
|
|
||||||
let command;
|
|
||||||
|
|
||||||
switch (aAction) {
|
|
||||||
case Ci.nsINetworkService.MODIFY_ROUTE_ADD:
|
|
||||||
command = 'addHostRoute';
|
|
||||||
break;
|
|
||||||
case Ci.nsINetworkService.MODIFY_ROUTE_REMOVE:
|
|
||||||
command = 'removeHostRoute';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
debug('Unknown action: ' + aAction);
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
|
|
||||||
let route = this._routeToString(aInterfaceName, aHost, aPrefixLength, aGateway);
|
|
||||||
let setupFunc = () => {
|
|
||||||
let count = this.addedRoutes.get(route);
|
|
||||||
debug(command + ": " + route + " -> " + count);
|
|
||||||
|
|
||||||
// Return false if there is no need to send the command to network worker.
|
|
||||||
if ((aAction == Ci.nsINetworkService.MODIFY_ROUTE_ADD && count) ||
|
|
||||||
(aAction == Ci.nsINetworkService.MODIFY_ROUTE_REMOVE &&
|
|
||||||
(!count || count > 1))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
debug(command + " " + aHost + " on " + aInterfaceName);
|
|
||||||
let options = {
|
|
||||||
cmd: command,
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
gateway: aGateway,
|
|
||||||
prefixLength: aPrefixLength,
|
|
||||||
ip: aHost
|
|
||||||
};
|
|
||||||
|
|
||||||
return new Promise((aResolve, aReject) => {
|
|
||||||
this.controlMessage(options, (aData) => {
|
|
||||||
let count = this.addedRoutes.get(route);
|
|
||||||
|
|
||||||
// Remove route from addedRoutes on success or failure.
|
|
||||||
if (aAction == Ci.nsINetworkService.MODIFY_ROUTE_REMOVE) {
|
|
||||||
if (count > 1) {
|
|
||||||
this.addedRoutes.set(route, count - 1);
|
|
||||||
} else {
|
|
||||||
this.addedRoutes.delete(route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aData.error) {
|
|
||||||
aReject(aData.reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aAction == Ci.nsINetworkService.MODIFY_ROUTE_ADD) {
|
|
||||||
this.addedRoutes.set(route, count ? count + 1 : 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
aResolve();
|
|
||||||
}, setupFunc);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
addSecondaryRoute: function(aInterfaceName, aRoute, aCallback) {
|
|
||||||
debug("Going to add route to secondary table on " + aInterfaceName);
|
|
||||||
let options = {
|
|
||||||
cmd: "addSecondaryRoute",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
ip: aRoute.ip,
|
|
||||||
prefix: aRoute.prefix,
|
|
||||||
gateway: aRoute.gateway
|
|
||||||
};
|
|
||||||
this.controlMessage(options, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
removeSecondaryRoute: function(aInterfaceName, aRoute, aCallback) {
|
|
||||||
debug("Going to remove route from secondary table on " + aInterfaceName);
|
|
||||||
let options = {
|
|
||||||
cmd: "removeSecondaryRoute",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
ip: aRoute.ip,
|
|
||||||
prefix: aRoute.prefix,
|
|
||||||
gateway: aRoute.gateway
|
|
||||||
};
|
|
||||||
this.controlMessage(options, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Enable/Disable DHCP server.
|
|
||||||
setDhcpServer: function(aEnabled, aConfig, aCallback) {
|
|
||||||
if (null === aConfig) {
|
|
||||||
aConfig = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
aConfig.cmd = "setDhcpServer";
|
|
||||||
aConfig.enabled = aEnabled;
|
|
||||||
|
|
||||||
this.controlMessage(aConfig, function(aResponse) {
|
|
||||||
if (!aResponse.success) {
|
|
||||||
aCallback.dhcpServerResult('Set DHCP server error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
aCallback.dhcpServerResult(null);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Enable/disable WiFi tethering by sending commands to netd.
|
|
||||||
setWifiTethering: function(aEnable, aConfig, aCallback) {
|
|
||||||
// config should've already contained:
|
|
||||||
// .ifname
|
|
||||||
// .internalIfname
|
|
||||||
// .externalIfname
|
|
||||||
aConfig.wifictrlinterfacename = WIFI_CTRL_INTERFACE;
|
|
||||||
aConfig.cmd = "setWifiTethering";
|
|
||||||
|
|
||||||
// The callback function in controlMessage may not be fired immediately.
|
|
||||||
this.controlMessage(aConfig, (aData) => {
|
|
||||||
let code = aData.resultCode;
|
|
||||||
let reason = aData.resultReason;
|
|
||||||
let enable = aData.enable;
|
|
||||||
let enableString = aEnable ? "Enable" : "Disable";
|
|
||||||
|
|
||||||
debug(enableString + " Wifi tethering result: Code " + code + " reason " + reason);
|
|
||||||
|
|
||||||
this.setNetworkTetheringAlarm(aEnable, aConfig.externalIfname);
|
|
||||||
|
|
||||||
if (isError(code)) {
|
|
||||||
aCallback.wifiTetheringEnabledChange("netd command error");
|
|
||||||
} else {
|
|
||||||
aCallback.wifiTetheringEnabledChange(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Enable/disable USB tethering by sending commands to netd.
|
|
||||||
setUSBTethering: function(aEnable, aConfig, aCallback) {
|
|
||||||
aConfig.cmd = "setUSBTethering";
|
|
||||||
// The callback function in controlMessage may not be fired immediately.
|
|
||||||
this.controlMessage(aConfig, (aData) => {
|
|
||||||
let code = aData.resultCode;
|
|
||||||
let reason = aData.resultReason;
|
|
||||||
let enable = aData.enable;
|
|
||||||
let enableString = aEnable ? "Enable" : "Disable";
|
|
||||||
|
|
||||||
debug(enableString + " USB tethering result: Code " + code + " reason " + reason);
|
|
||||||
|
|
||||||
this.setNetworkTetheringAlarm(aEnable, aConfig.externalIfname);
|
|
||||||
|
|
||||||
if (isError(code)) {
|
|
||||||
aCallback.usbTetheringEnabledChange("netd command error");
|
|
||||||
} else {
|
|
||||||
aCallback.usbTetheringEnabledChange(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Switch usb function by modifying property of persist.sys.usb.config.
|
|
||||||
enableUsbRndis: function(aEnable, aCallback) {
|
|
||||||
debug("enableUsbRndis: " + aEnable);
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
cmd: "enableUsbRndis",
|
|
||||||
enable: aEnable
|
|
||||||
};
|
|
||||||
// Ask net work to report the result when this value is set to true.
|
|
||||||
if (aCallback) {
|
|
||||||
params.report = true;
|
|
||||||
} else {
|
|
||||||
params.report = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The callback function in controlMessage may not be fired immediately.
|
|
||||||
//this._usbTetheringAction = TETHERING_STATE_ONGOING;
|
|
||||||
this.controlMessage(params, function(aData) {
|
|
||||||
aCallback.enableUsbRndisResult(aData.result, aData.enable);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
updateUpStream: function(aPrevious, aCurrent, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "updateUpStream",
|
|
||||||
preInternalIfname: aPrevious.internalIfname,
|
|
||||||
preExternalIfname: aPrevious.externalIfname,
|
|
||||||
curInternalIfname: aCurrent.internalIfname,
|
|
||||||
curExternalIfname: aCurrent.externalIfname
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aData) {
|
|
||||||
let code = aData.resultCode;
|
|
||||||
let reason = aData.resultReason;
|
|
||||||
debug("updateUpStream result: Code " + code + " reason " + reason);
|
|
||||||
aCallback.updateUpStreamResult(!isError(code), aData.curExternalIfname);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getInterfaces: function(callback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "getInterfaces",
|
|
||||||
isAsync: true
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(data) {
|
|
||||||
debug("getInterfaces result: " + JSON.stringify(data));
|
|
||||||
let success = !isError(data.resultCode);
|
|
||||||
callback.getInterfacesResult(success, data.interfaceList);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getInterfaceConfig: function(ifname, callback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "getInterfaceConfig",
|
|
||||||
ifname: ifname,
|
|
||||||
isAsync: true
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(data) {
|
|
||||||
debug("getInterfaceConfig result: " + JSON.stringify(data));
|
|
||||||
let success = !isError(data.resultCode);
|
|
||||||
let result = { ip: data.ipAddr,
|
|
||||||
prefix: data.prefixLength,
|
|
||||||
link: data.flag,
|
|
||||||
mac: data.macAddr };
|
|
||||||
callback.getInterfaceConfigResult(success, result);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setInterfaceConfig: function(config, callback) {
|
|
||||||
config.cmd = "setInterfaceConfig";
|
|
||||||
config.isAsync = true;
|
|
||||||
|
|
||||||
this.controlMessage(config, function(data) {
|
|
||||||
debug("setInterfaceConfig result: " + JSON.stringify(data));
|
|
||||||
let success = !isError(data.resultCode);
|
|
||||||
callback.setInterfaceConfigResult(success);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
configureInterface: function(aConfig, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "configureInterface",
|
|
||||||
ifname: aConfig.ifname,
|
|
||||||
ipaddr: aConfig.ipaddr,
|
|
||||||
mask: aConfig.mask,
|
|
||||||
gateway_long: aConfig.gateway,
|
|
||||||
dns1_long: aConfig.dns1,
|
|
||||||
dns2_long: aConfig.dns2,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
dhcpRequest: function(aInterfaceName, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "dhcpRequest",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.dhcpRequestResult(!aResult.error, aResult.error ? null : aResult);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
stopDhcp: function(aInterfaceName, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "stopDhcp",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
enableInterface: function(aInterfaceName, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "enableInterface",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
disableInterface: function(aInterfaceName, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "disableInterface",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
resetConnections: function(aInterfaceName, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "resetConnections",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
createNetwork: function(aInterfaceName, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "createNetwork",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyNetwork: function(aInterfaceName, aCallback) {
|
|
||||||
let params = {
|
|
||||||
cmd: "destroyNetwork",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getNetId: function(aInterfaceName) {
|
|
||||||
let params = {
|
|
||||||
cmd: "getNetId",
|
|
||||||
ifname: aInterfaceName
|
|
||||||
};
|
|
||||||
|
|
||||||
return new Promise((aResolve, aReject) => {
|
|
||||||
this.controlMessage(params, result => {
|
|
||||||
if (result.error) {
|
|
||||||
aReject(result.reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
aResolve(result.netId);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setMtu: function (aInterfaceName, aMtu, aCallback) {
|
|
||||||
debug("Set MTU on " + aInterfaceName + ": " + aMtu);
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
cmd: "setMtu",
|
|
||||||
ifname: aInterfaceName,
|
|
||||||
mtu: aMtu
|
|
||||||
};
|
|
||||||
|
|
||||||
this.controlMessage(params, function(aResult) {
|
|
||||||
aCallback.nativeCommandResult(!aResult.error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkService]);
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# NetworkService.js
|
|
||||||
component {48c13741-aec9-4a86-8962-432011708261} NetworkService.js
|
|
||||||
contract @mozilla.org/network/service;1 {48c13741-aec9-4a86-8962-432011708261}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,498 +0,0 @@
|
||||||
/* 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 NetworkUtils_h
|
|
||||||
#define NetworkUtils_h
|
|
||||||
|
|
||||||
#include "nsAutoPtr.h"
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "mozilla/dom/NetworkOptionsBinding.h"
|
|
||||||
#include "mozilla/dom/network/NetUtils.h"
|
|
||||||
#include "mozilla/ipc/Netd.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
#include "NetIdManager.h"
|
|
||||||
|
|
||||||
class NetworkParams;
|
|
||||||
class CommandChain;
|
|
||||||
|
|
||||||
class CommandCallback {
|
|
||||||
public:
|
|
||||||
typedef void (*CallbackType)(CommandChain*, bool,
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
|
|
||||||
typedef void (*CallbackWrapperType)(CallbackType aOriginalCallback,
|
|
||||||
CommandChain*, bool,
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
|
|
||||||
CommandCallback()
|
|
||||||
: mCallback(nullptr)
|
|
||||||
, mCallbackWrapper(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandCallback(CallbackType aCallback)
|
|
||||||
: mCallback(aCallback)
|
|
||||||
, mCallbackWrapper(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandCallback(CallbackWrapperType aCallbackWrapper,
|
|
||||||
CommandCallback aOriginalCallback)
|
|
||||||
: mCallback(aOriginalCallback.mCallback)
|
|
||||||
, mCallbackWrapper(aCallbackWrapper)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(CommandChain* aChain, bool aError,
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult)
|
|
||||||
{
|
|
||||||
if (mCallbackWrapper) {
|
|
||||||
return mCallbackWrapper(mCallback, aChain, aError, aResult);
|
|
||||||
}
|
|
||||||
if (mCallback) {
|
|
||||||
return mCallback(aChain, aError, aResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
CallbackType mCallback;
|
|
||||||
CallbackWrapperType mCallbackWrapper;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void (*CommandFunc)(CommandChain*, CommandCallback,
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
typedef void (*MessageCallback)(mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
typedef void (*ErrorCallback)(NetworkParams& aOptions,
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
|
|
||||||
class NetworkParams
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NetworkParams() {
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkParams(const mozilla::dom::NetworkCommandOptions& aOther) {
|
|
||||||
|
|
||||||
#define COPY_SEQUENCE_FIELD(prop, type) \
|
|
||||||
if (aOther.prop.WasPassed()) { \
|
|
||||||
mozilla::dom::Sequence<type > const & currentValue = aOther.prop.InternalValue(); \
|
|
||||||
uint32_t length = currentValue.Length(); \
|
|
||||||
for (uint32_t idx = 0; idx < length; idx++) { \
|
|
||||||
prop.AppendElement(currentValue[idx]); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define COPY_OPT_STRING_FIELD(prop, defaultValue) \
|
|
||||||
if (aOther.prop.WasPassed()) { \
|
|
||||||
if (aOther.prop.Value().EqualsLiteral("null")) { \
|
|
||||||
prop = defaultValue; \
|
|
||||||
} else { \
|
|
||||||
prop = aOther.prop.Value(); \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
prop = defaultValue; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define COPY_OPT_FIELD(prop, defaultValue) \
|
|
||||||
if (aOther.prop.WasPassed()) { \
|
|
||||||
prop = aOther.prop.Value(); \
|
|
||||||
} else { \
|
|
||||||
prop = defaultValue; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define COPY_FIELD(prop) prop = aOther.prop;
|
|
||||||
|
|
||||||
COPY_FIELD(mId)
|
|
||||||
COPY_FIELD(mCmd)
|
|
||||||
COPY_OPT_STRING_FIELD(mDomain, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mGateway, EmptyString())
|
|
||||||
COPY_SEQUENCE_FIELD(mGateways, nsString)
|
|
||||||
COPY_OPT_STRING_FIELD(mIfname, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mIp, EmptyString())
|
|
||||||
COPY_OPT_FIELD(mPrefixLength, 0)
|
|
||||||
COPY_OPT_STRING_FIELD(mMode, EmptyString())
|
|
||||||
COPY_OPT_FIELD(mReport, false)
|
|
||||||
COPY_OPT_FIELD(mEnabled, false)
|
|
||||||
COPY_OPT_STRING_FIELD(mWifictrlinterfacename, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mInternalIfname, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mExternalIfname, EmptyString())
|
|
||||||
COPY_OPT_FIELD(mEnable, false)
|
|
||||||
COPY_OPT_STRING_FIELD(mSsid, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mSecurity, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mKey, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mPrefix, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mLink, EmptyString())
|
|
||||||
COPY_SEQUENCE_FIELD(mInterfaceList, nsString)
|
|
||||||
COPY_OPT_STRING_FIELD(mWifiStartIp, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mWifiEndIp, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mUsbStartIp, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mUsbEndIp, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mDns1, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mDns2, EmptyString())
|
|
||||||
COPY_SEQUENCE_FIELD(mDnses, nsString)
|
|
||||||
COPY_OPT_STRING_FIELD(mStartIp, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mEndIp, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mServerIp, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mMaskLength, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mPreInternalIfname, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mPreExternalIfname, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mCurInternalIfname, EmptyString())
|
|
||||||
COPY_OPT_STRING_FIELD(mCurExternalIfname, EmptyString())
|
|
||||||
COPY_OPT_FIELD(mThreshold, -1)
|
|
||||||
COPY_OPT_FIELD(mIpaddr, 0)
|
|
||||||
COPY_OPT_FIELD(mMask, 0)
|
|
||||||
COPY_OPT_FIELD(mGateway_long, 0)
|
|
||||||
COPY_OPT_FIELD(mDns1_long, 0)
|
|
||||||
COPY_OPT_FIELD(mDns2_long, 0)
|
|
||||||
COPY_OPT_FIELD(mMtu, 0)
|
|
||||||
|
|
||||||
mLoopIndex = 0;
|
|
||||||
|
|
||||||
#undef COPY_SEQUENCE_FIELD
|
|
||||||
#undef COPY_OPT_STRING_FIELD
|
|
||||||
#undef COPY_OPT_FIELD
|
|
||||||
#undef COPY_FIELD
|
|
||||||
}
|
|
||||||
|
|
||||||
// Followings attributes are 1-to-1 mapping to NetworkCommandOptions.
|
|
||||||
int32_t mId;
|
|
||||||
nsString mCmd;
|
|
||||||
nsString mDomain;
|
|
||||||
nsString mGateway;
|
|
||||||
nsTArray<nsString> mGateways;
|
|
||||||
nsString mIfname;
|
|
||||||
nsString mIp;
|
|
||||||
uint32_t mPrefixLength;
|
|
||||||
nsString mMode;
|
|
||||||
bool mReport;
|
|
||||||
bool mEnabled;
|
|
||||||
nsString mWifictrlinterfacename;
|
|
||||||
nsString mInternalIfname;
|
|
||||||
nsString mExternalIfname;
|
|
||||||
bool mEnable;
|
|
||||||
nsString mSsid;
|
|
||||||
nsString mSecurity;
|
|
||||||
nsString mKey;
|
|
||||||
nsString mPrefix;
|
|
||||||
nsString mLink;
|
|
||||||
nsTArray<nsString> mInterfaceList;
|
|
||||||
nsString mWifiStartIp;
|
|
||||||
nsString mWifiEndIp;
|
|
||||||
nsString mUsbStartIp;
|
|
||||||
nsString mUsbEndIp;
|
|
||||||
nsString mDns1;
|
|
||||||
nsString mDns2;
|
|
||||||
nsTArray<nsString> mDnses;
|
|
||||||
nsString mStartIp;
|
|
||||||
nsString mEndIp;
|
|
||||||
nsString mServerIp;
|
|
||||||
nsString mMaskLength;
|
|
||||||
nsString mPreInternalIfname;
|
|
||||||
nsString mPreExternalIfname;
|
|
||||||
nsString mCurInternalIfname;
|
|
||||||
nsString mCurExternalIfname;
|
|
||||||
long long mThreshold;
|
|
||||||
long mIpaddr;
|
|
||||||
long mMask;
|
|
||||||
long mGateway_long;
|
|
||||||
long mDns1_long;
|
|
||||||
long mDns2_long;
|
|
||||||
long mMtu;
|
|
||||||
|
|
||||||
// Auxiliary information required to carry accros command chain.
|
|
||||||
int mNetId; // A locally defined id per interface.
|
|
||||||
uint32_t mLoopIndex; // Loop index for adding/removing multiple gateways.
|
|
||||||
};
|
|
||||||
|
|
||||||
// CommandChain store the necessary information to execute command one by one.
|
|
||||||
// Including :
|
|
||||||
// 1. Command parameters.
|
|
||||||
// 2. Command list.
|
|
||||||
// 3. Error callback function.
|
|
||||||
// 4. Index of current execution command.
|
|
||||||
class CommandChain final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CommandChain(const NetworkParams& aParams,
|
|
||||||
const CommandFunc aCmds[],
|
|
||||||
uint32_t aLength,
|
|
||||||
ErrorCallback aError)
|
|
||||||
: mIndex(-1)
|
|
||||||
, mParams(aParams)
|
|
||||||
, mCommands(aCmds)
|
|
||||||
, mLength(aLength)
|
|
||||||
, mError(aError) {
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkParams&
|
|
||||||
getParams()
|
|
||||||
{
|
|
||||||
return mParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
CommandFunc
|
|
||||||
getNextCommand()
|
|
||||||
{
|
|
||||||
mIndex++;
|
|
||||||
return mIndex < mLength ? mCommands[mIndex] : nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
ErrorCallback
|
|
||||||
getErrorCallback() const
|
|
||||||
{
|
|
||||||
return mError;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t mIndex;
|
|
||||||
NetworkParams mParams;
|
|
||||||
const CommandFunc* mCommands;
|
|
||||||
uint32_t mLength;
|
|
||||||
ErrorCallback mError;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A helper class to easily construct a resolved
|
|
||||||
// or a pending result for command execution.
|
|
||||||
class CommandResult
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
struct Pending {};
|
|
||||||
|
|
||||||
public:
|
|
||||||
CommandResult(int32_t aResultCode);
|
|
||||||
CommandResult(const mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
CommandResult(const Pending&);
|
|
||||||
bool isPending() const;
|
|
||||||
|
|
||||||
mozilla::dom::NetworkResultOptions mResult;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool mIsPending;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NetworkUtils final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NetworkUtils(MessageCallback aCallback);
|
|
||||||
~NetworkUtils();
|
|
||||||
|
|
||||||
void ExecuteCommand(NetworkParams aOptions);
|
|
||||||
void onNetdMessage(mozilla::ipc::NetdCommand* aCommand);
|
|
||||||
|
|
||||||
MessageCallback getMessageCallback() { return mMessageCallback; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Commands supported by NetworkUtils.
|
|
||||||
*/
|
|
||||||
CommandResult configureInterface(NetworkParams& aOptions);
|
|
||||||
CommandResult dhcpRequest(NetworkParams& aOptions);
|
|
||||||
CommandResult stopDhcp(NetworkParams& aOptions);
|
|
||||||
CommandResult enableInterface(NetworkParams& aOptions);
|
|
||||||
CommandResult disableInterface(NetworkParams& aOptions);
|
|
||||||
CommandResult resetConnections(NetworkParams& aOptions);
|
|
||||||
CommandResult setDefaultRoute(NetworkParams& aOptions);
|
|
||||||
CommandResult addHostRoute(NetworkParams& aOptions);
|
|
||||||
CommandResult removeDefaultRoute(NetworkParams& aOptions);
|
|
||||||
CommandResult removeHostRoute(NetworkParams& aOptions);
|
|
||||||
CommandResult removeNetworkRoute(NetworkParams& aOptions);
|
|
||||||
CommandResult setDNS(NetworkParams& aOptions);
|
|
||||||
CommandResult addSecondaryRoute(NetworkParams& aOptions);
|
|
||||||
CommandResult removeSecondaryRoute(NetworkParams& aOptions);
|
|
||||||
CommandResult setNetworkInterfaceAlarm(NetworkParams& aOptions);
|
|
||||||
CommandResult enableNetworkInterfaceAlarm(NetworkParams& aOptions);
|
|
||||||
CommandResult disableNetworkInterfaceAlarm(NetworkParams& aOptions);
|
|
||||||
CommandResult setTetheringAlarm(NetworkParams& aOptions);
|
|
||||||
CommandResult removeTetheringAlarm(NetworkParams& aOptions);
|
|
||||||
CommandResult getTetheringStatus(NetworkParams& aOptions);
|
|
||||||
CommandResult setWifiOperationMode(NetworkParams& aOptions);
|
|
||||||
CommandResult setDhcpServer(NetworkParams& aOptions);
|
|
||||||
CommandResult setWifiTethering(NetworkParams& aOptions);
|
|
||||||
CommandResult setUSBTethering(NetworkParams& aOptions);
|
|
||||||
CommandResult enableUsbRndis(NetworkParams& aOptions);
|
|
||||||
CommandResult updateUpStream(NetworkParams& aOptions);
|
|
||||||
CommandResult createNetwork(NetworkParams& aOptions);
|
|
||||||
CommandResult destroyNetwork(NetworkParams& aOptions);
|
|
||||||
CommandResult getNetId(NetworkParams& aOptions);
|
|
||||||
CommandResult setMtu(NetworkParams& aOptions);
|
|
||||||
CommandResult getInterfaces(NetworkParams& aOptions);
|
|
||||||
CommandResult getInterfaceConfig(NetworkParams& aOptions);
|
|
||||||
CommandResult setInterfaceConfig(NetworkParams& aOptions);
|
|
||||||
|
|
||||||
CommandResult addHostRouteLegacy(NetworkParams& aOptions);
|
|
||||||
CommandResult removeHostRouteLegacy(NetworkParams& aOptions);
|
|
||||||
CommandResult setDefaultRouteLegacy(NetworkParams& aOptions);
|
|
||||||
CommandResult removeDefaultRouteLegacy(NetworkParams& aOptions);
|
|
||||||
CommandResult removeNetworkRouteLegacy(NetworkParams& aOptions);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* function pointer array holds all netd commands should be executed
|
|
||||||
* in sequence to accomplish a given command by other module.
|
|
||||||
*/
|
|
||||||
static const CommandFunc sWifiEnableChain[];
|
|
||||||
static const CommandFunc sWifiDisableChain[];
|
|
||||||
static const CommandFunc sWifiFailChain[];
|
|
||||||
static const CommandFunc sWifiRetryChain[];
|
|
||||||
static const CommandFunc sWifiOperationModeChain[];
|
|
||||||
static const CommandFunc sUSBEnableChain[];
|
|
||||||
static const CommandFunc sUSBDisableChain[];
|
|
||||||
static const CommandFunc sUSBFailChain[];
|
|
||||||
static const CommandFunc sUpdateUpStreamChain[];
|
|
||||||
static const CommandFunc sStartDhcpServerChain[];
|
|
||||||
static const CommandFunc sStopDhcpServerChain[];
|
|
||||||
static const CommandFunc sNetworkInterfaceEnableAlarmChain[];
|
|
||||||
static const CommandFunc sNetworkInterfaceDisableAlarmChain[];
|
|
||||||
static const CommandFunc sNetworkInterfaceSetAlarmChain[];
|
|
||||||
static const CommandFunc sTetheringInterfaceSetAlarmChain[];
|
|
||||||
static const CommandFunc sTetheringInterfaceRemoveAlarmChain[];
|
|
||||||
static const CommandFunc sTetheringGetStatusChain[];
|
|
||||||
static const CommandFunc sGetInterfacesChain[];
|
|
||||||
static const CommandFunc sGetInterfaceConfigChain[];
|
|
||||||
static const CommandFunc sSetInterfaceConfigChain[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Individual netd command stored in command chain.
|
|
||||||
*/
|
|
||||||
#define PARAMS CommandChain* aChain, CommandCallback aCallback, \
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult
|
|
||||||
static void wifiFirmwareReload(PARAMS);
|
|
||||||
static void startAccessPointDriver(PARAMS);
|
|
||||||
static void stopAccessPointDriver(PARAMS);
|
|
||||||
static void setAccessPoint(PARAMS);
|
|
||||||
static void cleanUpStream(PARAMS);
|
|
||||||
static void createUpStream(PARAMS);
|
|
||||||
static void startSoftAP(PARAMS);
|
|
||||||
static void stopSoftAP(PARAMS);
|
|
||||||
static void clearWifiTetherParms(PARAMS);
|
|
||||||
static void enableAlarm(PARAMS);
|
|
||||||
static void disableAlarm(PARAMS);
|
|
||||||
static void setQuota(PARAMS);
|
|
||||||
static void removeQuota(PARAMS);
|
|
||||||
static void setAlarm(PARAMS);
|
|
||||||
static void removeAlarm(PARAMS);
|
|
||||||
static void setGlobalAlarm(PARAMS);
|
|
||||||
static void removeGlobalAlarm(PARAMS);
|
|
||||||
static void tetherInterface(PARAMS);
|
|
||||||
static void addInterfaceToLocalNetwork(PARAMS);
|
|
||||||
static void addRouteToLocalNetwork(PARAMS);
|
|
||||||
static void preTetherInterfaceList(PARAMS);
|
|
||||||
static void postTetherInterfaceList(PARAMS);
|
|
||||||
static void addUpstreamInterface(PARAMS);
|
|
||||||
static void removeUpstreamInterface(PARAMS);
|
|
||||||
static void setIpForwardingEnabled(PARAMS);
|
|
||||||
static void tetheringStatus(PARAMS);
|
|
||||||
static void stopTethering(PARAMS);
|
|
||||||
static void startTethering(PARAMS);
|
|
||||||
static void untetherInterface(PARAMS);
|
|
||||||
static void removeInterfaceFromLocalNetwork(PARAMS);
|
|
||||||
static void setDnsForwarders(PARAMS);
|
|
||||||
static void enableNat(PARAMS);
|
|
||||||
static void disableNat(PARAMS);
|
|
||||||
static void setDefaultInterface(PARAMS);
|
|
||||||
static void setInterfaceDns(PARAMS);
|
|
||||||
static void getInterfaceList(PARAMS);
|
|
||||||
static void getConfig(PARAMS);
|
|
||||||
static void setConfig(PARAMS);
|
|
||||||
static void wifiTetheringSuccess(PARAMS);
|
|
||||||
static void usbTetheringSuccess(PARAMS);
|
|
||||||
static void networkInterfaceAlarmSuccess(PARAMS);
|
|
||||||
static void updateUpStreamSuccess(PARAMS);
|
|
||||||
static void setDhcpServerSuccess(PARAMS);
|
|
||||||
static void wifiOperationModeSuccess(PARAMS);
|
|
||||||
static void clearAddrForInterface(PARAMS);
|
|
||||||
static void createNetwork(PARAMS);
|
|
||||||
static void destroyNetwork(PARAMS);
|
|
||||||
static void addInterfaceToNetwork(PARAMS);
|
|
||||||
static void addDefaultRouteToNetwork(PARAMS);
|
|
||||||
static void setDefaultNetwork(PARAMS);
|
|
||||||
static void removeDefaultRoute(PARAMS);
|
|
||||||
static void removeNetworkRouteSuccess(PARAMS);
|
|
||||||
static void removeNetworkRoute(PARAMS);
|
|
||||||
static void addRouteToInterface(PARAMS);
|
|
||||||
static void removeRouteFromInterface(PARAMS);
|
|
||||||
static void modifyRouteOnInterface(PARAMS, bool aDoAdd);
|
|
||||||
static void enableIpv6(PARAMS);
|
|
||||||
static void disableIpv6(PARAMS);
|
|
||||||
static void setMtu(PARAMS);
|
|
||||||
static void setIpv6Enabled(PARAMS, bool aEnabled);
|
|
||||||
static void addRouteToSecondaryTable(PARAMS);
|
|
||||||
static void removeRouteFromSecondaryTable(PARAMS);
|
|
||||||
static void defaultAsyncSuccessHandler(PARAMS);
|
|
||||||
static void getInterfacesSuccess(PARAMS);
|
|
||||||
static void getInterfaceConfigSuccess(PARAMS);
|
|
||||||
static void setInterfaceConfigSuccess(PARAMS);
|
|
||||||
|
|
||||||
#undef PARAMS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error callback function executed when a command is fail.
|
|
||||||
*/
|
|
||||||
#define PARAMS NetworkParams& aOptions, \
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult
|
|
||||||
static void wifiTetheringFail(PARAMS);
|
|
||||||
static void wifiOperationModeFail(PARAMS);
|
|
||||||
static void usbTetheringFail(PARAMS);
|
|
||||||
static void updateUpStreamFail(PARAMS);
|
|
||||||
static void setDhcpServerFail(PARAMS);
|
|
||||||
static void networkInterfaceAlarmFail(PARAMS);
|
|
||||||
static void setDnsFail(PARAMS);
|
|
||||||
static void defaultAsyncFailureHandler(PARAMS);
|
|
||||||
static void getInterfacesFail(PARAMS);
|
|
||||||
static void getInterfaceConfigFail(PARAMS);
|
|
||||||
static void setInterfaceConfigFail(PARAMS);
|
|
||||||
|
|
||||||
#undef PARAMS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command chain processing functions.
|
|
||||||
*/
|
|
||||||
static void next(CommandChain* aChain, bool aError,
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
static void nextNetdCommand();
|
|
||||||
static void doCommand(const char* aCommand, CommandChain* aChain, CommandCallback aCallback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify broadcast message to main thread.
|
|
||||||
*/
|
|
||||||
void sendBroadcastMessage(uint32_t code, char* reason);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility functions.
|
|
||||||
*/
|
|
||||||
CommandResult checkUsbRndisState(NetworkParams& aOptions);
|
|
||||||
void dumpParams(NetworkParams& aOptions, const char* aType);
|
|
||||||
|
|
||||||
static void escapeQuote(nsCString& aString);
|
|
||||||
inline uint32_t netdResponseType(uint32_t code);
|
|
||||||
inline bool isBroadcastMessage(uint32_t code);
|
|
||||||
inline bool isError(uint32_t code);
|
|
||||||
inline bool isComplete(uint32_t code);
|
|
||||||
inline bool isProceeding(uint32_t code);
|
|
||||||
void Shutdown();
|
|
||||||
static void runNextQueuedCommandChain();
|
|
||||||
static void finalizeSuccess(CommandChain* aChain,
|
|
||||||
mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
|
|
||||||
template<size_t N>
|
|
||||||
static void runChain(const NetworkParams& aParams,
|
|
||||||
const CommandFunc (&aCmds)[N],
|
|
||||||
ErrorCallback aError);
|
|
||||||
|
|
||||||
static nsCString getSubnetIp(const nsCString& aIp, int aPrefixLength);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function to send netd result to main thread.
|
|
||||||
*/
|
|
||||||
MessageCallback mMessageCallback;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Utility class to access libnetutils.
|
|
||||||
*/
|
|
||||||
nsAutoPtr<NetUtils> mNetUtils;
|
|
||||||
|
|
||||||
NetIdManager mNetIdManager;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
/* 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 "NetworkWorker.h"
|
|
||||||
#include "NetworkUtils.h"
|
|
||||||
#include <nsThreadUtils.h>
|
|
||||||
#include "mozilla/ModuleUtils.h"
|
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
|
||||||
#include "mozilla/dom/ScriptSettings.h"
|
|
||||||
#include "mozilla/dom/ToJSValue.h"
|
|
||||||
#include "nsAutoPtr.h"
|
|
||||||
#include "nsXULAppAPI.h"
|
|
||||||
|
|
||||||
#define NS_NETWORKWORKER_CID \
|
|
||||||
{ 0x6df093e1, 0x8127, 0x4fa7, {0x90, 0x13, 0xa3, 0xaa, 0xa7, 0x79, 0xbb, 0xdd} }
|
|
||||||
|
|
||||||
using namespace mozilla;
|
|
||||||
using namespace mozilla::dom;
|
|
||||||
using namespace mozilla::ipc;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
nsCOMPtr<nsIThread> gWorkerThread;
|
|
||||||
|
|
||||||
// The singleton network worker, to be used on the main thread.
|
|
||||||
StaticRefPtr<NetworkWorker> gNetworkWorker;
|
|
||||||
|
|
||||||
// The singleton networkutils class, that can be used on any thread.
|
|
||||||
static nsAutoPtr<NetworkUtils> gNetworkUtils;
|
|
||||||
|
|
||||||
// Runnable used dispatch command result on the main thread.
|
|
||||||
class NetworkResultDispatcher : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NetworkResultDispatcher(const NetworkResultOptions& aResult)
|
|
||||||
: mResult(aResult)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
if (gNetworkWorker) {
|
|
||||||
gNetworkWorker->DispatchNetworkResult(mResult);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
NetworkResultOptions mResult;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Runnable used dispatch netd command on the worker thread.
|
|
||||||
class NetworkCommandDispatcher : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NetworkCommandDispatcher(const NetworkParams& aParams)
|
|
||||||
: mParams(aParams)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
|
|
||||||
if (gNetworkUtils) {
|
|
||||||
gNetworkUtils->ExecuteCommand(mParams);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
NetworkParams mParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Runnable used dispatch netd result on the worker thread.
|
|
||||||
class NetdEventRunnable : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NetdEventRunnable(NetdCommand* aCommand)
|
|
||||||
: mCommand(aCommand)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
|
|
||||||
if (gNetworkUtils) {
|
|
||||||
gNetworkUtils->onNetdMessage(mCommand);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsAutoPtr<NetdCommand> mCommand;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NetdMessageConsumer : public NetdConsumer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NetdMessageConsumer()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
}
|
|
||||||
|
|
||||||
void MessageReceived(NetdCommand* aCommand)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = new NetdEventRunnable(aCommand);
|
|
||||||
if (gWorkerThread) {
|
|
||||||
gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(NetworkWorker, nsINetworkWorker)
|
|
||||||
|
|
||||||
NetworkWorker::NetworkWorker()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
MOZ_ASSERT(!gNetworkWorker);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkWorker::~NetworkWorker()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!gNetworkWorker);
|
|
||||||
MOZ_ASSERT(!mListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<NetworkWorker>
|
|
||||||
NetworkWorker::FactoryCreate()
|
|
||||||
{
|
|
||||||
if (!XRE_IsParentProcess()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
if (!gNetworkWorker) {
|
|
||||||
gNetworkWorker = new NetworkWorker();
|
|
||||||
ClearOnShutdown(&gNetworkWorker);
|
|
||||||
|
|
||||||
gNetworkUtils = new NetworkUtils(NetworkWorker::NotifyResult);
|
|
||||||
ClearOnShutdown(&gNetworkUtils);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<NetworkWorker> worker = gNetworkWorker.get();
|
|
||||||
return worker.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
NetworkWorker::Start(nsINetworkEventListener* aListener)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
MOZ_ASSERT(aListener);
|
|
||||||
|
|
||||||
if (mListener) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult rv;
|
|
||||||
|
|
||||||
rv = NS_NewNamedThread("NetworkWorker", getter_AddRefs(gWorkerThread));
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("Can't create network control thread");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
StartNetd(new NetdMessageConsumer());
|
|
||||||
mListener = aListener;
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
NetworkWorker::Shutdown()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
if (!mListener) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
StopNetd();
|
|
||||||
|
|
||||||
gWorkerThread->Shutdown();
|
|
||||||
gWorkerThread = nullptr;
|
|
||||||
|
|
||||||
mListener = nullptr;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receive command from main thread (NetworkService.js).
|
|
||||||
NS_IMETHODIMP
|
|
||||||
NetworkWorker::PostMessage(JS::Handle<JS::Value> aOptions, JSContext* aCx)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
NetworkCommandOptions options;
|
|
||||||
if (!options.Init(aCx, aOptions)) {
|
|
||||||
NS_WARNING("Bad dictionary passed to NetworkWorker::SendCommand");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dispatch the command to the control thread.
|
|
||||||
NetworkParams NetworkParams(options);
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = new NetworkCommandDispatcher(NetworkParams);
|
|
||||||
if (gWorkerThread) {
|
|
||||||
gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
NetworkWorker::DispatchNetworkResult(const NetworkResultOptions& aOptions)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
mozilla::AutoSafeJSContext cx;
|
|
||||||
JS::RootedValue val(cx);
|
|
||||||
|
|
||||||
if (!ToJSValue(cx, aOptions, &val)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the listener with a JS value.
|
|
||||||
if (mListener) {
|
|
||||||
mListener->OnEvent(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback function from network worker thread to update result on main thread.
|
|
||||||
void
|
|
||||||
NetworkWorker::NotifyResult(NetworkResultOptions& aResult)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = new NetworkResultDispatcher(aResult);
|
|
||||||
NS_DispatchToMainThread(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(NetworkWorker,
|
|
||||||
NetworkWorker::FactoryCreate)
|
|
||||||
|
|
||||||
NS_DEFINE_NAMED_CID(NS_NETWORKWORKER_CID);
|
|
||||||
|
|
||||||
static const mozilla::Module::CIDEntry kNetworkWorkerCIDs[] = {
|
|
||||||
{ &kNS_NETWORKWORKER_CID, false, nullptr, NetworkWorkerConstructor },
|
|
||||||
{ nullptr }
|
|
||||||
};
|
|
||||||
|
|
||||||
static const mozilla::Module::ContractIDEntry kNetworkWorkerContracts[] = {
|
|
||||||
{ "@mozilla.org/network/worker;1", &kNS_NETWORKWORKER_CID },
|
|
||||||
{ nullptr }
|
|
||||||
};
|
|
||||||
|
|
||||||
static const mozilla::Module kNetworkWorkerModule = {
|
|
||||||
mozilla::Module::kVersion,
|
|
||||||
kNetworkWorkerCIDs,
|
|
||||||
kNetworkWorkerContracts,
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
NSMODULE_DEFN(NetworkWorkerModule) = &kNetworkWorkerModule;
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
/* 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 NetworkWorker_h
|
|
||||||
#define NetworkWorker_h
|
|
||||||
|
|
||||||
#include "mozilla/dom/NetworkOptionsBinding.h"
|
|
||||||
#include "mozilla/ipc/Netd.h"
|
|
||||||
#include "nsINetworkWorker.h"
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsThread.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
class NetworkWorker final : public nsINetworkWorker
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSINETWORKWORKER
|
|
||||||
|
|
||||||
static already_AddRefed<NetworkWorker> FactoryCreate();
|
|
||||||
|
|
||||||
void DispatchNetworkResult(const mozilla::dom::NetworkResultOptions& aOptions);
|
|
||||||
|
|
||||||
private:
|
|
||||||
NetworkWorker();
|
|
||||||
~NetworkWorker();
|
|
||||||
|
|
||||||
static void NotifyResult(mozilla::dom::NetworkResultOptions& aResult);
|
|
||||||
|
|
||||||
nsCOMPtr<nsINetworkEventListener> mListener;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // NetworkWorker_h
|
|
||||||
|
|
@ -1,251 +0,0 @@
|
||||||
/* 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 "OpenFileFinder.h"
|
|
||||||
|
|
||||||
#include "mozilla/FileUtils.h"
|
|
||||||
#include "nsPrintfCString.h"
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#undef USE_DEBUG
|
|
||||||
#define USE_DEBUG 0
|
|
||||||
|
|
||||||
#undef LOG
|
|
||||||
#undef LOGW
|
|
||||||
#undef ERR
|
|
||||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OpenFileFinder", ## args)
|
|
||||||
#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "OpenFileFinder", ## args)
|
|
||||||
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "OpenFileFinder", ## args)
|
|
||||||
|
|
||||||
#undef DBG
|
|
||||||
#if USE_DEBUG
|
|
||||||
#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "OpenFileFinder" , ## args)
|
|
||||||
#else
|
|
||||||
#define DBG(args...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
OpenFileFinder::OpenFileFinder(const nsACString& aPath,
|
|
||||||
bool aCheckIsB2gOrDescendant /* = true */)
|
|
||||||
: mPath(aPath),
|
|
||||||
mProcDir(nullptr),
|
|
||||||
mFdDir(nullptr),
|
|
||||||
mPid(0),
|
|
||||||
mCheckIsB2gOrDescendant(aCheckIsB2gOrDescendant)
|
|
||||||
{
|
|
||||||
// We assume that we're running in the parent process
|
|
||||||
mMyPid = getpid();
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenFileFinder::~OpenFileFinder()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
OpenFileFinder::First(OpenFileFinder::Info* aInfo)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
|
|
||||||
mProcDir = opendir("/proc");
|
|
||||||
if (!mProcDir) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mState = NEXT_PID;
|
|
||||||
return Next(aInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
OpenFileFinder::Next(OpenFileFinder::Info* aInfo)
|
|
||||||
{
|
|
||||||
// NOTE: This function calls readdir and readlink, neither of which should
|
|
||||||
// block since we're using the proc filesystem, which is a purely
|
|
||||||
// kernel in-memory filesystem and doesn't depend on external driver
|
|
||||||
// behaviour.
|
|
||||||
while (mState != DONE) {
|
|
||||||
switch (mState) {
|
|
||||||
case NEXT_PID: {
|
|
||||||
struct dirent *pidEntry;
|
|
||||||
pidEntry = readdir(mProcDir);
|
|
||||||
if (!pidEntry) {
|
|
||||||
mState = DONE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
char *endPtr;
|
|
||||||
mPid = strtol(pidEntry->d_name, &endPtr, 10);
|
|
||||||
if (mPid == 0 || *endPtr != '\0') {
|
|
||||||
// Not a +ve number - ignore
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// We've found a /proc/PID directory. Scan open file descriptors.
|
|
||||||
if (mFdDir) {
|
|
||||||
closedir(mFdDir);
|
|
||||||
}
|
|
||||||
nsPrintfCString fdDirPath("/proc/%d/fd", mPid);
|
|
||||||
mFdDir = opendir(fdDirPath.get());
|
|
||||||
if (!mFdDir) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mState = CHECK_FDS;
|
|
||||||
}
|
|
||||||
// Fall through
|
|
||||||
case CHECK_FDS: {
|
|
||||||
struct dirent *fdEntry;
|
|
||||||
while((fdEntry = readdir(mFdDir))) {
|
|
||||||
if (!strcmp(fdEntry->d_name, ".") ||
|
|
||||||
!strcmp(fdEntry->d_name, "..")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nsPrintfCString fdSymLink("/proc/%d/fd/%s", mPid, fdEntry->d_name);
|
|
||||||
nsCString resolvedPath;
|
|
||||||
if (ReadSymLink(fdSymLink, resolvedPath) && PathMatches(resolvedPath)) {
|
|
||||||
// We found an open file contained within the directory tree passed
|
|
||||||
// into the constructor.
|
|
||||||
FillInfo(aInfo, resolvedPath);
|
|
||||||
// If sCheckIsB2gOrDescendant is set false, the caller cares about
|
|
||||||
// all processes which have open files. If sCheckIsB2gOrDescendant
|
|
||||||
// is set false, we only care about the b2g proccess or its descendants.
|
|
||||||
if (!mCheckIsB2gOrDescendant || aInfo->mIsB2gOrDescendant) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
LOG("Ignore process(%d), not a b2g process or its descendant.",
|
|
||||||
aInfo->mPid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We've checked all of the files for this pid, move onto the next one.
|
|
||||||
mState = NEXT_PID;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
case DONE:
|
|
||||||
default:
|
|
||||||
mState = DONE; // covers the default case
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
OpenFileFinder::Close()
|
|
||||||
{
|
|
||||||
if (mFdDir) {
|
|
||||||
closedir(mFdDir);
|
|
||||||
}
|
|
||||||
if (mProcDir) {
|
|
||||||
closedir(mProcDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
OpenFileFinder::FillInfo(OpenFileFinder::Info* aInfo, const nsACString& aPath)
|
|
||||||
{
|
|
||||||
aInfo->mFileName = aPath;
|
|
||||||
aInfo->mPid = mPid;
|
|
||||||
nsPrintfCString exePath("/proc/%d/exe", mPid);
|
|
||||||
ReadSymLink(exePath, aInfo->mExe);
|
|
||||||
aInfo->mComm.Truncate();
|
|
||||||
aInfo->mAppName.Truncate();
|
|
||||||
nsPrintfCString statPath("/proc/%d/stat", mPid);
|
|
||||||
nsCString statString;
|
|
||||||
statString.SetLength(200);
|
|
||||||
char *stat = statString.BeginWriting();
|
|
||||||
if (!stat) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ReadSysFile(statPath.get(), stat, statString.Length());
|
|
||||||
// The stat line includes the comm field, surrounded by parenthesis.
|
|
||||||
// However, the contents of the comm field itself is arbitrary and
|
|
||||||
// and can include ')', so we search for the rightmost ) as being
|
|
||||||
// the end of the comm field.
|
|
||||||
char *closeParen = strrchr(stat, ')');
|
|
||||||
if (!closeParen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
char *openParen = strchr(stat, '(');
|
|
||||||
if (!openParen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (openParen >= closeParen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nsDependentCSubstring comm(&openParen[1], closeParen - openParen - 1);
|
|
||||||
aInfo->mComm = comm;
|
|
||||||
// There is a single character field after the comm and then
|
|
||||||
// the parent pid (the field we're interested in).
|
|
||||||
// ) X ppid
|
|
||||||
// 01234
|
|
||||||
int ppid = atoi(&closeParen[4]);
|
|
||||||
|
|
||||||
if (mPid == mMyPid) {
|
|
||||||
// This is chrome process
|
|
||||||
aInfo->mIsB2gOrDescendant = true;
|
|
||||||
DBG("Chrome process has open file(s)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// For the rest (non-chrome process), we recursively check the ppid to know
|
|
||||||
// it is a descendant of b2g or not. See bug 931456.
|
|
||||||
while (ppid != mMyPid && ppid != 1) {
|
|
||||||
DBG("Process(%d) is not forked from b2g(%d) or Init(1), keep looking",
|
|
||||||
ppid, mMyPid);
|
|
||||||
nsPrintfCString ppStatPath("/proc/%d/stat", ppid);
|
|
||||||
ReadSysFile(ppStatPath.get(), stat, statString.Length());
|
|
||||||
closeParen = strrchr(stat, ')');
|
|
||||||
if (!closeParen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ppid = atoi(&closeParen[4]);
|
|
||||||
}
|
|
||||||
if (ppid == 1) {
|
|
||||||
// This is a not a b2g process.
|
|
||||||
DBG("Non-b2g process has open file(s)");
|
|
||||||
aInfo->mIsB2gOrDescendant = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ppid == mMyPid) {
|
|
||||||
// This is a descendant of b2g.
|
|
||||||
DBG("Child process of chrome process has open file(s)");
|
|
||||||
aInfo->mIsB2gOrDescendant = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This looks like a content process. The comm field will be the
|
|
||||||
// app name.
|
|
||||||
aInfo->mAppName = aInfo->mComm;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
OpenFileFinder::ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath)
|
|
||||||
{
|
|
||||||
aOutPath.Truncate();
|
|
||||||
const char *symLink = aSymLink.BeginReading();
|
|
||||||
|
|
||||||
// Verify that we actually have a symlink.
|
|
||||||
struct stat st;
|
|
||||||
if (lstat(symLink, &st)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((st.st_mode & S_IFMT) != S_IFLNK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contrary to the documentation st.st_size doesn't seem to be a reliable
|
|
||||||
// indication of the length when reading from /proc, so we use a fixed
|
|
||||||
// size buffer instead.
|
|
||||||
|
|
||||||
char resolvedSymLink[PATH_MAX];
|
|
||||||
ssize_t pathLength = readlink(symLink, resolvedSymLink,
|
|
||||||
sizeof(resolvedSymLink) - 1);
|
|
||||||
if (pathLength <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
resolvedSymLink[pathLength] = '\0';
|
|
||||||
aOutPath.Assign(resolvedSymLink);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/* 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_system_openfilefinder_h__
|
|
||||||
#define mozilla_system_openfilefinder_h__
|
|
||||||
|
|
||||||
#include "nsString.h"
|
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
class OpenFileFinder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum State
|
|
||||||
{
|
|
||||||
NEXT_PID,
|
|
||||||
CHECK_FDS,
|
|
||||||
DONE
|
|
||||||
};
|
|
||||||
class Info
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
nsCString mFileName; // name of the the open file
|
|
||||||
nsCString mAppName; // App which has the file open (if it's a b2g app)
|
|
||||||
pid_t mPid; // pid of the process which has the file open
|
|
||||||
nsCString mComm; // comm associated with pid
|
|
||||||
nsCString mExe; // executable name associated with pid
|
|
||||||
bool mIsB2gOrDescendant; // this is b2g/its descendant or not
|
|
||||||
};
|
|
||||||
|
|
||||||
OpenFileFinder(const nsACString& aPath, bool aCheckIsB2gOrDescendant = true);
|
|
||||||
~OpenFileFinder();
|
|
||||||
|
|
||||||
bool First(Info* aInfo); // Return the first open file
|
|
||||||
bool Next(Info* aInfo); // Return the next open file
|
|
||||||
void Close();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void FillInfo(Info *aInfo, const nsACString& aPath);
|
|
||||||
bool ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath);
|
|
||||||
bool PathMatches(const nsACString& aPath)
|
|
||||||
{
|
|
||||||
return Substring(aPath, 0, mPath.Length()).Equals(mPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
State mState; // Keeps track of what we're doing.
|
|
||||||
nsCString mPath; // Only report files contained within this directory tree
|
|
||||||
DIR* mProcDir; // Used for scanning /proc
|
|
||||||
DIR* mFdDir; // Used for scanning /proc/PID/fd
|
|
||||||
int mPid; // PID currently being processed
|
|
||||||
pid_t mMyPid; // PID of parent process, we assume we're running on it.
|
|
||||||
bool mCheckIsB2gOrDescendant; // Do we care about non-b2g process?
|
|
||||||
};
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_nsvolume_h__
|
|
||||||
|
|
@ -1,338 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "RIL", function () {
|
|
||||||
let obj = {};
|
|
||||||
Cu.import("resource://gre/modules/ril_consts.js", obj);
|
|
||||||
return obj;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RILSystemMessenger
|
|
||||||
*/
|
|
||||||
this.RILSystemMessenger = function() {};
|
|
||||||
RILSystemMessenger.prototype = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook of Broadcast function
|
|
||||||
*
|
|
||||||
* @param aType
|
|
||||||
* The type of the message to be sent.
|
|
||||||
* @param aMessage
|
|
||||||
* The message object to be broadcasted.
|
|
||||||
*/
|
|
||||||
broadcastMessage: function(aType, aMessage) {
|
|
||||||
// Function stub to be replaced by the owner of this messenger.
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook of the function to create MozStkCommand message.
|
|
||||||
* @param aStkProactiveCmd
|
|
||||||
* nsIStkProactiveCmd instance.
|
|
||||||
*
|
|
||||||
* @return a JS object which complies the dictionary of MozStkCommand defined
|
|
||||||
* in MozStkCommandEvent.webidl
|
|
||||||
*/
|
|
||||||
createCommandMessage: function(aStkProactiveCmd) {
|
|
||||||
// Function stub to be replaced by the owner of this messenger.
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send "telephony-new-call" system message.
|
|
||||||
*/
|
|
||||||
notifyNewCall: function() {
|
|
||||||
this.broadcastMessage("telephony-new-call", {});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send "telephony-call-ended" system message.
|
|
||||||
*/
|
|
||||||
notifyCallEnded: function(aServiceId, aNumber, aCdmaWaitingNumber, aEmergency,
|
|
||||||
aDuration, aOutgoing, aHangUpLocal) {
|
|
||||||
let data = {
|
|
||||||
serviceId: aServiceId,
|
|
||||||
number: aNumber,
|
|
||||||
emergency: aEmergency,
|
|
||||||
duration: aDuration,
|
|
||||||
direction: aOutgoing ? "outgoing" : "incoming",
|
|
||||||
hangUpLocal: aHangUpLocal
|
|
||||||
};
|
|
||||||
|
|
||||||
if (aCdmaWaitingNumber != null) {
|
|
||||||
data.secondNumber = aCdmaWaitingNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.broadcastMessage("telephony-call-ended", data);
|
|
||||||
},
|
|
||||||
|
|
||||||
_convertSmsMessageClass: function(aMessageClass) {
|
|
||||||
return RIL.GECKO_SMS_MESSAGE_CLASSES[aMessageClass] || null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_convertSmsDelivery: function(aDelivery) {
|
|
||||||
return ["received", "sending", "sent", "error"][aDelivery] || null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_convertSmsDeliveryStatus: function(aDeliveryStatus) {
|
|
||||||
return [
|
|
||||||
RIL.GECKO_SMS_DELIVERY_STATUS_NOT_APPLICABLE,
|
|
||||||
RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS,
|
|
||||||
RIL.GECKO_SMS_DELIVERY_STATUS_PENDING,
|
|
||||||
RIL.GECKO_SMS_DELIVERY_STATUS_ERROR
|
|
||||||
][aDeliveryStatus] || null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'sms-received', 'sms-delivery-success', 'sms-sent',
|
|
||||||
* 'sms-failed', 'sms-delivery-error' system message.
|
|
||||||
*/
|
|
||||||
notifySms: function(aNotificationType, aId, aThreadId, aIccId, aDelivery,
|
|
||||||
aDeliveryStatus, aSender, aReceiver, aBody, aMessageClass,
|
|
||||||
aTimestamp, aSentTimestamp, aDeliveryTimestamp, aRead) {
|
|
||||||
let msgType = [
|
|
||||||
"sms-received",
|
|
||||||
"sms-sent",
|
|
||||||
"sms-delivery-success",
|
|
||||||
"sms-failed",
|
|
||||||
"sms-delivery-error"
|
|
||||||
][aNotificationType];
|
|
||||||
|
|
||||||
if (!msgType) {
|
|
||||||
throw new Error("Invalid Notification Type: " + aNotificationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.broadcastMessage(msgType, {
|
|
||||||
iccId: aIccId,
|
|
||||||
type: "sms",
|
|
||||||
id: aId,
|
|
||||||
threadId: aThreadId,
|
|
||||||
delivery: this._convertSmsDelivery(aDelivery),
|
|
||||||
deliveryStatus: this._convertSmsDeliveryStatus(aDeliveryStatus),
|
|
||||||
sender: aSender,
|
|
||||||
receiver: aReceiver,
|
|
||||||
body: aBody,
|
|
||||||
messageClass: this._convertSmsMessageClass(aMessageClass),
|
|
||||||
timestamp: aTimestamp,
|
|
||||||
sentTimestamp: aSentTimestamp,
|
|
||||||
deliveryTimestamp: aDeliveryTimestamp,
|
|
||||||
read: aRead
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_convertCbGsmGeographicalScope: function(aGeographicalScope) {
|
|
||||||
return RIL.CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[aGeographicalScope] || null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_convertCbMessageClass: function(aMessageClass) {
|
|
||||||
return RIL.GECKO_SMS_MESSAGE_CLASSES[aMessageClass] || null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_convertCbEtwsWarningType: function(aWarningType) {
|
|
||||||
return RIL.CB_ETWS_WARNING_TYPE_NAMES[aWarningType] || null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cellbroadcast-received' system message.
|
|
||||||
*/
|
|
||||||
notifyCbMessageReceived: function(aServiceId, aGsmGeographicalScope, aMessageCode,
|
|
||||||
aMessageId, aLanguage, aBody, aMessageClass,
|
|
||||||
aTimestamp, aCdmaServiceCategory, aHasEtwsInfo,
|
|
||||||
aEtwsWarningType, aEtwsEmergencyUserAlert, aEtwsPopup) {
|
|
||||||
// Align the same layout to MozCellBroadcastMessage
|
|
||||||
let data = {
|
|
||||||
serviceId: aServiceId,
|
|
||||||
gsmGeographicalScope: this._convertCbGsmGeographicalScope(aGsmGeographicalScope),
|
|
||||||
messageCode: aMessageCode,
|
|
||||||
messageId: aMessageId,
|
|
||||||
language: aLanguage,
|
|
||||||
body: aBody,
|
|
||||||
messageClass: this._convertCbMessageClass(aMessageClass),
|
|
||||||
timestamp: aTimestamp,
|
|
||||||
cdmaServiceCategory: null,
|
|
||||||
etws: null
|
|
||||||
};
|
|
||||||
|
|
||||||
if (aHasEtwsInfo) {
|
|
||||||
data.etws = {
|
|
||||||
warningType: this._convertCbEtwsWarningType(aEtwsWarningType),
|
|
||||||
emergencyUserAlert: aEtwsEmergencyUserAlert,
|
|
||||||
popup: aEtwsPopup
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aCdmaServiceCategory !=
|
|
||||||
Ci.nsICellBroadcastService.CDMA_SERVICE_CATEGORY_INVALID) {
|
|
||||||
data.cdmaServiceCategory = aCdmaServiceCategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.broadcastMessage("cellbroadcast-received", data);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'ussd-received' system message.
|
|
||||||
*/
|
|
||||||
notifyUssdReceived: function(aServiceId, aMessage, aSessionEnded) {
|
|
||||||
this.broadcastMessage("ussd-received", {
|
|
||||||
serviceId: aServiceId,
|
|
||||||
message: aMessage,
|
|
||||||
sessionEnded: aSessionEnded
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with Display Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecDisplay: function(aServiceId, aDisplay) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
display: aDisplay
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with Called Party
|
|
||||||
* Number Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecCalledPartyNumber: function(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
calledNumber: {
|
|
||||||
type: aType,
|
|
||||||
plan: aPlan,
|
|
||||||
number: aNumber,
|
|
||||||
pi: aPi,
|
|
||||||
si: aSi
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with Calling Party
|
|
||||||
* Number Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecCallingPartyNumber: function(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
callingNumber: {
|
|
||||||
type: aType,
|
|
||||||
plan: aPlan,
|
|
||||||
number: aNumber,
|
|
||||||
pi: aPi,
|
|
||||||
si: aSi
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with Connected Party
|
|
||||||
* Number Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecConnectedPartyNumber: function(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
connectedNumber: {
|
|
||||||
type: aType,
|
|
||||||
plan: aPlan,
|
|
||||||
number: aNumber,
|
|
||||||
pi: aPi,
|
|
||||||
si: aSi
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with Signal Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecSignal: function(aServiceId, aType, aAlertPitch, aSignal) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
signal: {
|
|
||||||
type: aType,
|
|
||||||
alertPitch: aAlertPitch,
|
|
||||||
signal: aSignal
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with Redirecting
|
|
||||||
* Number Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecRedirectingNumber: function(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi, aReason) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
redirect: {
|
|
||||||
type: aType,
|
|
||||||
plan: aPlan,
|
|
||||||
number: aNumber,
|
|
||||||
pi: aPi,
|
|
||||||
si: aSi,
|
|
||||||
reason: aReason
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with Line Control Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecLineControl: function(aServiceId, aPolarityIncluded,
|
|
||||||
aToggle, aReverse, aPowerDenial) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
lineControl: {
|
|
||||||
polarityIncluded: aPolarityIncluded,
|
|
||||||
toggle: aToggle,
|
|
||||||
reverse: aReverse,
|
|
||||||
powerDenial: aPowerDenial
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with CLIR Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecClir: function(aServiceId, aCause) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
clirCause: aCause
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'cdma-info-rec-received' system message with Audio Control Info.
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecAudioControl: function(aServiceId, aUpLink, aDownLink) {
|
|
||||||
this.broadcastMessage("cdma-info-rec-received", {
|
|
||||||
clientId: aServiceId,
|
|
||||||
audioControl: {
|
|
||||||
upLink: aUpLink,
|
|
||||||
downLink: aDownLink
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to send 'icc-stkcommand' system message with Audio Control Info.
|
|
||||||
*/
|
|
||||||
notifyStkProactiveCommand: function(aIccId, aCommand) {
|
|
||||||
this.broadcastMessage("icc-stkcommand", {
|
|
||||||
iccId: aIccId,
|
|
||||||
command: this.createCommandMessage(aCommand)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = [
|
|
||||||
'RILSystemMessenger'
|
|
||||||
];
|
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
|
|
||||||
var RSM = {};
|
|
||||||
Cu.import("resource://gre/modules/RILSystemMessenger.jsm", RSM);
|
|
||||||
|
|
||||||
const RILSYSTEMMESSENGERHELPER_CONTRACTID =
|
|
||||||
"@mozilla.org/ril/system-messenger-helper;1";
|
|
||||||
const RILSYSTEMMESSENGERHELPER_CID =
|
|
||||||
Components.ID("{19d9a4ea-580d-11e4-8f6c-37ababfaaea9}");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
|
|
||||||
"@mozilla.org/system-message-internal;1",
|
|
||||||
"nsISystemMessagesInternal");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gStkCmdFactory",
|
|
||||||
"@mozilla.org/icc/stkcmdfactory;1",
|
|
||||||
"nsIStkCmdFactory");
|
|
||||||
|
|
||||||
var DEBUG = false;
|
|
||||||
function debug(s) {
|
|
||||||
dump("-@- RILSystemMessenger: " + s + "\n");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Read debug setting from pref.
|
|
||||||
try {
|
|
||||||
let debugPref = Services.prefs.getBoolPref("ril.debugging.enabled");
|
|
||||||
DEBUG = DEBUG || debugPref;
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RILSystemMessengerHelper
|
|
||||||
*/
|
|
||||||
function RILSystemMessengerHelper() {
|
|
||||||
this.messenger = new RSM.RILSystemMessenger();
|
|
||||||
this.messenger.broadcastMessage = (aType, aMessage) => {
|
|
||||||
if (DEBUG) {
|
|
||||||
debug("broadcastMessage: aType: " + aType +
|
|
||||||
", aMessage: "+ JSON.stringify(aMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
gSystemMessenger.broadcastMessage(aType, aMessage);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.messenger.createCommandMessage = (aStkProactiveCmd) => {
|
|
||||||
return gStkCmdFactory.createCommandMessage(aStkProactiveCmd);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
RILSystemMessengerHelper.prototype = {
|
|
||||||
|
|
||||||
classID: RILSYSTEMMESSENGERHELPER_CID,
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyMessenger,
|
|
||||||
Ci.nsISmsMessenger,
|
|
||||||
Ci.nsICellbroadcastMessenger,
|
|
||||||
Ci.nsIMobileConnectionMessenger,
|
|
||||||
Ci.nsIIccMessenger]),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RILSystemMessenger instance.
|
|
||||||
*/
|
|
||||||
messenger: null,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsITelephonyMessenger API
|
|
||||||
*/
|
|
||||||
notifyNewCall: function() {
|
|
||||||
this.messenger.notifyNewCall();
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCallEnded: function(aServiceId, aNumber, aCdmaWaitingNumber, aEmergency,
|
|
||||||
aDuration, aOutgoing, aHangUpLocal) {
|
|
||||||
this.messenger.notifyCallEnded(aServiceId, aNumber, aCdmaWaitingNumber, aEmergency,
|
|
||||||
aDuration, aOutgoing, aHangUpLocal);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyUssdReceived: function(aServiceId, aMessage, aSessionEnded) {
|
|
||||||
this.messenger.notifyUssdReceived(aServiceId, aMessage, aSessionEnded);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsISmsMessenger API
|
|
||||||
*/
|
|
||||||
notifySms: function(aNotificationType, aId, aThreadId, aIccId, aDelivery,
|
|
||||||
aDeliveryStatus, aSender, aReceiver, aBody, aMessageClass,
|
|
||||||
aTimestamp, aSentTimestamp, aDeliveryTimestamp, aRead) {
|
|
||||||
this.messenger.notifySms(aNotificationType, aId, aThreadId, aIccId, aDelivery,
|
|
||||||
aDeliveryStatus, aSender, aReceiver, aBody, aMessageClass,
|
|
||||||
aTimestamp, aSentTimestamp, aDeliveryTimestamp, aRead);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsICellbroadcastMessenger API
|
|
||||||
*/
|
|
||||||
notifyCbMessageReceived: function(aServiceId, aGsmGeographicalScope, aMessageCode,
|
|
||||||
aMessageId, aLanguage, aBody, aMessageClass,
|
|
||||||
aTimestamp, aCdmaServiceCategory, aHasEtwsInfo,
|
|
||||||
aEtwsWarningType, aEtwsEmergencyUserAlert, aEtwsPopup) {
|
|
||||||
this.messenger.notifyCbMessageReceived(aServiceId, aGsmGeographicalScope, aMessageCode,
|
|
||||||
aMessageId, aLanguage, aBody, aMessageClass,
|
|
||||||
aTimestamp, aCdmaServiceCategory, aHasEtwsInfo,
|
|
||||||
aEtwsWarningType, aEtwsEmergencyUserAlert, aEtwsPopup);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsIMobileConnectionMessenger API
|
|
||||||
*/
|
|
||||||
notifyCdmaInfoRecDisplay: function(aServiceId, aDisplay) {
|
|
||||||
this.messenger.notifyCdmaInfoRecDisplay(aServiceId, aDisplay);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCdmaInfoRecCalledPartyNumber: function(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi) {
|
|
||||||
this.messenger.notifyCdmaInfoRecCalledPartyNumber(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCdmaInfoRecCallingPartyNumber: function(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi) {
|
|
||||||
this.messenger.notifyCdmaInfoRecCallingPartyNumber(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCdmaInfoRecConnectedPartyNumber: function(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi) {
|
|
||||||
this.messenger.notifyCdmaInfoRecConnectedPartyNumber(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCdmaInfoRecSignal: function(aServiceId, aType, aAlertPitch, aSignal) {
|
|
||||||
this.messenger.notifyCdmaInfoRecSignal(aServiceId, aType, aAlertPitch, aSignal);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCdmaInfoRecRedirectingNumber: function(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi, aReason) {
|
|
||||||
this.messenger.notifyCdmaInfoRecRedirectingNumber(aServiceId, aType, aPlan,
|
|
||||||
aNumber, aPi, aSi, aReason);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCdmaInfoRecLineControl: function(aServiceId, aPolarityIncluded,
|
|
||||||
aToggle, aReverse, aPowerDenial) {
|
|
||||||
this.messenger.notifyCdmaInfoRecLineControl(aServiceId, aPolarityIncluded,
|
|
||||||
aToggle, aReverse, aPowerDenial);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCdmaInfoRecClir: function(aServiceId, aCause) {
|
|
||||||
this.messenger.notifyCdmaInfoRecClir(aServiceId, aCause);
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCdmaInfoRecAudioControl: function(aServiceId, aUpLink, aDownLink) {
|
|
||||||
this.messenger.notifyCdmaInfoRecAudioControl(aServiceId, aUpLink, aDownLink);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsIIccMessenger API
|
|
||||||
*/
|
|
||||||
notifyStkProactiveCommand: function(aIccId, aCommand) {
|
|
||||||
this.messenger.notifyStkProactiveCommand(aIccId, aCommand);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RILSystemMessengerHelper]);
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# 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/.
|
|
||||||
|
|
||||||
component {19d9a4ea-580d-11e4-8f6c-37ababfaaea9} RILSystemMessengerHelper.js
|
|
||||||
contract @mozilla.org/ril/system-messenger-helper;1 {19d9a4ea-580d-11e4-8f6c-37ababfaaea9}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,18 +0,0 @@
|
||||||
# Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
# RadioInterfaceLayer.js
|
|
||||||
component {2d831c8d-6017-435b-a80c-e5d422810cea} RadioInterfaceLayer.js
|
|
||||||
contract @mozilla.org/ril;1 {2d831c8d-6017-435b-a80c-e5d422810cea}
|
|
||||||
category profile-after-change RadioInterfaceLayer @mozilla.org/ril;1
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
/* -*- 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 "SystemProperty.h"
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "nsDebug.h"
|
|
||||||
#include "prinit.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
typedef int (*PropertyGet)(const char*, char*, const char*);
|
|
||||||
typedef int (*PropertySet)(const char*, const char*);
|
|
||||||
|
|
||||||
static void *sLibcUtils;
|
|
||||||
static PRCallOnceType sInitLibcUtils;
|
|
||||||
|
|
||||||
static int
|
|
||||||
FakePropertyGet(const char* key, char* value, const char* default_value)
|
|
||||||
{
|
|
||||||
if(!default_value) {
|
|
||||||
value[0] = '\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int len = strlen(default_value);
|
|
||||||
if (len >= Property::VALUE_MAX_LENGTH) {
|
|
||||||
len = Property::VALUE_MAX_LENGTH - 1;
|
|
||||||
}
|
|
||||||
memcpy(value, default_value, len);
|
|
||||||
value[len] = '\0';
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
FakePropertySet(const char* key, const char* value)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PRStatus
|
|
||||||
InitLibcUtils()
|
|
||||||
{
|
|
||||||
sLibcUtils = dlopen("/system/lib/libcutils.so", RTLD_LAZY);
|
|
||||||
// We will fallback to the fake getter/setter when sLibcUtils is not valid.
|
|
||||||
return PR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void*
|
|
||||||
GetLibcUtils()
|
|
||||||
{
|
|
||||||
PR_CallOnce(&sInitLibcUtils, InitLibcUtils);
|
|
||||||
return sLibcUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
/*static*/ int
|
|
||||||
Property::Get(const char* key, char* value, const char* default_value)
|
|
||||||
{
|
|
||||||
void *libcutils = GetLibcUtils();
|
|
||||||
if (libcutils) {
|
|
||||||
PropertyGet getter = (PropertyGet) dlsym(libcutils, "property_get");
|
|
||||||
if (getter) {
|
|
||||||
return getter(key, value, default_value);
|
|
||||||
}
|
|
||||||
NS_WARNING("Failed to get property_get() from libcutils!");
|
|
||||||
}
|
|
||||||
NS_WARNING("Fallback to the FakePropertyGet()");
|
|
||||||
return FakePropertyGet(key, value, default_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static*/ int
|
|
||||||
Property::Set(const char* key, const char* value)
|
|
||||||
{
|
|
||||||
void *libcutils = GetLibcUtils();
|
|
||||||
if (libcutils) {
|
|
||||||
PropertySet setter = (PropertySet) dlsym(libcutils, "property_set");
|
|
||||||
if (setter) {
|
|
||||||
return setter(key, value);
|
|
||||||
}
|
|
||||||
NS_WARNING("Failed to get property_set() from libcutils!");
|
|
||||||
}
|
|
||||||
NS_WARNING("Fallback to the FakePropertySet()");
|
|
||||||
return FakePropertySet(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
/* -*- 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/. */
|
|
||||||
|
|
||||||
#ifndef mozilla_system_Property_h
|
|
||||||
#define mozilla_system_Property_h
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstraction of property_get/property_get in libcutils from AOSP.
|
|
||||||
*/
|
|
||||||
class Property
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Constants defined in system_properties.h from AOSP.
|
|
||||||
enum {
|
|
||||||
KEY_MAX_LENGTH = 32,
|
|
||||||
VALUE_MAX_LENGTH = 92
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
|
||||||
Get(const char* key, char* value, const char* default_value);
|
|
||||||
|
|
||||||
static int
|
|
||||||
Set(const char* key, const char* value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Property() {}
|
|
||||||
virtual ~Property() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_Property_h
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
|
||||||
/* vim: set ts=2 et sw=2 tw=80: */
|
|
||||||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "SystemWorkerManager.h"
|
|
||||||
|
|
||||||
#include "nsINetworkService.h"
|
|
||||||
#include "nsIWifi.h"
|
|
||||||
#include "nsIWorkerHolder.h"
|
|
||||||
#include "nsIXPConnect.h"
|
|
||||||
|
|
||||||
#include "jsfriendapi.h"
|
|
||||||
#include "mozilla/dom/workers/Workers.h"
|
|
||||||
#include "AutoMounter.h"
|
|
||||||
#include "TimeZoneSettingObserver.h"
|
|
||||||
#include "AudioManager.h"
|
|
||||||
#include "mozilla/dom/ScriptSettings.h"
|
|
||||||
#include "mozilla/ipc/KeyStore.h"
|
|
||||||
#include "nsIObserverService.h"
|
|
||||||
#include "nsServiceManagerUtils.h"
|
|
||||||
#include "nsThreadUtils.h"
|
|
||||||
#include "WifiWorker.h"
|
|
||||||
#include "mozilla/Services.h"
|
|
||||||
|
|
||||||
USING_WORKERS_NAMESPACE
|
|
||||||
|
|
||||||
using namespace mozilla::dom::gonk;
|
|
||||||
using namespace mozilla::ipc;
|
|
||||||
using namespace mozilla::system;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
NS_DEFINE_CID(kWifiWorkerCID, NS_WIFIWORKER_CID);
|
|
||||||
|
|
||||||
// Doesn't carry a reference, we're owned by services.
|
|
||||||
SystemWorkerManager *gInstance = nullptr;
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
SystemWorkerManager::SystemWorkerManager()
|
|
||||||
: mShutdown(false)
|
|
||||||
{
|
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
||||||
NS_ASSERTION(!gInstance, "There should only be one instance!");
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemWorkerManager::~SystemWorkerManager()
|
|
||||||
{
|
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
||||||
NS_ASSERTION(!gInstance || gInstance == this,
|
|
||||||
"There should only be one instance!");
|
|
||||||
gInstance = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
SystemWorkerManager::Init()
|
|
||||||
{
|
|
||||||
if (!XRE_IsParentProcess()) {
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_ASSERTION(NS_IsMainThread(), "We can only initialize on the main thread");
|
|
||||||
NS_ASSERTION(!mShutdown, "Already shutdown!");
|
|
||||||
|
|
||||||
nsresult rv = InitWifi();
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("Failed to initialize WiFi Networking!");
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitKeyStore();
|
|
||||||
|
|
||||||
InitAutoMounter();
|
|
||||||
InitializeTimeZoneSettingObserver();
|
|
||||||
nsCOMPtr<nsIAudioManager> audioManager =
|
|
||||||
do_GetService(NS_AUDIOMANAGER_CONTRACTID);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
||||||
if (!obs) {
|
|
||||||
NS_WARNING("Failed to get observer service!");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = obs->AddObserver(this, WORKERS_SHUTDOWN_TOPIC, false);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("Failed to initialize worker shutdown event!");
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SystemWorkerManager::Shutdown()
|
|
||||||
{
|
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
||||||
|
|
||||||
mShutdown = true;
|
|
||||||
|
|
||||||
ShutdownAutoMounter();
|
|
||||||
|
|
||||||
nsCOMPtr<nsIWifi> wifi(do_QueryInterface(mWifiWorker));
|
|
||||||
if (wifi) {
|
|
||||||
wifi->Shutdown();
|
|
||||||
wifi = nullptr;
|
|
||||||
}
|
|
||||||
mWifiWorker = nullptr;
|
|
||||||
|
|
||||||
if (mKeyStore) {
|
|
||||||
mKeyStore->Shutdown();
|
|
||||||
mKeyStore = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
||||||
if (obs) {
|
|
||||||
obs->RemoveObserver(this, WORKERS_SHUTDOWN_TOPIC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
already_AddRefed<SystemWorkerManager>
|
|
||||||
SystemWorkerManager::FactoryCreate()
|
|
||||||
{
|
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
||||||
|
|
||||||
RefPtr<SystemWorkerManager> instance(gInstance);
|
|
||||||
|
|
||||||
if (!instance) {
|
|
||||||
instance = new SystemWorkerManager();
|
|
||||||
if (NS_FAILED(instance->Init())) {
|
|
||||||
instance->Shutdown();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
gInstance = instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
nsIInterfaceRequestor*
|
|
||||||
SystemWorkerManager::GetInterfaceRequestor()
|
|
||||||
{
|
|
||||||
return gInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
SystemWorkerManager::GetInterface(const nsIID &aIID, void **aResult)
|
|
||||||
{
|
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
||||||
|
|
||||||
if (aIID.Equals(NS_GET_IID(nsIWifi))) {
|
|
||||||
return CallQueryInterface(mWifiWorker,
|
|
||||||
reinterpret_cast<nsIWifi**>(aResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_WARNING("Got nothing for the requested IID!");
|
|
||||||
return NS_ERROR_NO_INTERFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
SystemWorkerManager::RegisterRilWorker(unsigned int aClientId,
|
|
||||||
JS::Handle<JS::Value> aWorker,
|
|
||||||
JSContext *aCx)
|
|
||||||
{
|
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
SystemWorkerManager::InitWifi()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIWorkerHolder> worker = do_CreateInstance(kWifiWorkerCID);
|
|
||||||
NS_ENSURE_TRUE(worker, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
mWifiWorker = worker;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
SystemWorkerManager::InitKeyStore()
|
|
||||||
{
|
|
||||||
mKeyStore = new KeyStore();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(SystemWorkerManager,
|
|
||||||
nsIObserver,
|
|
||||||
nsIInterfaceRequestor,
|
|
||||||
nsISystemWorkerManager)
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
SystemWorkerManager::Observe(nsISupports *aSubject, const char *aTopic,
|
|
||||||
const char16_t *aData)
|
|
||||||
{
|
|
||||||
if (!strcmp(aTopic, WORKERS_SHUTDOWN_TOPIC)) {
|
|
||||||
Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
|
||||||
/* vim: set ts=2 et sw=2 tw=40: */
|
|
||||||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef mozilla_dom_system_b2g_systemworkermanager_h__
|
|
||||||
#define mozilla_dom_system_b2g_systemworkermanager_h__
|
|
||||||
|
|
||||||
#include "nsIInterfaceRequestor.h"
|
|
||||||
#include "nsISystemWorkerManager.h"
|
|
||||||
#include "nsIObserver.h"
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsXULAppAPI.h" // For XRE_GetProcessType
|
|
||||||
|
|
||||||
class nsIWorkerHolder;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
class KeyStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace dom {
|
|
||||||
namespace gonk {
|
|
||||||
|
|
||||||
class SystemWorkerManager final : public nsIObserver,
|
|
||||||
public nsIInterfaceRequestor,
|
|
||||||
public nsISystemWorkerManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
NS_DECL_NSIINTERFACEREQUESTOR
|
|
||||||
NS_DECL_NSISYSTEMWORKERMANAGER
|
|
||||||
|
|
||||||
nsresult Init();
|
|
||||||
void Shutdown();
|
|
||||||
|
|
||||||
static already_AddRefed<SystemWorkerManager>
|
|
||||||
FactoryCreate();
|
|
||||||
|
|
||||||
static nsIInterfaceRequestor*
|
|
||||||
GetInterfaceRequestor();
|
|
||||||
|
|
||||||
private:
|
|
||||||
SystemWorkerManager();
|
|
||||||
~SystemWorkerManager();
|
|
||||||
|
|
||||||
nsresult InitWifi();
|
|
||||||
nsresult InitKeyStore();
|
|
||||||
|
|
||||||
nsCOMPtr<nsIWorkerHolder> mWifiWorker;
|
|
||||||
|
|
||||||
RefPtr<mozilla::ipc::KeyStore> mKeyStore;
|
|
||||||
|
|
||||||
bool mShutdown;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // mozilla_dom_system_b2g_systemworkermanager_h__
|
|
||||||
|
|
@ -1,891 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/systemlibs.js");
|
|
||||||
|
|
||||||
const TETHERINGSERVICE_CONTRACTID = "@mozilla.org/tethering/service;1";
|
|
||||||
const TETHERINGSERVICE_CID =
|
|
||||||
Components.ID("{527a4121-ee5a-4651-be9c-f46f59cf7c01}");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
|
|
||||||
"@mozilla.org/network/manager;1",
|
|
||||||
"nsINetworkManager");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
|
|
||||||
"@mozilla.org/network/service;1",
|
|
||||||
"nsINetworkService");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
|
|
||||||
"@mozilla.org/settingsService;1",
|
|
||||||
"nsISettingsService");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
|
|
||||||
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
|
|
||||||
"nsIMobileConnectionService");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "gRil", function() {
|
|
||||||
try {
|
|
||||||
return Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const TOPIC_MOZSETTINGS_CHANGED = "mozsettings-changed";
|
|
||||||
const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
|
|
||||||
const TOPIC_PREF_CHANGED = "nsPref:changed";
|
|
||||||
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
|
|
||||||
const PREF_MANAGE_OFFLINE_STATUS = "network.gonk.manage-offline-status";
|
|
||||||
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
|
|
||||||
|
|
||||||
const POSSIBLE_USB_INTERFACE_NAME = "rndis0,usb0";
|
|
||||||
const DEFAULT_USB_INTERFACE_NAME = "rndis0";
|
|
||||||
const DEFAULT_3G_INTERFACE_NAME = "rmnet0";
|
|
||||||
const DEFAULT_WIFI_INTERFACE_NAME = "wlan0";
|
|
||||||
|
|
||||||
// The kernel's proc entry for network lists.
|
|
||||||
const KERNEL_NETWORK_ENTRY = "/sys/class/net";
|
|
||||||
|
|
||||||
const TETHERING_TYPE_WIFI = "WiFi";
|
|
||||||
const TETHERING_TYPE_USB = "USB";
|
|
||||||
|
|
||||||
const WIFI_FIRMWARE_AP = "AP";
|
|
||||||
const WIFI_FIRMWARE_STATION = "STA";
|
|
||||||
const WIFI_SECURITY_TYPE_NONE = "open";
|
|
||||||
const WIFI_SECURITY_TYPE_WPA_PSK = "wpa-psk";
|
|
||||||
const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk";
|
|
||||||
const WIFI_CTRL_INTERFACE = "wl0.1";
|
|
||||||
|
|
||||||
const NETWORK_INTERFACE_UP = "up";
|
|
||||||
const NETWORK_INTERFACE_DOWN = "down";
|
|
||||||
|
|
||||||
const TETHERING_STATE_ONGOING = "ongoing";
|
|
||||||
const TETHERING_STATE_IDLE = "idle";
|
|
||||||
const TETHERING_STATE_ACTIVE = "active";
|
|
||||||
|
|
||||||
// Settings DB path for USB tethering.
|
|
||||||
const SETTINGS_USB_ENABLED = "tethering.usb.enabled";
|
|
||||||
const SETTINGS_USB_IP = "tethering.usb.ip";
|
|
||||||
const SETTINGS_USB_PREFIX = "tethering.usb.prefix";
|
|
||||||
const SETTINGS_USB_DHCPSERVER_STARTIP = "tethering.usb.dhcpserver.startip";
|
|
||||||
const SETTINGS_USB_DHCPSERVER_ENDIP = "tethering.usb.dhcpserver.endip";
|
|
||||||
const SETTINGS_USB_DNS1 = "tethering.usb.dns1";
|
|
||||||
const SETTINGS_USB_DNS2 = "tethering.usb.dns2";
|
|
||||||
|
|
||||||
// Settings DB path for WIFI tethering.
|
|
||||||
const SETTINGS_WIFI_DHCPSERVER_STARTIP = "tethering.wifi.dhcpserver.startip";
|
|
||||||
const SETTINGS_WIFI_DHCPSERVER_ENDIP = "tethering.wifi.dhcpserver.endip";
|
|
||||||
|
|
||||||
// Settings DB patch for dun required setting.
|
|
||||||
const SETTINGS_DUN_REQUIRED = "tethering.dun.required";
|
|
||||||
|
|
||||||
// Default value for USB tethering.
|
|
||||||
const DEFAULT_USB_IP = "192.168.0.1";
|
|
||||||
const DEFAULT_USB_PREFIX = "24";
|
|
||||||
const DEFAULT_USB_DHCPSERVER_STARTIP = "192.168.0.10";
|
|
||||||
const DEFAULT_USB_DHCPSERVER_ENDIP = "192.168.0.30";
|
|
||||||
|
|
||||||
const DEFAULT_DNS1 = "8.8.8.8";
|
|
||||||
const DEFAULT_DNS2 = "8.8.4.4";
|
|
||||||
|
|
||||||
const DEFAULT_WIFI_DHCPSERVER_STARTIP = "192.168.1.10";
|
|
||||||
const DEFAULT_WIFI_DHCPSERVER_ENDIP = "192.168.1.30";
|
|
||||||
|
|
||||||
const SETTINGS_DATA_DEFAULT_SERVICE_ID = "ril.data.defaultServiceId";
|
|
||||||
const MOBILE_DUN_CONNECT_TIMEOUT = 30000;
|
|
||||||
const MOBILE_DUN_RETRY_INTERVAL = 5000;
|
|
||||||
const MOBILE_DUN_MAX_RETRIES = 5;
|
|
||||||
|
|
||||||
var debug;
|
|
||||||
function updateDebug() {
|
|
||||||
let debugPref = false; // set default value here.
|
|
||||||
try {
|
|
||||||
debugPref = debugPref || Services.prefs.getBoolPref(PREF_NETWORK_DEBUG_ENABLED);
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
if (debugPref) {
|
|
||||||
debug = function(s) {
|
|
||||||
dump("-*- TetheringService: " + s + "\n");
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
debug = function(s) {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateDebug();
|
|
||||||
|
|
||||||
function TetheringService() {
|
|
||||||
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
|
|
||||||
Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
|
|
||||||
Services.obs.addObserver(this, TOPIC_CONNECTION_STATE_CHANGED, false);
|
|
||||||
Services.prefs.addObserver(PREF_NETWORK_DEBUG_ENABLED, this, false);
|
|
||||||
Services.prefs.addObserver(PREF_MANAGE_OFFLINE_STATUS, this, false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
this._manageOfflineStatus =
|
|
||||||
Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
|
|
||||||
} catch(ex) {
|
|
||||||
// Ignore.
|
|
||||||
}
|
|
||||||
|
|
||||||
this._dataDefaultServiceId = 0;
|
|
||||||
|
|
||||||
// Possible usb tethering interfaces for different gonk platform.
|
|
||||||
this.possibleInterface = POSSIBLE_USB_INTERFACE_NAME.split(",");
|
|
||||||
|
|
||||||
// Default values for internal and external interfaces.
|
|
||||||
this._tetheringInterface = {};
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB] = {
|
|
||||||
externalInterface: DEFAULT_3G_INTERFACE_NAME,
|
|
||||||
internalInterface: DEFAULT_USB_INTERFACE_NAME
|
|
||||||
};
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_WIFI] = {
|
|
||||||
externalInterface: DEFAULT_3G_INTERFACE_NAME,
|
|
||||||
internalInterface: DEFAULT_WIFI_INTERFACE_NAME
|
|
||||||
};
|
|
||||||
|
|
||||||
this.tetheringSettings = {};
|
|
||||||
this.initTetheringSettings();
|
|
||||||
|
|
||||||
let settingsLock = gSettingsService.createLock();
|
|
||||||
// Read the default service id for data call.
|
|
||||||
settingsLock.get(SETTINGS_DATA_DEFAULT_SERVICE_ID, this);
|
|
||||||
|
|
||||||
// Read usb tethering data from settings DB.
|
|
||||||
settingsLock.get(SETTINGS_USB_IP, this);
|
|
||||||
settingsLock.get(SETTINGS_USB_PREFIX, this);
|
|
||||||
settingsLock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this);
|
|
||||||
settingsLock.get(SETTINGS_USB_DHCPSERVER_ENDIP, this);
|
|
||||||
settingsLock.get(SETTINGS_USB_DNS1, this);
|
|
||||||
settingsLock.get(SETTINGS_USB_DNS2, this);
|
|
||||||
settingsLock.get(SETTINGS_USB_ENABLED, this);
|
|
||||||
|
|
||||||
// Read wifi tethering data from settings DB.
|
|
||||||
settingsLock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this);
|
|
||||||
settingsLock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this);
|
|
||||||
|
|
||||||
this._usbTetheringSettingsToRead = [SETTINGS_USB_IP,
|
|
||||||
SETTINGS_USB_PREFIX,
|
|
||||||
SETTINGS_USB_DHCPSERVER_STARTIP,
|
|
||||||
SETTINGS_USB_DHCPSERVER_ENDIP,
|
|
||||||
SETTINGS_USB_DNS1,
|
|
||||||
SETTINGS_USB_DNS2,
|
|
||||||
SETTINGS_USB_ENABLED,
|
|
||||||
SETTINGS_WIFI_DHCPSERVER_STARTIP,
|
|
||||||
SETTINGS_WIFI_DHCPSERVER_ENDIP];
|
|
||||||
|
|
||||||
this.wantConnectionEvent = null;
|
|
||||||
|
|
||||||
this.dunConnectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
||||||
|
|
||||||
this.dunRetryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
||||||
|
|
||||||
this._pendingTetheringRequests = [];
|
|
||||||
}
|
|
||||||
TetheringService.prototype = {
|
|
||||||
classID: TETHERINGSERVICE_CID,
|
|
||||||
classInfo: XPCOMUtils.generateCI({classID: TETHERINGSERVICE_CID,
|
|
||||||
contractID: TETHERINGSERVICE_CONTRACTID,
|
|
||||||
classDescription: "Tethering Service",
|
|
||||||
interfaces: [Ci.nsITetheringService]}),
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITetheringService,
|
|
||||||
Ci.nsISupportsWeakReference,
|
|
||||||
Ci.nsIObserver,
|
|
||||||
Ci.nsISettingsServiceCallback]),
|
|
||||||
|
|
||||||
// Flag to record the default client id for data call.
|
|
||||||
_dataDefaultServiceId: null,
|
|
||||||
|
|
||||||
// Number of usb tehering requests to be processed.
|
|
||||||
_usbTetheringRequestCount: 0,
|
|
||||||
|
|
||||||
// Usb tethering state.
|
|
||||||
_usbTetheringAction: TETHERING_STATE_IDLE,
|
|
||||||
|
|
||||||
// Tethering settings.
|
|
||||||
tetheringSettings: null,
|
|
||||||
|
|
||||||
// Tethering settings need to be read from settings DB.
|
|
||||||
_usbTetheringSettingsToRead: null,
|
|
||||||
|
|
||||||
// Previous usb tethering enabled state.
|
|
||||||
_oldUsbTetheringEnabledState: null,
|
|
||||||
|
|
||||||
// External and internal interface name.
|
|
||||||
_tetheringInterface: null,
|
|
||||||
|
|
||||||
// Dun connection timer.
|
|
||||||
dunConnectTimer: null,
|
|
||||||
|
|
||||||
// Dun connection retry times.
|
|
||||||
dunRetryTimes: 0,
|
|
||||||
|
|
||||||
// Dun retry timer.
|
|
||||||
dunRetryTimer: null,
|
|
||||||
|
|
||||||
// Pending tethering request to handle after dun is connected.
|
|
||||||
_pendingTetheringRequests: null,
|
|
||||||
|
|
||||||
// Flag to indicate wether wifi tethering is being processed.
|
|
||||||
_wifiTetheringRequestOngoing: false,
|
|
||||||
|
|
||||||
// Arguments for pending wifi tethering request.
|
|
||||||
_pendingWifiTetheringRequestArgs: null,
|
|
||||||
|
|
||||||
// The state of tethering.
|
|
||||||
state: Ci.nsITetheringService.TETHERING_STATE_INACTIVE,
|
|
||||||
|
|
||||||
// Flag to check if we can modify the Services.io.offline.
|
|
||||||
_manageOfflineStatus: true,
|
|
||||||
|
|
||||||
// nsIObserver
|
|
||||||
|
|
||||||
observe: function(aSubject, aTopic, aData) {
|
|
||||||
let network;
|
|
||||||
|
|
||||||
switch(aTopic) {
|
|
||||||
case TOPIC_PREF_CHANGED:
|
|
||||||
if (aData === PREF_NETWORK_DEBUG_ENABLED) {
|
|
||||||
updateDebug();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TOPIC_MOZSETTINGS_CHANGED:
|
|
||||||
if ("wrappedJSObject" in aSubject) {
|
|
||||||
aSubject = aSubject.wrappedJSObject;
|
|
||||||
}
|
|
||||||
this.handle(aSubject.key, aSubject.value);
|
|
||||||
break;
|
|
||||||
case TOPIC_CONNECTION_STATE_CHANGED:
|
|
||||||
network = aSubject.QueryInterface(Ci.nsINetworkInfo);
|
|
||||||
debug("Network " + network.type + "/" + network.name +
|
|
||||||
" changed state to " + network.state);
|
|
||||||
this.onConnectionChanged(network);
|
|
||||||
break;
|
|
||||||
case TOPIC_XPCOM_SHUTDOWN:
|
|
||||||
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
|
|
||||||
Services.obs.removeObserver(this, TOPIC_MOZSETTINGS_CHANGED);
|
|
||||||
Services.obs.removeObserver(this, TOPIC_CONNECTION_STATE_CHANGED);
|
|
||||||
Services.prefs.removeObserver(PREF_NETWORK_DEBUG_ENABLED, this);
|
|
||||||
Services.prefs.removeObserver(PREF_MANAGE_OFFLINE_STATUS, this);
|
|
||||||
|
|
||||||
this.dunConnectTimer.cancel();
|
|
||||||
this.dunRetryTimer.cancel();
|
|
||||||
break;
|
|
||||||
case PREF_MANAGE_OFFLINE_STATUS:
|
|
||||||
try {
|
|
||||||
this._manageOfflineStatus =
|
|
||||||
Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
|
|
||||||
} catch(ex) {
|
|
||||||
// Ignore.
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsISettingsServiceCallback
|
|
||||||
|
|
||||||
handle: function(aName, aResult) {
|
|
||||||
switch(aName) {
|
|
||||||
case SETTINGS_DATA_DEFAULT_SERVICE_ID:
|
|
||||||
this._dataDefaultServiceId = aResult || 0;
|
|
||||||
debug("'_dataDefaultServiceId' is now " + this._dataDefaultServiceId);
|
|
||||||
break;
|
|
||||||
case SETTINGS_USB_ENABLED:
|
|
||||||
this._oldUsbTetheringEnabledState = this.tetheringSettings[SETTINGS_USB_ENABLED];
|
|
||||||
case SETTINGS_USB_IP:
|
|
||||||
case SETTINGS_USB_PREFIX:
|
|
||||||
case SETTINGS_USB_DHCPSERVER_STARTIP:
|
|
||||||
case SETTINGS_USB_DHCPSERVER_ENDIP:
|
|
||||||
case SETTINGS_USB_DNS1:
|
|
||||||
case SETTINGS_USB_DNS2:
|
|
||||||
case SETTINGS_WIFI_DHCPSERVER_STARTIP:
|
|
||||||
case SETTINGS_WIFI_DHCPSERVER_ENDIP:
|
|
||||||
if (aResult !== null) {
|
|
||||||
this.tetheringSettings[aName] = aResult;
|
|
||||||
}
|
|
||||||
debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]);
|
|
||||||
let index = this._usbTetheringSettingsToRead.indexOf(aName);
|
|
||||||
|
|
||||||
if (index != -1) {
|
|
||||||
this._usbTetheringSettingsToRead.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._usbTetheringSettingsToRead.length) {
|
|
||||||
debug("We haven't read completely the usb Tethering data from settings db.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._oldUsbTetheringEnabledState === this.tetheringSettings[SETTINGS_USB_ENABLED]) {
|
|
||||||
debug("No changes for SETTINGS_USB_ENABLED flag. Nothing to do.");
|
|
||||||
this.handlePendingWifiTetheringRequest();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._usbTetheringRequestCount++;
|
|
||||||
if (this._usbTetheringRequestCount === 1) {
|
|
||||||
if (this._wifiTetheringRequestOngoing) {
|
|
||||||
debug('USB tethering request is blocked by ongoing wifi tethering request.');
|
|
||||||
} else {
|
|
||||||
this.handleLastUsbTetheringRequest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
handleError: function(aErrorMessage) {
|
|
||||||
debug("There was an error while reading Tethering settings.");
|
|
||||||
this.tetheringSettings = {};
|
|
||||||
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
initTetheringSettings: function() {
|
|
||||||
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
|
|
||||||
this.tetheringSettings[SETTINGS_USB_IP] = DEFAULT_USB_IP;
|
|
||||||
this.tetheringSettings[SETTINGS_USB_PREFIX] = DEFAULT_USB_PREFIX;
|
|
||||||
this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP] = DEFAULT_USB_DHCPSERVER_STARTIP;
|
|
||||||
this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP] = DEFAULT_USB_DHCPSERVER_ENDIP;
|
|
||||||
this.tetheringSettings[SETTINGS_USB_DNS1] = DEFAULT_DNS1;
|
|
||||||
this.tetheringSettings[SETTINGS_USB_DNS2] = DEFAULT_DNS2;
|
|
||||||
|
|
||||||
this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP;
|
|
||||||
this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP;
|
|
||||||
|
|
||||||
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
|
|
||||||
libcutils.property_get("ro.tethering.dun_required") === "1";
|
|
||||||
},
|
|
||||||
|
|
||||||
getNetworkInfo: function(aType, aServiceId) {
|
|
||||||
for (let networkId in gNetworkManager.allNetworkInfo) {
|
|
||||||
let networkInfo = gNetworkManager.allNetworkInfo[networkId];
|
|
||||||
if (networkInfo.type == aType) {
|
|
||||||
try {
|
|
||||||
if (networkInfo instanceof Ci.nsIRilNetworkInfo) {
|
|
||||||
let rilNetwork = networkInfo.QueryInterface(Ci.nsIRilNetworkInfo);
|
|
||||||
if (rilNetwork.serviceId != aServiceId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
return networkInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleLastUsbTetheringRequest: function() {
|
|
||||||
debug('handleLastUsbTetheringRequest... ' + this._usbTetheringRequestCount);
|
|
||||||
|
|
||||||
if (this._usbTetheringRequestCount === 0) {
|
|
||||||
if (this.wantConnectionEvent) {
|
|
||||||
if (this.tetheringSettings[SETTINGS_USB_ENABLED]) {
|
|
||||||
this.wantConnectionEvent.call(this);
|
|
||||||
}
|
|
||||||
this.wantConnectionEvent = null;
|
|
||||||
}
|
|
||||||
this.handlePendingWifiTetheringRequest();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancel the accumlated count to 1 since we only care about the
|
|
||||||
// last state.
|
|
||||||
this._usbTetheringRequestCount = 1;
|
|
||||||
this.handleUSBTetheringToggle(this.tetheringSettings[SETTINGS_USB_ENABLED]);
|
|
||||||
this.wantConnectionEvent = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePendingWifiTetheringRequest: function() {
|
|
||||||
if (this._pendingWifiTetheringRequestArgs) {
|
|
||||||
this.setWifiTethering.apply(this, this._pendingWifiTetheringRequestArgs);
|
|
||||||
this._pendingWifiTetheringRequestArgs = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback when dun connection fails to connect within timeout.
|
|
||||||
*/
|
|
||||||
onDunConnectTimerTimeout: function() {
|
|
||||||
while (this._pendingTetheringRequests.length > 0) {
|
|
||||||
debug("onDunConnectTimerTimeout: callback without network info.");
|
|
||||||
let callback = this._pendingTetheringRequests.shift();
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setupDunConnection: function() {
|
|
||||||
this.dunRetryTimer.cancel();
|
|
||||||
let connection =
|
|
||||||
gMobileConnectionService.getItemByServiceId(this._dataDefaultServiceId);
|
|
||||||
let data = connection && connection.data;
|
|
||||||
if (data && data.state === "registered") {
|
|
||||||
let ril = gRil.getRadioInterface(this._dataDefaultServiceId);
|
|
||||||
|
|
||||||
this.dunRetryTimes = 0;
|
|
||||||
ril.setupDataCallByType(Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN);
|
|
||||||
this.dunConnectTimer.cancel();
|
|
||||||
this.dunConnectTimer.
|
|
||||||
initWithCallback(this.onDunConnectTimerTimeout.bind(this),
|
|
||||||
MOBILE_DUN_CONNECT_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.dunRetryTimes++ >= this.MOBILE_DUN_MAX_RETRIES) {
|
|
||||||
debug("setupDunConnection: max retries reached.");
|
|
||||||
this.dunRetryTimes = 0;
|
|
||||||
// same as dun connect timeout.
|
|
||||||
this.onDunConnectTimerTimeout();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("Data not ready, retry dun after " + MOBILE_DUN_RETRY_INTERVAL + " ms.");
|
|
||||||
this.dunRetryTimer.
|
|
||||||
initWithCallback(this.setupDunConnection.bind(this),
|
|
||||||
MOBILE_DUN_RETRY_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
||||||
},
|
|
||||||
|
|
||||||
_dunActiveUsers: 0,
|
|
||||||
handleDunConnection: function(aEnable, aCallback) {
|
|
||||||
debug("handleDunConnection: " + aEnable);
|
|
||||||
let dun = this.getNetworkInfo(
|
|
||||||
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN, this._dataDefaultServiceId);
|
|
||||||
|
|
||||||
if (!aEnable) {
|
|
||||||
this._dunActiveUsers--;
|
|
||||||
if (this._dunActiveUsers > 0) {
|
|
||||||
debug("Dun still needed by others, do not disconnect.")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dunRetryTimes = 0;
|
|
||||||
this.dunRetryTimer.cancel();
|
|
||||||
this.dunConnectTimer.cancel();
|
|
||||||
this._pendingTetheringRequests = [];
|
|
||||||
|
|
||||||
if (dun && (dun.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED)) {
|
|
||||||
gRil.getRadioInterface(this._dataDefaultServiceId)
|
|
||||||
.deactivateDataCallByType(Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._dunActiveUsers++;
|
|
||||||
if (!dun || (dun.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED)) {
|
|
||||||
debug("DUN data call inactive, setup dun data call!")
|
|
||||||
this._pendingTetheringRequests.push(aCallback);
|
|
||||||
this.dunRetryTimes = 0;
|
|
||||||
this.setupDunConnection();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = dun.name;
|
|
||||||
aCallback(dun);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleUSBTetheringToggle: function(aEnable) {
|
|
||||||
debug("handleUSBTetheringToggle: " + aEnable);
|
|
||||||
if (aEnable &&
|
|
||||||
(this._usbTetheringAction === TETHERING_STATE_ONGOING ||
|
|
||||||
this._usbTetheringAction === TETHERING_STATE_ACTIVE)) {
|
|
||||||
debug("Usb tethering already connecting/connected.");
|
|
||||||
this._usbTetheringRequestCount = 0;
|
|
||||||
this.handlePendingWifiTetheringRequest();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aEnable &&
|
|
||||||
this._usbTetheringAction === TETHERING_STATE_IDLE) {
|
|
||||||
debug("Usb tethering already disconnected.");
|
|
||||||
this._usbTetheringRequestCount = 0;
|
|
||||||
this.handlePendingWifiTetheringRequest();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aEnable) {
|
|
||||||
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
|
|
||||||
gNetworkService.enableUsbRndis(false, this.enableUsbRndisResult.bind(this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.tetheringSettings[SETTINGS_USB_ENABLED] = true;
|
|
||||||
this._usbTetheringAction = TETHERING_STATE_ONGOING;
|
|
||||||
|
|
||||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
|
|
||||||
this.handleDunConnection(true, (aNetworkInfo) => {
|
|
||||||
if (!aNetworkInfo){
|
|
||||||
this.usbTetheringResultReport(aEnable, "Dun connection failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
|
|
||||||
aNetworkInfo.name;
|
|
||||||
gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gNetworkManager.activeNetworkInfo) {
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
|
|
||||||
gNetworkManager.activeNetworkInfo.name;
|
|
||||||
} else {
|
|
||||||
let mobile = this.getNetworkInfo(
|
|
||||||
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, this._dataDefaultServiceId);
|
|
||||||
if (mobile && mobile.name) {
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = mobile.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
getUSBTetheringParameters: function(aEnable, aTetheringInterface) {
|
|
||||||
let interfaceIp = this.tetheringSettings[SETTINGS_USB_IP];
|
|
||||||
let prefix = this.tetheringSettings[SETTINGS_USB_PREFIX];
|
|
||||||
let wifiDhcpStartIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP];
|
|
||||||
let wifiDhcpEndIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP];
|
|
||||||
let usbDhcpStartIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP];
|
|
||||||
let usbDhcpEndIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP];
|
|
||||||
let dns1 = this.tetheringSettings[SETTINGS_USB_DNS1];
|
|
||||||
let dns2 = this.tetheringSettings[SETTINGS_USB_DNS2];
|
|
||||||
let internalInterface = aTetheringInterface.internalInterface;
|
|
||||||
let externalInterface = aTetheringInterface.externalInterface;
|
|
||||||
|
|
||||||
// Using the default values here until application support these settings.
|
|
||||||
if (interfaceIp == "" || prefix == "" ||
|
|
||||||
wifiDhcpStartIp == "" || wifiDhcpEndIp == "" ||
|
|
||||||
usbDhcpStartIp == "" || usbDhcpEndIp == "") {
|
|
||||||
debug("Invalid subnet information.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
ifname: internalInterface,
|
|
||||||
ip: interfaceIp,
|
|
||||||
prefix: prefix,
|
|
||||||
wifiStartIp: wifiDhcpStartIp,
|
|
||||||
wifiEndIp: wifiDhcpEndIp,
|
|
||||||
usbStartIp: usbDhcpStartIp,
|
|
||||||
usbEndIp: usbDhcpEndIp,
|
|
||||||
dns1: dns1,
|
|
||||||
dns2: dns2,
|
|
||||||
internalIfname: internalInterface,
|
|
||||||
externalIfname: externalInterface,
|
|
||||||
enable: aEnable,
|
|
||||||
link: aEnable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: function(aResetSettings, aCallback, aMsg) {
|
|
||||||
if (aResetSettings) {
|
|
||||||
let settingsLock = gSettingsService.createLock();
|
|
||||||
// Disable wifi tethering with a useful error message for the user.
|
|
||||||
settingsLock.set("tethering.wifi.enabled", false, null, aMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("setWifiTethering: " + (aMsg ? aMsg : "success"));
|
|
||||||
|
|
||||||
if (aCallback) {
|
|
||||||
// Callback asynchronously to avoid netsted toggling.
|
|
||||||
Services.tm.currentThread.dispatch(() => {
|
|
||||||
aCallback.wifiTetheringEnabledChange(aMsg);
|
|
||||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
enableWifiTethering: function(aEnable, aConfig, aCallback) {
|
|
||||||
// Fill in config's required fields.
|
|
||||||
aConfig.ifname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
|
|
||||||
aConfig.internalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
|
|
||||||
aConfig.externalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface;
|
|
||||||
|
|
||||||
this._wifiTetheringRequestOngoing = true;
|
|
||||||
gNetworkService.setWifiTethering(aEnable, aConfig, (aError) => {
|
|
||||||
// Change the tethering state to WIFI if there is no error.
|
|
||||||
if (aEnable && !aError) {
|
|
||||||
this.state = Ci.nsITetheringService.TETHERING_STATE_WIFI;
|
|
||||||
} else {
|
|
||||||
// If wifi thethering is disable, or any error happens,
|
|
||||||
// then consider the following statements.
|
|
||||||
|
|
||||||
// Check whether the state is USB now or not. If no then just change
|
|
||||||
// it to INACTIVE, if yes then just keep it.
|
|
||||||
// It means that don't let the disable or error of WIFI affect
|
|
||||||
// the original active state.
|
|
||||||
if (this.state != Ci.nsITetheringService.TETHERING_STATE_USB) {
|
|
||||||
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect dun on error or when wifi tethering is disabled.
|
|
||||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
|
|
||||||
this.handleDunConnection(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._manageOfflineStatus) {
|
|
||||||
Services.io.offline = !this.isAnyConnected() &&
|
|
||||||
(this.state ===
|
|
||||||
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
let resetSettings = aError;
|
|
||||||
debug('gNetworkService.setWifiTethering finished');
|
|
||||||
this.notifyError(resetSettings, aCallback, aError);
|
|
||||||
this._wifiTetheringRequestOngoing = false;
|
|
||||||
if (this._usbTetheringRequestCount > 0) {
|
|
||||||
debug('Perform pending USB tethering requests.');
|
|
||||||
this.handleLastUsbTetheringRequest();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Enable/disable WiFi tethering by sending commands to netd.
|
|
||||||
setWifiTethering: function(aEnable, aInterfaceName, aConfig, aCallback) {
|
|
||||||
debug("setWifiTethering: " + aEnable);
|
|
||||||
if (!aInterfaceName) {
|
|
||||||
this.notifyError(true, aCallback, "invalid network interface name");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aConfig) {
|
|
||||||
this.notifyError(true, aCallback, "invalid configuration");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._usbTetheringRequestCount > 0) {
|
|
||||||
// If there's still pending usb tethering request, save
|
|
||||||
// the request params and redo |setWifiTethering| on
|
|
||||||
// usb tethering task complete.
|
|
||||||
debug('USB tethering request is being processed. Queue this wifi tethering request.');
|
|
||||||
this._pendingWifiTetheringRequestArgs = Array.prototype.slice.call(arguments);
|
|
||||||
debug('Pending args: ' + JSON.stringify(this._pendingWifiTetheringRequestArgs));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-check again, test cases set this property later.
|
|
||||||
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
|
|
||||||
libcutils.property_get("ro.tethering.dun_required") === "1";
|
|
||||||
|
|
||||||
if (!aEnable) {
|
|
||||||
this.enableWifiTethering(false, aConfig, aCallback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface =
|
|
||||||
aInterfaceName;
|
|
||||||
|
|
||||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
|
|
||||||
this.handleDunConnection(true, (aNetworkInfo) => {
|
|
||||||
if (!aNetworkInfo) {
|
|
||||||
this.notifyError(true, aCallback, "Dun connection failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface =
|
|
||||||
aNetworkInfo.name;
|
|
||||||
this.enableWifiTethering(true, aConfig, aCallback);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mobile = this.getNetworkInfo(
|
|
||||||
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, this._dataDefaultServiceId);
|
|
||||||
// Update the real interface name
|
|
||||||
if (mobile && mobile.name) {
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = mobile.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.enableWifiTethering(true, aConfig, aCallback);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Enable/disable USB tethering by sending commands to netd.
|
|
||||||
setUSBTethering: function(aEnable, aTetheringInterface, aCallback) {
|
|
||||||
let params = this.getUSBTetheringParameters(aEnable, aTetheringInterface);
|
|
||||||
|
|
||||||
if (params === null) {
|
|
||||||
gNetworkService.enableUsbRndis(false, function() {
|
|
||||||
this.usbTetheringResultReport(aEnable, "Invalid parameters");
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gNetworkService.setUSBTethering(aEnable, params, aCallback);
|
|
||||||
},
|
|
||||||
|
|
||||||
getUsbInterface: function() {
|
|
||||||
// Find the rndis interface.
|
|
||||||
for (let i = 0; i < this.possibleInterface.length; i++) {
|
|
||||||
try {
|
|
||||||
let file = new FileUtils.File(KERNEL_NETWORK_ENTRY + "/" +
|
|
||||||
this.possibleInterface[i]);
|
|
||||||
if (file.exists()) {
|
|
||||||
return this.possibleInterface[i];
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debug("Not " + this.possibleInterface[i] + " interface.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug("Can't find rndis interface in possible lists.");
|
|
||||||
return DEFAULT_USB_INTERFACE_NAME;
|
|
||||||
},
|
|
||||||
|
|
||||||
enableUsbRndisResult: function(aSuccess, aEnable) {
|
|
||||||
if (aSuccess) {
|
|
||||||
// If enable is false, don't find usb interface cause it is already down,
|
|
||||||
// just use the internal interface in settings.
|
|
||||||
if (aEnable) {
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB].internalInterface =
|
|
||||||
this.getUsbInterface();
|
|
||||||
}
|
|
||||||
this.setUSBTethering(aEnable,
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB],
|
|
||||||
this.usbTetheringResultReport.bind(this, aEnable));
|
|
||||||
} else {
|
|
||||||
this.usbTetheringResultReport(aEnable, "enableUsbRndisResult failure");
|
|
||||||
throw new Error("failed to set USB Function to adb");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
usbTetheringResultReport: function(aEnable, aError) {
|
|
||||||
this._usbTetheringRequestCount--;
|
|
||||||
|
|
||||||
let settingsLock = gSettingsService.createLock();
|
|
||||||
|
|
||||||
debug('usbTetheringResultReport callback. enable: ' + aEnable +
|
|
||||||
', error: ' + aError);
|
|
||||||
|
|
||||||
// Disable tethering settings when fail to enable it.
|
|
||||||
if (aError) {
|
|
||||||
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
|
|
||||||
settingsLock.set("tethering.usb.enabled", false, null);
|
|
||||||
// Skip others request when we found an error.
|
|
||||||
this._usbTetheringRequestCount = 0;
|
|
||||||
this._usbTetheringAction = TETHERING_STATE_IDLE;
|
|
||||||
// If the thethering state is WIFI now, then just keep it,
|
|
||||||
// if not, just change the state to INACTIVE.
|
|
||||||
// It means that don't let the error of USB affect the original active state.
|
|
||||||
if (this.state != Ci.nsITetheringService.TETHERING_STATE_WIFI) {
|
|
||||||
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
|
|
||||||
}
|
|
||||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
|
|
||||||
this.handleDunConnection(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (aEnable) {
|
|
||||||
this._usbTetheringAction = TETHERING_STATE_ACTIVE;
|
|
||||||
this.state = Ci.nsITetheringService.TETHERING_STATE_USB;
|
|
||||||
} else {
|
|
||||||
this._usbTetheringAction = TETHERING_STATE_IDLE;
|
|
||||||
// If the state is now WIFI, don't let the disable of USB affect it.
|
|
||||||
if (this.state != Ci.nsITetheringService.TETHERING_STATE_WIFI) {
|
|
||||||
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
|
|
||||||
}
|
|
||||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
|
|
||||||
this.handleDunConnection(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._manageOfflineStatus) {
|
|
||||||
Services.io.offline = !this.isAnyConnected() &&
|
|
||||||
(this.state ===
|
|
||||||
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleLastUsbTetheringRequest();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onConnectionChangedReport: function(aSuccess, aExternalIfname) {
|
|
||||||
debug("onConnectionChangedReport result: success " + aSuccess);
|
|
||||||
|
|
||||||
if (aSuccess) {
|
|
||||||
// Update the external interface.
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
|
|
||||||
aExternalIfname;
|
|
||||||
debug("Change the interface name to " + aExternalIfname);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onConnectionChanged: function(aNetworkInfo) {
|
|
||||||
if (aNetworkInfo.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
|
||||||
debug("We are only interested in CONNECTED event");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
|
|
||||||
aNetworkInfo.type === Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN) {
|
|
||||||
this.dunConnectTimer.cancel();
|
|
||||||
debug("DUN data call connected, process callbacks.");
|
|
||||||
while (this._pendingTetheringRequests.length > 0) {
|
|
||||||
let callback = this._pendingTetheringRequests.shift();
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback(aNetworkInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.tetheringSettings[SETTINGS_USB_ENABLED]) {
|
|
||||||
debug("Usb tethering settings is not enabled");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
|
|
||||||
aNetworkInfo.type === Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN &&
|
|
||||||
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
|
|
||||||
aNetworkInfo.name) {
|
|
||||||
debug("Dun required and dun interface is the same");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
|
|
||||||
gNetworkManager.activeNetworkInfo.name) {
|
|
||||||
debug("The active interface is the same");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let previous = {
|
|
||||||
internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface,
|
|
||||||
externalIfname: this._tetheringInterface[TETHERING_TYPE_USB].externalInterface
|
|
||||||
};
|
|
||||||
|
|
||||||
let current = {
|
|
||||||
internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface,
|
|
||||||
externalIfname: aNetworkInfo.name
|
|
||||||
};
|
|
||||||
|
|
||||||
let callback = (() => {
|
|
||||||
// Update external network interface.
|
|
||||||
debug("Update upstream interface to " + aNetworkInfo.name);
|
|
||||||
gNetworkService.updateUpStream(previous, current,
|
|
||||||
this.onConnectionChangedReport.bind(this));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this._usbTetheringAction === TETHERING_STATE_ONGOING) {
|
|
||||||
debug("Postpone the event and handle it when state is idle.");
|
|
||||||
this.wantConnectionEvent = callback;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.wantConnectionEvent = null;
|
|
||||||
|
|
||||||
callback.call(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
isAnyConnected: function() {
|
|
||||||
let allNetworkInfo = gNetworkManager.allNetworkInfo;
|
|
||||||
for (let networkId in allNetworkInfo) {
|
|
||||||
if (allNetworkInfo.hasOwnProperty(networkId) &&
|
|
||||||
allNetworkInfo[networkId].state === Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TetheringService]);
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
# TetheringService.js
|
|
||||||
component {527a4121-ee5a-4651-be9c-f46f59cf7c01} TetheringService.js
|
|
||||||
contract @mozilla.org/tethering/service;1 {527a4121-ee5a-4651-be9c-f46f59cf7c01}
|
|
||||||
category profile-after-change TetheringService @mozilla.org/tethering/service;1
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
/* 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/message_loop.h"
|
|
||||||
#include "jsapi.h"
|
|
||||||
#include "mozilla/dom/ScriptSettings.h"
|
|
||||||
#include "mozilla/Attributes.h"
|
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
|
||||||
#include "mozilla/Hal.h"
|
|
||||||
#include "mozilla/Services.h"
|
|
||||||
#include "mozilla/StaticPtr.h"
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsDebug.h"
|
|
||||||
#include "nsIObserver.h"
|
|
||||||
#include "nsIObserverService.h"
|
|
||||||
#include "nsISettingsService.h"
|
|
||||||
#include "nsJSUtils.h"
|
|
||||||
#include "nsServiceManagerUtils.h"
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "TimeZoneSettingObserver.h"
|
|
||||||
#include "xpcpublic.h"
|
|
||||||
#include "nsContentUtils.h"
|
|
||||||
#include "nsPrintfCString.h"
|
|
||||||
#include "mozilla/dom/BindingUtils.h"
|
|
||||||
#include "mozilla/dom/SettingChangeNotificationBinding.h"
|
|
||||||
|
|
||||||
#undef LOG
|
|
||||||
#undef ERR
|
|
||||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Time Zone Setting" , ## args)
|
|
||||||
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "Time Zone Setting" , ## args)
|
|
||||||
|
|
||||||
#define TIME_TIMEZONE "time.timezone"
|
|
||||||
#define MOZSETTINGS_CHANGED "mozsettings-changed"
|
|
||||||
|
|
||||||
using namespace mozilla;
|
|
||||||
using namespace mozilla::dom;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class TimeZoneSettingObserver : public nsIObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
|
|
||||||
TimeZoneSettingObserver();
|
|
||||||
static nsresult SetTimeZone(const JS::Value &aValue, JSContext *aContext);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~TimeZoneSettingObserver();
|
|
||||||
};
|
|
||||||
|
|
||||||
class TimeZoneSettingCb final : public nsISettingsServiceCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
|
|
||||||
TimeZoneSettingCb() {}
|
|
||||||
|
|
||||||
NS_IMETHOD Handle(const nsAString &aName, JS::Handle<JS::Value> aResult) {
|
|
||||||
|
|
||||||
JSContext *cx = nsContentUtils::GetCurrentJSContext();
|
|
||||||
NS_ENSURE_TRUE(cx, NS_OK);
|
|
||||||
|
|
||||||
// If we don't have time.timezone value in the settings, we need
|
|
||||||
// to initialize the settings based on the current system timezone
|
|
||||||
// to make settings consistent with system. This usually happens
|
|
||||||
// at the very first boot. After that, settings must have a value.
|
|
||||||
if (aResult.isNull()) {
|
|
||||||
// Get the current system time zone offset. Note that we need to
|
|
||||||
// convert the value to a UTC representation in the format of
|
|
||||||
// "UTC{+,-}hh:mm", so that the Gaia end can know how to interpret.
|
|
||||||
// E.g., -480 is "UTC+08:00"; 630 is "UTC-10:30".
|
|
||||||
int32_t timeZoneOffset = hal::GetTimezoneOffset();
|
|
||||||
nsPrintfCString curTimeZone("UTC%+03d:%02d",
|
|
||||||
-timeZoneOffset / 60,
|
|
||||||
abs(timeZoneOffset) % 60);
|
|
||||||
|
|
||||||
// Convert it to a JS string.
|
|
||||||
NS_ConvertUTF8toUTF16 utf16Str(curTimeZone);
|
|
||||||
|
|
||||||
JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx,
|
|
||||||
utf16Str.get(),
|
|
||||||
utf16Str.Length()));
|
|
||||||
if (!jsStr) {
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the settings based on the current system timezone.
|
|
||||||
nsCOMPtr<nsISettingsServiceLock> lock;
|
|
||||||
nsCOMPtr<nsISettingsService> settingsService =
|
|
||||||
do_GetService("@mozilla.org/settingsService;1");
|
|
||||||
if (!settingsService) {
|
|
||||||
ERR("Failed to get settingsLock service!");
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
|
|
||||||
JS::Rooted<JS::Value> value(cx, JS::StringValue(jsStr));
|
|
||||||
lock->Set(TIME_TIMEZONE, value, nullptr, nullptr);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the system timezone based on the current settings.
|
|
||||||
if (aResult.isString()) {
|
|
||||||
return TimeZoneSettingObserver::SetTimeZone(aResult, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD HandleError(const nsAString &aName) {
|
|
||||||
ERR("TimeZoneSettingCb::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
~TimeZoneSettingCb() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(TimeZoneSettingCb, nsISettingsServiceCallback)
|
|
||||||
|
|
||||||
TimeZoneSettingObserver::TimeZoneSettingObserver()
|
|
||||||
{
|
|
||||||
// Setup an observer to watch changes to the setting.
|
|
||||||
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
|
||||||
if (!observerService) {
|
|
||||||
ERR("GetObserverService failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nsresult rv;
|
|
||||||
rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
ERR("AddObserver failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the 'time.timezone' setting in order to start with a known
|
|
||||||
// value at boot time. The handle() will be called after reading.
|
|
||||||
nsCOMPtr<nsISettingsServiceLock> lock;
|
|
||||||
nsCOMPtr<nsISettingsService> settingsService =
|
|
||||||
do_GetService("@mozilla.org/settingsService;1");
|
|
||||||
if (!settingsService) {
|
|
||||||
ERR("Failed to get settingsLock service!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
settingsService->CreateLock(nullptr, getter_AddRefs(lock));
|
|
||||||
nsCOMPtr<nsISettingsServiceCallback> callback = new TimeZoneSettingCb();
|
|
||||||
lock->Get(TIME_TIMEZONE, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult TimeZoneSettingObserver::SetTimeZone(const JS::Value &aValue, JSContext *aContext)
|
|
||||||
{
|
|
||||||
// Convert the JS value to a nsCString type.
|
|
||||||
// The value should be a JS string like "America/Chicago" or "UTC-05:00".
|
|
||||||
nsAutoJSString valueStr;
|
|
||||||
if (!valueStr.init(aContext, aValue.toString())) {
|
|
||||||
ERR("Failed to convert JS value to nsCString");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
NS_ConvertUTF16toUTF8 newTimezone(valueStr);
|
|
||||||
|
|
||||||
// Hal expects opposite sign from general notations,
|
|
||||||
// so we need to flip it.
|
|
||||||
if (newTimezone.Find(NS_LITERAL_CSTRING("UTC+")) == 0) {
|
|
||||||
if (!newTimezone.SetCharAt('-', 3)) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
} else if (newTimezone.Find(NS_LITERAL_CSTRING("UTC-")) == 0) {
|
|
||||||
if (!newTimezone.SetCharAt('+', 3)) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the timezone only when the system timezone is not identical.
|
|
||||||
nsCString curTimezone = hal::GetTimezone();
|
|
||||||
if (!curTimezone.Equals(newTimezone)) {
|
|
||||||
hal::SetTimezone(newTimezone);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeZoneSettingObserver::~TimeZoneSettingObserver()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
|
||||||
if (observerService) {
|
|
||||||
observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(TimeZoneSettingObserver, nsIObserver)
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
TimeZoneSettingObserver::Observe(nsISupports *aSubject,
|
|
||||||
const char *aTopic,
|
|
||||||
const char16_t *aData)
|
|
||||||
{
|
|
||||||
if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that this function gets called for any and all settings changes,
|
|
||||||
// so we need to carefully check if we have the one we're interested in.
|
|
||||||
//
|
|
||||||
// The string that we're interested in will be a JSON string that looks like:
|
|
||||||
// {"key":"time.timezone","value":"America/Chicago"} or
|
|
||||||
// {"key":"time.timezone","value":"UTC-05:00"}
|
|
||||||
|
|
||||||
AutoSafeJSContext cx;
|
|
||||||
RootedDictionary<SettingChangeNotification> setting(cx);
|
|
||||||
if (!WrappedJSToDictionary(cx, aSubject, setting)) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
if (!setting.mKey.EqualsASCII(TIME_TIMEZONE)) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
if (!setting.mValue.isString()) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the system timezone.
|
|
||||||
return SetTimeZone(setting.mValue, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
static mozilla::StaticRefPtr<TimeZoneSettingObserver> sTimeZoneSettingObserver;
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
void
|
|
||||||
InitializeTimeZoneSettingObserver()
|
|
||||||
{
|
|
||||||
sTimeZoneSettingObserver = new TimeZoneSettingObserver();
|
|
||||||
ClearOnShutdown(&sTimeZoneSettingObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/* 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_system_timesetting_h__
|
|
||||||
#define mozilla_system_timesetting_h__
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
// Initialize TimeZoneSettingObserver which observes the time zone change
|
|
||||||
// event from settings service. When receiving the event, it modifies the
|
|
||||||
// system time zone.
|
|
||||||
void InitializeTimeZoneSettingObserver();
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_timesetting_h__
|
|
||||||
|
|
||||||
|
|
@ -1,596 +0,0 @@
|
||||||
/* 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 "Volume.h"
|
|
||||||
#include "VolumeCommand.h"
|
|
||||||
#include "VolumeManager.h"
|
|
||||||
#include "VolumeManagerLog.h"
|
|
||||||
#include "nsIVolume.h"
|
|
||||||
#include "nsXULAppAPI.h"
|
|
||||||
|
|
||||||
#include <vold/ResponseCode.h>
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
#if DEBUG_VOLUME_OBSERVER
|
|
||||||
void
|
|
||||||
VolumeObserverList::Broadcast(Volume* const& aVolume)
|
|
||||||
{
|
|
||||||
uint32_t size = mObservers.Length();
|
|
||||||
for (uint32_t i = 0; i < size; ++i) {
|
|
||||||
LOG("VolumeObserverList::Broadcast to [%u] %p volume '%s'",
|
|
||||||
i, mObservers[i], aVolume->NameStr());
|
|
||||||
mObservers[i]->Notify(aVolume);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VolumeObserverList Volume::sEventObserverList;
|
|
||||||
|
|
||||||
// We have a feature where volumes can be locked when mounted. This
|
|
||||||
// is used to prevent a volume from being shared with the PC while
|
|
||||||
// it is actively being used (say for storing an update image)
|
|
||||||
//
|
|
||||||
// We use WakeLocks (a poor choice of name, but it does what we want)
|
|
||||||
// from the PowerManagerService to determine when we're locked.
|
|
||||||
// In particular we'll create a wakelock called volume-NAME-GENERATION
|
|
||||||
// (where NAME is the volume name, and GENERATION is its generation
|
|
||||||
// number), and if this wakelock is locked, then we'll prevent a volume
|
|
||||||
// from being shared.
|
|
||||||
//
|
|
||||||
// Implementation Details:
|
|
||||||
//
|
|
||||||
// Since the AutoMounter can only control when something gets mounted
|
|
||||||
// and not when it gets unmounted (for example: a user pulls the SDCard)
|
|
||||||
// and because Volume and nsVolume data structures are maintained on
|
|
||||||
// separate threads, we have the potential for some race conditions.
|
|
||||||
// We eliminate the race conditions by introducing the concept of a
|
|
||||||
// generation number. Every time a volume transitions to the Mounted
|
|
||||||
// state, it gets assigned a new generation number. Whenever the state
|
|
||||||
// of a Volume changes, we send the updated state and current generation
|
|
||||||
// number to the main thread where it gets updated in the nsVolume.
|
|
||||||
//
|
|
||||||
// Since WakeLocks can only be queried from the main-thread, the
|
|
||||||
// nsVolumeService looks for WakeLock status changes, and forwards
|
|
||||||
// the results to the IOThread.
|
|
||||||
//
|
|
||||||
// If the Volume (IOThread) receives a volume update where the generation
|
|
||||||
// number mismatches, then the update is simply ignored.
|
|
||||||
//
|
|
||||||
// When a Volume (IOThread) initially becomes mounted, we assume it to
|
|
||||||
// be locked until we get our first update from nsVolume (MainThread).
|
|
||||||
static int32_t sMountGeneration = 0;
|
|
||||||
|
|
||||||
static uint32_t sNextId = 1;
|
|
||||||
|
|
||||||
// We don't get media inserted/removed events at startup. So we
|
|
||||||
// assume it's present, and we'll be told that it's missing.
|
|
||||||
Volume::Volume(const nsCSubstring& aName)
|
|
||||||
: mMediaPresent(true),
|
|
||||||
mState(nsIVolume::STATE_INIT),
|
|
||||||
mName(aName),
|
|
||||||
mMountGeneration(-1),
|
|
||||||
mMountLocked(true), // Needs to agree with nsVolume::nsVolume
|
|
||||||
mSharingEnabled(false),
|
|
||||||
mFormatRequested(false),
|
|
||||||
mMountRequested(false),
|
|
||||||
mUnmountRequested(false),
|
|
||||||
mCanBeShared(true),
|
|
||||||
mIsSharing(false),
|
|
||||||
mIsFormatting(false),
|
|
||||||
mIsUnmounting(false),
|
|
||||||
mIsRemovable(false),
|
|
||||||
mIsHotSwappable(false),
|
|
||||||
mId(sNextId++)
|
|
||||||
{
|
|
||||||
DBG("Volume %s: created", NameStr());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::Dump(const char* aLabel) const
|
|
||||||
{
|
|
||||||
LOG("%s: Volume: %s (%d) is %s and %s @ %s gen %d locked %d",
|
|
||||||
aLabel,
|
|
||||||
NameStr(),
|
|
||||||
Id(),
|
|
||||||
StateStr(),
|
|
||||||
MediaPresent() ? "inserted" : "missing",
|
|
||||||
MountPoint().get(),
|
|
||||||
MountGeneration(),
|
|
||||||
(int)IsMountLocked());
|
|
||||||
LOG("%s: Sharing %s Mounting %s Formating %s Unmounting %s",
|
|
||||||
aLabel,
|
|
||||||
CanBeShared() ? (IsSharingEnabled() ? (IsSharing() ? "en-y" : "en-n")
|
|
||||||
: "dis")
|
|
||||||
: "x",
|
|
||||||
IsMountRequested() ? "req" : "n",
|
|
||||||
IsFormatRequested() ? (IsFormatting() ? "req-y" : "req-n")
|
|
||||||
: (IsFormatting() ? "y" : "n"),
|
|
||||||
IsUnmountRequested() ? (IsUnmounting() ? "req-y" : "req-n")
|
|
||||||
: (IsUnmounting() ? "y" : "n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::ResolveAndSetMountPoint(const nsCSubstring& aMountPoint)
|
|
||||||
{
|
|
||||||
nsCString mountPoint(aMountPoint);
|
|
||||||
char realPathBuf[PATH_MAX];
|
|
||||||
|
|
||||||
// Call realpath so that we wind up with a path which is compatible with
|
|
||||||
// functions like nsVolumeService::GetVolumeByPath.
|
|
||||||
|
|
||||||
if (realpath(mountPoint.get(), realPathBuf) < 0) {
|
|
||||||
// The path we were handed doesn't exist. Warn about it, but use it
|
|
||||||
// anyways assuming that the user knows what they're doing.
|
|
||||||
|
|
||||||
ERR("ResolveAndSetMountPoint: realpath on '%s' failed: %d",
|
|
||||||
mountPoint.get(), errno);
|
|
||||||
mMountPoint = mountPoint;
|
|
||||||
} else {
|
|
||||||
mMountPoint = realPathBuf;
|
|
||||||
}
|
|
||||||
DBG("Volume %s: Setting mountpoint to '%s'", NameStr(), mMountPoint.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Volume::SetFakeVolume(const nsACString& aMountPoint)
|
|
||||||
{
|
|
||||||
this->mMountLocked = false;
|
|
||||||
this->mCanBeShared = false;
|
|
||||||
ResolveAndSetMountPoint(aMountPoint);
|
|
||||||
SetState(nsIVolume::STATE_MOUNTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetIsSharing(bool aIsSharing)
|
|
||||||
{
|
|
||||||
if (aIsSharing == mIsSharing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIsSharing = aIsSharing;
|
|
||||||
LOG("Volume %s: IsSharing set to %d state %s",
|
|
||||||
NameStr(), (int)mIsSharing, StateStr(mState));
|
|
||||||
sEventObserverList.Broadcast(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetIsFormatting(bool aIsFormatting)
|
|
||||||
{
|
|
||||||
if (aIsFormatting == mIsFormatting) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIsFormatting = aIsFormatting;
|
|
||||||
LOG("Volume %s: IsFormatting set to %d state %s",
|
|
||||||
NameStr(), (int)mIsFormatting, StateStr(mState));
|
|
||||||
if (mIsFormatting) {
|
|
||||||
sEventObserverList.Broadcast(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetIsUnmounting(bool aIsUnmounting)
|
|
||||||
{
|
|
||||||
if (aIsUnmounting == mIsUnmounting) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIsUnmounting = aIsUnmounting;
|
|
||||||
LOG("Volume %s: IsUnmounting set to %d state %s",
|
|
||||||
NameStr(), (int)mIsUnmounting, StateStr(mState));
|
|
||||||
sEventObserverList.Broadcast(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetIsRemovable(bool aIsRemovable)
|
|
||||||
{
|
|
||||||
if (aIsRemovable == mIsRemovable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIsRemovable = aIsRemovable;
|
|
||||||
if (!mIsRemovable) {
|
|
||||||
mIsHotSwappable = false;
|
|
||||||
}
|
|
||||||
LOG("Volume %s: IsRemovable set to %d state %s",
|
|
||||||
NameStr(), (int)mIsRemovable, StateStr(mState));
|
|
||||||
sEventObserverList.Broadcast(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetIsHotSwappable(bool aIsHotSwappable)
|
|
||||||
{
|
|
||||||
if (aIsHotSwappable == mIsHotSwappable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIsHotSwappable = aIsHotSwappable;
|
|
||||||
if (mIsHotSwappable) {
|
|
||||||
mIsRemovable = true;
|
|
||||||
}
|
|
||||||
LOG("Volume %s: IsHotSwappable set to %d state %s",
|
|
||||||
NameStr(), (int)mIsHotSwappable, StateStr(mState));
|
|
||||||
sEventObserverList.Broadcast(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Volume::BoolConfigValue(const nsCString& aConfigValue, bool& aBoolValue)
|
|
||||||
{
|
|
||||||
if (aConfigValue.EqualsLiteral("1") ||
|
|
||||||
aConfigValue.LowerCaseEqualsLiteral("true")) {
|
|
||||||
aBoolValue = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (aConfigValue.EqualsLiteral("0") ||
|
|
||||||
aConfigValue.LowerCaseEqualsLiteral("false")) {
|
|
||||||
aBoolValue = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetConfig(const nsCString& aConfigName, const nsCString& aConfigValue)
|
|
||||||
{
|
|
||||||
if (aConfigName.LowerCaseEqualsLiteral("removable")) {
|
|
||||||
bool value = false;
|
|
||||||
if (BoolConfigValue(aConfigValue, value)) {
|
|
||||||
SetIsRemovable(value);
|
|
||||||
} else {
|
|
||||||
ERR("Volume %s: invalid value '%s' for configuration '%s'",
|
|
||||||
NameStr(), aConfigValue.get(), aConfigName.get());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (aConfigName.LowerCaseEqualsLiteral("hotswappable")) {
|
|
||||||
bool value = false;
|
|
||||||
if (BoolConfigValue(aConfigValue, value)) {
|
|
||||||
SetIsHotSwappable(value);
|
|
||||||
} else {
|
|
||||||
ERR("Volume %s: invalid value '%s' for configuration '%s'",
|
|
||||||
NameStr(), aConfigValue.get(), aConfigName.get());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ERR("Volume %s: invalid config '%s'", NameStr(), aConfigName.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetMediaPresent(bool aMediaPresent)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
// mMediaPresent is slightly redunant to the state, however
|
|
||||||
// when media is removed (while Idle), we get the following:
|
|
||||||
// 631 Volume sdcard /mnt/sdcard disk removed (179:0)
|
|
||||||
// 605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
|
|
||||||
//
|
|
||||||
// And on media insertion, we get:
|
|
||||||
// 630 Volume sdcard /mnt/sdcard disk inserted (179:0)
|
|
||||||
// 605 Volume sdcard /mnt/sdcard state changed from 0 (No-Media) to 2 (Pending)
|
|
||||||
// 605 Volume sdcard /mnt/sdcard state changed from 2 (Pending) to 1 (Idle-Unmounted)
|
|
||||||
//
|
|
||||||
// On media removal while the media is mounted:
|
|
||||||
// 632 Volume sdcard /mnt/sdcard bad removal (179:1)
|
|
||||||
// 605 Volume sdcard /mnt/sdcard state changed from 4 (Mounted) to 5 (Unmounting)
|
|
||||||
// 605 Volume sdcard /mnt/sdcard state changed from 5 (Unmounting) to 1 (Idle-Unmounted)
|
|
||||||
// 631 Volume sdcard /mnt/sdcard disk removed (179:0)
|
|
||||||
// 605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
|
|
||||||
//
|
|
||||||
// When sharing with a PC, it goes Mounted -> Idle -> Shared
|
|
||||||
// When unsharing with a PC, it goes Shared -> Idle -> Mounted
|
|
||||||
//
|
|
||||||
// The AutoMounter needs to know whether the media is present or not when
|
|
||||||
// processing the Idle state.
|
|
||||||
|
|
||||||
if (mMediaPresent == aMediaPresent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG("Volume: %s media %s", NameStr(), aMediaPresent ? "inserted" : "removed");
|
|
||||||
mMediaPresent = aMediaPresent;
|
|
||||||
sEventObserverList.Broadcast(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetSharingEnabled(bool aSharingEnabled)
|
|
||||||
{
|
|
||||||
mSharingEnabled = aSharingEnabled;
|
|
||||||
|
|
||||||
LOG("SetSharingMode for volume %s to %d canBeShared = %d",
|
|
||||||
NameStr(), (int)mSharingEnabled, (int)mCanBeShared);
|
|
||||||
sEventObserverList.Broadcast(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetFormatRequested(bool aFormatRequested)
|
|
||||||
{
|
|
||||||
mFormatRequested = aFormatRequested;
|
|
||||||
|
|
||||||
LOG("SetFormatRequested for volume %s to %d CanBeFormatted = %d",
|
|
||||||
NameStr(), (int)mFormatRequested, (int)CanBeFormatted());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetMountRequested(bool aMountRequested)
|
|
||||||
{
|
|
||||||
mMountRequested = aMountRequested;
|
|
||||||
|
|
||||||
LOG("SetMountRequested for volume %s to %d CanBeMounted = %d",
|
|
||||||
NameStr(), (int)mMountRequested, (int)CanBeMounted());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetUnmountRequested(bool aUnmountRequested)
|
|
||||||
{
|
|
||||||
mUnmountRequested = aUnmountRequested;
|
|
||||||
|
|
||||||
LOG("SetUnmountRequested for volume %s to %d CanBeMounted = %d",
|
|
||||||
NameStr(), (int)mUnmountRequested, (int)CanBeMounted());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetState(Volume::STATE aNewState)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
if (aNewState == mState) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (aNewState == nsIVolume::STATE_MOUNTED) {
|
|
||||||
mMountGeneration = ++sMountGeneration;
|
|
||||||
LOG("Volume %s (%u): changing state from %s to %s @ '%s' (%d observers) "
|
|
||||||
"mountGeneration = %d, locked = %d",
|
|
||||||
NameStr(), mId, StateStr(mState),
|
|
||||||
StateStr(aNewState), mMountPoint.get(), sEventObserverList.Length(),
|
|
||||||
mMountGeneration, (int)mMountLocked);
|
|
||||||
} else {
|
|
||||||
LOG("Volume %s (%u): changing state from %s to %s (%d observers)",
|
|
||||||
NameStr(), mId, StateStr(mState),
|
|
||||||
StateStr(aNewState), sEventObserverList.Length());
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (aNewState) {
|
|
||||||
case nsIVolume::STATE_NOMEDIA:
|
|
||||||
// Cover the startup case where we don't get insertion/removal events
|
|
||||||
mMediaPresent = false;
|
|
||||||
mIsSharing = false;
|
|
||||||
mUnmountRequested = false;
|
|
||||||
mMountRequested = false;
|
|
||||||
mIsUnmounting = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case nsIVolume::STATE_MOUNTED:
|
|
||||||
case nsIVolume::STATE_MOUNT_FAIL:
|
|
||||||
mMountRequested = false;
|
|
||||||
mIsFormatting = false;
|
|
||||||
mIsSharing = false;
|
|
||||||
mIsUnmounting = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case nsIVolume::STATE_FORMATTING:
|
|
||||||
mFormatRequested = false;
|
|
||||||
mIsFormatting = true;
|
|
||||||
mIsSharing = false;
|
|
||||||
mIsUnmounting = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case nsIVolume::STATE_SHARED:
|
|
||||||
case nsIVolume::STATE_SHAREDMNT:
|
|
||||||
// Covers startup cases. Normally, mIsSharing would be set to true
|
|
||||||
// when we issue the command to initiate the sharing process, but
|
|
||||||
// it's conceivable that a volume could already be in a shared state
|
|
||||||
// when b2g starts.
|
|
||||||
mIsSharing = true;
|
|
||||||
mIsUnmounting = false;
|
|
||||||
mIsFormatting = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case nsIVolume::STATE_UNMOUNTING:
|
|
||||||
mIsUnmounting = true;
|
|
||||||
mIsFormatting = false;
|
|
||||||
mIsSharing = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case nsIVolume::STATE_IDLE: // Fall through
|
|
||||||
case nsIVolume::STATE_CHECKMNT: // Fall through
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mState = aNewState;
|
|
||||||
sEventObserverList.Broadcast(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::SetMountPoint(const nsCSubstring& aMountPoint)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
if (mMountPoint.Equals(aMountPoint)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ResolveAndSetMountPoint(aMountPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::StartMount(VolumeResponseCallback* aCallback)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
StartCommand(new VolumeActionCommand(this, "mount", "", aCallback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::StartUnmount(VolumeResponseCallback* aCallback)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
StartCommand(new VolumeActionCommand(this, "unmount", "force", aCallback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::StartFormat(VolumeResponseCallback* aCallback)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
StartCommand(new VolumeActionCommand(this, "format", "", aCallback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::StartShare(VolumeResponseCallback* aCallback)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
StartCommand(new VolumeActionCommand(this, "share", "ums", aCallback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::StartUnshare(VolumeResponseCallback* aCallback)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
StartCommand(new VolumeActionCommand(this, "unshare", "ums", aCallback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::StartCommand(VolumeCommand* aCommand)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
VolumeManager::PostCommand(aCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
Volume::RegisterVolumeObserver(Volume::EventObserver* aObserver, const char* aName)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
sEventObserverList.AddObserver(aObserver);
|
|
||||||
|
|
||||||
DBG("Added Volume Observer '%s' @%p, length = %u",
|
|
||||||
aName, aObserver, sEventObserverList.Length());
|
|
||||||
|
|
||||||
// Send an initial event to the observer (for each volume)
|
|
||||||
size_t numVolumes = VolumeManager::NumVolumes();
|
|
||||||
for (size_t volIndex = 0; volIndex < numVolumes; volIndex++) {
|
|
||||||
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
|
|
||||||
aObserver->Notify(vol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
Volume::UnregisterVolumeObserver(Volume::EventObserver* aObserver, const char* aName)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
sEventObserverList.RemoveObserver(aObserver);
|
|
||||||
|
|
||||||
DBG("Removed Volume Observer '%s' @%p, length = %u",
|
|
||||||
aName, aObserver, sEventObserverList.Length());
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
Volume::UpdateMountLock(const nsACString& aVolumeName,
|
|
||||||
const int32_t& aMountGeneration,
|
|
||||||
const bool& aMountLocked)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName);
|
|
||||||
if (!vol || (vol->mMountGeneration != aMountGeneration)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (vol->mMountLocked != aMountLocked) {
|
|
||||||
vol->mMountLocked = aMountLocked;
|
|
||||||
DBG("Volume::UpdateMountLock for '%s' to %d\n", vol->NameStr(), (int)aMountLocked);
|
|
||||||
sEventObserverList.Broadcast(vol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Volume::HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
// The volume name will have already been parsed, and the tokenizer will point
|
|
||||||
// to the token after the volume name
|
|
||||||
switch (aResponseCode) {
|
|
||||||
case ::ResponseCode::VolumeListResult: {
|
|
||||||
// Each line will look something like:
|
|
||||||
//
|
|
||||||
// sdcard /mnt/sdcard 1
|
|
||||||
//
|
|
||||||
nsDependentCSubstring mntPoint(aTokenizer.nextToken());
|
|
||||||
SetMountPoint(mntPoint);
|
|
||||||
nsresult errCode;
|
|
||||||
nsCString state(aTokenizer.nextToken());
|
|
||||||
if (state.EqualsLiteral("X")) {
|
|
||||||
// Special state for creating fake volumes which can't be shared.
|
|
||||||
mCanBeShared = false;
|
|
||||||
SetState(nsIVolume::STATE_MOUNTED);
|
|
||||||
} else {
|
|
||||||
SetState((STATE)state.ToInteger(&errCode));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ::ResponseCode::VolumeStateChange: {
|
|
||||||
// Format of the line looks something like:
|
|
||||||
//
|
|
||||||
// Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
|
|
||||||
//
|
|
||||||
// So we parse out the state after the string " to "
|
|
||||||
while (aTokenizer.hasMoreTokens()) {
|
|
||||||
nsAutoCString token(aTokenizer.nextToken());
|
|
||||||
if (token.EqualsLiteral("to")) {
|
|
||||||
nsresult errCode;
|
|
||||||
token = aTokenizer.nextToken();
|
|
||||||
STATE newState = (STATE)(token.ToInteger(&errCode));
|
|
||||||
if (newState == nsIVolume::STATE_MOUNTED) {
|
|
||||||
// We set the state to STATE_CHECKMNT here, and the once the
|
|
||||||
// AutoMounter detects that the volume is actually accessible
|
|
||||||
// then the AutoMounter will set the volume as STATE_MOUNTED.
|
|
||||||
SetState(nsIVolume::STATE_CHECKMNT);
|
|
||||||
} else {
|
|
||||||
if (State() == nsIVolume::STATE_CHECKING && newState == nsIVolume::STATE_IDLE) {
|
|
||||||
LOG("Mount of volume '%s' failed", NameStr());
|
|
||||||
SetState(nsIVolume::STATE_MOUNT_FAIL);
|
|
||||||
} else {
|
|
||||||
SetState(newState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ::ResponseCode::VolumeDiskInserted:
|
|
||||||
SetMediaPresent(true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ::ResponseCode::VolumeDiskRemoved: // fall-thru
|
|
||||||
case ::ResponseCode::VolumeBadRemoval:
|
|
||||||
SetMediaPresent(false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG("Volume: %s unrecognized reponse code (ignored)", NameStr());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
/* 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_system_volume_h__
|
|
||||||
#define mozilla_system_volume_h__
|
|
||||||
|
|
||||||
#include "VolumeCommand.h"
|
|
||||||
#include "nsIVolume.h"
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "mozilla/Observer.h"
|
|
||||||
#include "nsISupportsImpl.h"
|
|
||||||
#include "nsWhitespaceTokenizer.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* There is an instance of the Volume class for each volume reported
|
|
||||||
* from vold.
|
|
||||||
*
|
|
||||||
* Each volume originates from the /system/etv/vold.fstab file.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
class Volume;
|
|
||||||
|
|
||||||
#define DEBUG_VOLUME_OBSERVER 0
|
|
||||||
|
|
||||||
#if DEBUG_VOLUME_OBSERVER
|
|
||||||
class VolumeObserverList : public mozilla::ObserverList<Volume*>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void Broadcast(Volume* const& aVolume);
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
typedef mozilla::ObserverList<Volume*> VolumeObserverList;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class Volume final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_REFCOUNTING(Volume)
|
|
||||||
|
|
||||||
Volume(const nsCSubstring& aVolumeName);
|
|
||||||
|
|
||||||
typedef long STATE; // States are now defined in nsIVolume.idl
|
|
||||||
|
|
||||||
static const char* StateStr(STATE aState) { return NS_VolumeStateStr(aState); }
|
|
||||||
const char* StateStr() const { return StateStr(mState); }
|
|
||||||
STATE State() const { return mState; }
|
|
||||||
|
|
||||||
const nsCString& Name() const { return mName; }
|
|
||||||
const char* NameStr() const { return mName.get(); }
|
|
||||||
|
|
||||||
void Dump(const char* aLabel) const;
|
|
||||||
|
|
||||||
// The mount point is the name of the directory where the volume is mounted.
|
|
||||||
// (i.e. path that leads to the files stored on the volume).
|
|
||||||
const nsCString& MountPoint() const { return mMountPoint; }
|
|
||||||
|
|
||||||
uint32_t Id() const { return mId; }
|
|
||||||
|
|
||||||
int32_t MountGeneration() const { return mMountGeneration; }
|
|
||||||
bool IsMountLocked() const { return mMountLocked; }
|
|
||||||
bool MediaPresent() const { return mMediaPresent; }
|
|
||||||
bool CanBeShared() const { return mCanBeShared; }
|
|
||||||
bool CanBeFormatted() const { return CanBeShared(); }
|
|
||||||
bool CanBeMounted() const { return CanBeShared(); }
|
|
||||||
bool IsSharingEnabled() const { return mCanBeShared && mSharingEnabled; }
|
|
||||||
bool IsFormatRequested() const { return CanBeFormatted() && mFormatRequested; }
|
|
||||||
bool IsMountRequested() const { return CanBeMounted() && mMountRequested; }
|
|
||||||
bool IsUnmountRequested() const { return CanBeMounted() && mUnmountRequested; }
|
|
||||||
bool IsSharing() const { return mIsSharing; }
|
|
||||||
bool IsFormatting() const { return mIsFormatting; }
|
|
||||||
bool IsUnmounting() const { return mIsUnmounting; }
|
|
||||||
bool IsRemovable() const { return mIsRemovable; }
|
|
||||||
bool IsHotSwappable() const { return mIsHotSwappable; }
|
|
||||||
|
|
||||||
void SetFakeVolume(const nsACString& aMountPoint);
|
|
||||||
|
|
||||||
void SetSharingEnabled(bool aSharingEnabled);
|
|
||||||
void SetFormatRequested(bool aFormatRequested);
|
|
||||||
void SetMountRequested(bool aMountRequested);
|
|
||||||
void SetUnmountRequested(bool aUnmountRequested);
|
|
||||||
|
|
||||||
typedef mozilla::Observer<Volume *> EventObserver;
|
|
||||||
|
|
||||||
// NOTE: that observers must live in the IOThread.
|
|
||||||
static void RegisterVolumeObserver(EventObserver* aObserver, const char* aName);
|
|
||||||
static void UnregisterVolumeObserver(EventObserver* aObserver, const char* aName);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
~Volume() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class AutoMounter; // Calls StartXxx
|
|
||||||
friend class nsVolume; // Calls UpdateMountLock
|
|
||||||
friend class VolumeManager; // Calls HandleVoldResponse
|
|
||||||
friend class VolumeListCallback; // Calls SetMountPoint, SetState
|
|
||||||
|
|
||||||
// The StartXxx functions will queue up a command to the VolumeManager.
|
|
||||||
// You can queue up as many commands as you like, and aCallback will
|
|
||||||
// be called as each one completes.
|
|
||||||
void StartMount(VolumeResponseCallback* aCallback);
|
|
||||||
void StartUnmount(VolumeResponseCallback* aCallback);
|
|
||||||
void StartFormat(VolumeResponseCallback* aCallback);
|
|
||||||
void StartShare(VolumeResponseCallback* aCallback);
|
|
||||||
void StartUnshare(VolumeResponseCallback* aCallback);
|
|
||||||
|
|
||||||
void SetIsSharing(bool aIsSharing);
|
|
||||||
void SetIsFormatting(bool aIsFormatting);
|
|
||||||
void SetIsUnmounting(bool aIsUnmounting);
|
|
||||||
void SetIsRemovable(bool aIsRemovable);
|
|
||||||
void SetIsHotSwappable(bool aIsHotSwappable);
|
|
||||||
void SetState(STATE aNewState);
|
|
||||||
void SetMediaPresent(bool aMediaPresent);
|
|
||||||
void SetMountPoint(const nsCSubstring& aMountPoint);
|
|
||||||
void StartCommand(VolumeCommand* aCommand);
|
|
||||||
|
|
||||||
void ResolveAndSetMountPoint(const nsCSubstring& aMountPoint);
|
|
||||||
|
|
||||||
bool BoolConfigValue(const nsCString& aConfigValue, bool& aBoolValue);
|
|
||||||
void SetConfig(const nsCString& aConfigName, const nsCString& aConfigValue);
|
|
||||||
|
|
||||||
void HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer);
|
|
||||||
|
|
||||||
static void UpdateMountLock(const nsACString& aVolumeName,
|
|
||||||
const int32_t& aMountGeneration,
|
|
||||||
const bool& aMountLocked);
|
|
||||||
|
|
||||||
bool mMediaPresent;
|
|
||||||
STATE mState;
|
|
||||||
const nsCString mName;
|
|
||||||
nsCString mMountPoint;
|
|
||||||
int32_t mMountGeneration;
|
|
||||||
bool mMountLocked;
|
|
||||||
bool mSharingEnabled;
|
|
||||||
bool mFormatRequested;
|
|
||||||
bool mMountRequested;
|
|
||||||
bool mUnmountRequested;
|
|
||||||
bool mCanBeShared;
|
|
||||||
bool mIsSharing;
|
|
||||||
bool mIsFormatting;
|
|
||||||
bool mIsUnmounting;
|
|
||||||
bool mIsRemovable;
|
|
||||||
bool mIsHotSwappable;
|
|
||||||
uint32_t mId; // Unique ID (used by MTP)
|
|
||||||
|
|
||||||
static VolumeObserverList sEventObserverList;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_volumemanager_h__
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
/* 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 "nsString.h"
|
|
||||||
#include "nsWhitespaceTokenizer.h"
|
|
||||||
|
|
||||||
#include "Volume.h"
|
|
||||||
#include "VolumeCommand.h"
|
|
||||||
#include "VolumeManager.h"
|
|
||||||
#include "VolumeManagerLog.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* The VolumeActionCommand class is used to send commands which apply
|
|
||||||
* to a particular volume.
|
|
||||||
*
|
|
||||||
* The following commands would fit into this category:
|
|
||||||
*
|
|
||||||
* volume mount <volname>
|
|
||||||
* volume unmount <volname> [force]
|
|
||||||
* volume format <volname>
|
|
||||||
* volume share <volname> <method>
|
|
||||||
* volume unshare <volname> <method>
|
|
||||||
* volume shared <volname> <method>
|
|
||||||
*
|
|
||||||
* A typical response looks like:
|
|
||||||
*
|
|
||||||
* # vdc volume unshare sdcard ums
|
|
||||||
* 605 Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
|
|
||||||
* 200 volume operation succeeded
|
|
||||||
*
|
|
||||||
* Note that the 600 series of responses are considered unsolicited and
|
|
||||||
* are dealt with directly by the VolumeManager. This command will only
|
|
||||||
* see the terminating response code (200 in the example above).
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
VolumeActionCommand::VolumeActionCommand(Volume* aVolume,
|
|
||||||
const char* aAction,
|
|
||||||
const char* aExtraArgs,
|
|
||||||
VolumeResponseCallback* aCallback)
|
|
||||||
: VolumeCommand(aCallback),
|
|
||||||
mVolume(aVolume)
|
|
||||||
{
|
|
||||||
nsAutoCString cmd;
|
|
||||||
|
|
||||||
cmd = "volume ";
|
|
||||||
cmd += aAction;
|
|
||||||
cmd += " ";
|
|
||||||
cmd += aVolume->Name().get();
|
|
||||||
|
|
||||||
// vold doesn't like trailing white space, so only add it if we really need to.
|
|
||||||
if (aExtraArgs && (*aExtraArgs != '\0')) {
|
|
||||||
cmd += " ";
|
|
||||||
cmd += aExtraArgs;
|
|
||||||
}
|
|
||||||
SetCmd(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* The VolumeListCommand class is used to send the "volume list" command to
|
|
||||||
* vold.
|
|
||||||
*
|
|
||||||
* A typical response looks like:
|
|
||||||
*
|
|
||||||
* # vdc volume list
|
|
||||||
* 110 sdcard /mnt/sdcard 4
|
|
||||||
* 110 sdcard1 /mnt/sdcard/external_sd 4
|
|
||||||
* 200 Volumes listed.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
VolumeListCommand::VolumeListCommand(VolumeResponseCallback* aCallback)
|
|
||||||
: VolumeCommand(NS_LITERAL_CSTRING("volume list"), aCallback)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
||||||
|
|
@ -1,204 +0,0 @@
|
||||||
/* 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_system_volumecommand_h__
|
|
||||||
#define mozilla_system_volumecommand_h__
|
|
||||||
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "nsISupportsImpl.h"
|
|
||||||
#include "mozilla/RefPtr.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vold/ResponseCode.h>
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
class Volume;
|
|
||||||
class VolumeCommand;
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* The VolumeResponseCallback class is an abstract base class. The ResponseReceived
|
|
||||||
* method will be called for each response received.
|
|
||||||
*
|
|
||||||
* Depending on the command, there may be multiple responses for the
|
|
||||||
* command. Done() will return true if this is the last response.
|
|
||||||
*
|
|
||||||
* The responses from vold are all of the form:
|
|
||||||
*
|
|
||||||
* <ResponseCode> <String>
|
|
||||||
*
|
|
||||||
* Valid Response codes can be found in the vold/ResponseCode.h header.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
class VolumeResponseCallback
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
virtual ~VolumeResponseCallback() {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_REFCOUNTING(VolumeResponseCallback)
|
|
||||||
VolumeResponseCallback()
|
|
||||||
: mResponseCode(0), mPending(false) {}
|
|
||||||
|
|
||||||
bool Done() const
|
|
||||||
{
|
|
||||||
// Response codes from the 200, 400, and 500 series all indicated that
|
|
||||||
// the command has completed.
|
|
||||||
|
|
||||||
return (mResponseCode >= ::ResponseCode::CommandOkay)
|
|
||||||
&& (mResponseCode < ::ResponseCode::UnsolicitedInformational);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WasSuccessful() const
|
|
||||||
{
|
|
||||||
return mResponseCode == ::ResponseCode::CommandOkay;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPending() const { return mPending; }
|
|
||||||
int ResponseCode() const { return mResponseCode; }
|
|
||||||
const nsCString &ResponseStr() const { return mResponseStr; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void ResponseReceived(const VolumeCommand* aCommand) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class VolumeCommand; // Calls HandleResponse and SetPending
|
|
||||||
|
|
||||||
void HandleResponse(const VolumeCommand* aCommand,
|
|
||||||
int aResponseCode,
|
|
||||||
nsACString& aResponseStr)
|
|
||||||
{
|
|
||||||
mResponseCode = aResponseCode;
|
|
||||||
#if ANDROID_VERSION >= 17
|
|
||||||
// There's a sequence number here that we don't care about
|
|
||||||
// We expect it to be 0. See VolumeCommand::SetCmd
|
|
||||||
mResponseStr = Substring(aResponseStr, 2);
|
|
||||||
#else
|
|
||||||
mResponseStr = aResponseStr;
|
|
||||||
#endif
|
|
||||||
if (mResponseCode >= ::ResponseCode::CommandOkay) {
|
|
||||||
// This is a final response.
|
|
||||||
mPending = false;
|
|
||||||
}
|
|
||||||
ResponseReceived(aCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetPending(bool aPending) { mPending = aPending; }
|
|
||||||
|
|
||||||
int mResponseCode; // The response code parsed from vold
|
|
||||||
nsCString mResponseStr; // The rest of the line.
|
|
||||||
bool mPending; // Waiting for response?
|
|
||||||
};
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* The VolumeCommand class is an abstract base class used to encapsulate
|
|
||||||
* volume commands send to vold.
|
|
||||||
*
|
|
||||||
* See VolumeManager.h for a list of the volume commands.
|
|
||||||
*
|
|
||||||
* Commands sent to vold need an explicit null character so we add one
|
|
||||||
* to the command to ensure that it's included in the length.
|
|
||||||
*
|
|
||||||
* All of these commands are asynchronous in nature, and the
|
|
||||||
* ResponseReceived callback will be called when a response is available.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
class VolumeCommand
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
virtual ~VolumeCommand() {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_REFCOUNTING(VolumeCommand)
|
|
||||||
|
|
||||||
VolumeCommand(VolumeResponseCallback* aCallback)
|
|
||||||
: mBytesConsumed(0),
|
|
||||||
mCallback(aCallback)
|
|
||||||
{
|
|
||||||
SetCmd(NS_LITERAL_CSTRING(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
VolumeCommand(const nsACString& aCommand, VolumeResponseCallback* aCallback)
|
|
||||||
: mBytesConsumed(0),
|
|
||||||
mCallback(aCallback)
|
|
||||||
{
|
|
||||||
SetCmd(aCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetCmd(const nsACString& aCommand)
|
|
||||||
{
|
|
||||||
mCmd.Truncate();
|
|
||||||
#if ANDROID_VERSION >= 17
|
|
||||||
// JB requires a sequence number at the beginning of messages.
|
|
||||||
// It doesn't matter what we use, so we use 0.
|
|
||||||
mCmd = "0 ";
|
|
||||||
#endif
|
|
||||||
mCmd.Append(aCommand);
|
|
||||||
// Add a null character. We want this to be included in the length since
|
|
||||||
// vold uses it to determine the end of the command.
|
|
||||||
mCmd.Append('\0');
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* CmdStr() const { return mCmd.get(); }
|
|
||||||
const char* Data() const { return mCmd.Data() + mBytesConsumed; }
|
|
||||||
size_t BytesConsumed() const { return mBytesConsumed; }
|
|
||||||
|
|
||||||
size_t BytesRemaining() const
|
|
||||||
{
|
|
||||||
return mCmd.Length() - std::min(mCmd.Length(), mBytesConsumed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsumeBytes(size_t aNumBytes)
|
|
||||||
{
|
|
||||||
mBytesConsumed += std::min(BytesRemaining(), aNumBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class VolumeManager; // Calls SetPending & HandleResponse
|
|
||||||
|
|
||||||
void SetPending(bool aPending)
|
|
||||||
{
|
|
||||||
if (mCallback) {
|
|
||||||
mCallback->SetPending(aPending);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleResponse(int aResponseCode, nsACString& aResponseStr)
|
|
||||||
{
|
|
||||||
if (mCallback) {
|
|
||||||
mCallback->HandleResponse(this, aResponseCode, aResponseStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCString mCmd; // Command being sent
|
|
||||||
size_t mBytesConsumed; // How many bytes have been sent
|
|
||||||
|
|
||||||
// Called when a response to the command is received.
|
|
||||||
RefPtr<VolumeResponseCallback> mCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VolumeActionCommand : public VolumeCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VolumeActionCommand(Volume* aVolume, const char* aAction,
|
|
||||||
const char* aExtraArgs, VolumeResponseCallback* aCallback);
|
|
||||||
|
|
||||||
private:
|
|
||||||
RefPtr<Volume> mVolume;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VolumeListCommand : public VolumeCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VolumeListCommand(VolumeResponseCallback* aCallback);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_volumecommand_h__
|
|
||||||
|
|
@ -1,591 +0,0 @@
|
||||||
/* 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 "VolumeManager.h"
|
|
||||||
|
|
||||||
#include "Volume.h"
|
|
||||||
#include "VolumeCommand.h"
|
|
||||||
#include "VolumeManagerLog.h"
|
|
||||||
#include "VolumeServiceTest.h"
|
|
||||||
|
|
||||||
#include "nsWhitespaceTokenizer.h"
|
|
||||||
#include "nsXULAppAPI.h"
|
|
||||||
|
|
||||||
#include "base/message_loop.h"
|
|
||||||
#include "base/task.h"
|
|
||||||
#include "mozilla/Scoped.h"
|
|
||||||
#include "mozilla/StaticPtr.h"
|
|
||||||
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <cutils/sockets.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
static StaticRefPtr<VolumeManager> sVolumeManager;
|
|
||||||
|
|
||||||
VolumeManager::STATE VolumeManager::mState = VolumeManager::UNINITIALIZED;
|
|
||||||
VolumeManager::StateObserverList VolumeManager::mStateObserverList;
|
|
||||||
|
|
||||||
/***************************************************************************/
|
|
||||||
|
|
||||||
VolumeManager::VolumeManager()
|
|
||||||
: LineWatcher('\0', kRcvBufSize),
|
|
||||||
mSocket(-1),
|
|
||||||
mCommandPending(false)
|
|
||||||
{
|
|
||||||
DBG("VolumeManager constructor called");
|
|
||||||
}
|
|
||||||
|
|
||||||
VolumeManager::~VolumeManager()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
VolumeManager::Dump(const char* aLabel)
|
|
||||||
{
|
|
||||||
if (!sVolumeManager) {
|
|
||||||
LOG("%s: sVolumeManager == null", aLabel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VolumeArray::size_type numVolumes = NumVolumes();
|
|
||||||
VolumeArray::index_type volIndex;
|
|
||||||
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
|
|
||||||
RefPtr<Volume> vol = GetVolume(volIndex);
|
|
||||||
vol->Dump(aLabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
size_t
|
|
||||||
VolumeManager::NumVolumes()
|
|
||||||
{
|
|
||||||
if (!sVolumeManager) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return sVolumeManager->mVolumeArray.Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
already_AddRefed<Volume>
|
|
||||||
VolumeManager::GetVolume(size_t aIndex)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aIndex < NumVolumes());
|
|
||||||
RefPtr<Volume> vol = sVolumeManager->mVolumeArray[aIndex];
|
|
||||||
return vol.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
VolumeManager::STATE
|
|
||||||
VolumeManager::State()
|
|
||||||
{
|
|
||||||
return mState;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
const char *
|
|
||||||
VolumeManager::StateStr(VolumeManager::STATE aState)
|
|
||||||
{
|
|
||||||
switch (aState) {
|
|
||||||
case UNINITIALIZED: return "Uninitialized";
|
|
||||||
case STARTING: return "Starting";
|
|
||||||
case VOLUMES_READY: return "Volumes Ready";
|
|
||||||
}
|
|
||||||
return "???";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
VolumeManager::SetState(STATE aNewState)
|
|
||||||
{
|
|
||||||
if (mState != aNewState) {
|
|
||||||
LOG("changing state from '%s' to '%s'",
|
|
||||||
StateStr(mState), StateStr(aNewState));
|
|
||||||
mState = aNewState;
|
|
||||||
mStateObserverList.Broadcast(StateChangedEvent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
VolumeManager::RegisterStateObserver(StateObserver* aObserver)
|
|
||||||
{
|
|
||||||
mStateObserverList.AddObserver(aObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void VolumeManager::UnregisterStateObserver(StateObserver* aObserver)
|
|
||||||
{
|
|
||||||
mStateObserverList.RemoveObserver(aObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
already_AddRefed<Volume>
|
|
||||||
VolumeManager::FindVolumeByName(const nsCSubstring& aName)
|
|
||||||
{
|
|
||||||
if (!sVolumeManager) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
VolumeArray::size_type numVolumes = NumVolumes();
|
|
||||||
VolumeArray::index_type volIndex;
|
|
||||||
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
|
|
||||||
RefPtr<Volume> vol = GetVolume(volIndex);
|
|
||||||
if (vol->Name().Equals(aName)) {
|
|
||||||
return vol.forget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
already_AddRefed<Volume>
|
|
||||||
VolumeManager::FindAddVolumeByName(const nsCSubstring& aName)
|
|
||||||
{
|
|
||||||
RefPtr<Volume> vol = FindVolumeByName(aName);
|
|
||||||
if (vol) {
|
|
||||||
return vol.forget();
|
|
||||||
}
|
|
||||||
// No volume found, create and add a new one.
|
|
||||||
vol = new Volume(aName);
|
|
||||||
sVolumeManager->mVolumeArray.AppendElement(vol);
|
|
||||||
return vol.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
bool
|
|
||||||
VolumeManager::RemoveVolumeByName(const nsCSubstring& aName)
|
|
||||||
{
|
|
||||||
if (!sVolumeManager) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
VolumeArray::size_type numVolumes = NumVolumes();
|
|
||||||
VolumeArray::index_type volIndex;
|
|
||||||
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
|
|
||||||
RefPtr<Volume> vol = GetVolume(volIndex);
|
|
||||||
if (vol->Name().Equals(aName)) {
|
|
||||||
sVolumeManager->mVolumeArray.RemoveElementAt(volIndex);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No volume found. Return false to indicate this.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//static
|
|
||||||
void VolumeManager::InitConfig()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
// This function uses /system/etc/volume.cfg to add additional volumes
|
|
||||||
// to the Volume Manager.
|
|
||||||
//
|
|
||||||
// This is useful on devices like the Nexus 4, which have no physical sd card
|
|
||||||
// or dedicated partition.
|
|
||||||
//
|
|
||||||
// The format of the volume.cfg file is as follows:
|
|
||||||
// create volume-name mount-point
|
|
||||||
// configure volume-name preference preference-value
|
|
||||||
// Blank lines and lines starting with the hash character "#" will be ignored.
|
|
||||||
|
|
||||||
ScopedCloseFile fp;
|
|
||||||
int n = 0;
|
|
||||||
char line[255];
|
|
||||||
const char *filename = "/system/etc/volume.cfg";
|
|
||||||
if (!(fp = fopen(filename, "r"))) {
|
|
||||||
LOG("Unable to open volume configuration file '%s' - ignoring", filename);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while(fgets(line, sizeof(line), fp)) {
|
|
||||||
n++;
|
|
||||||
|
|
||||||
if (line[0] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
nsCString commandline(line);
|
|
||||||
nsCWhitespaceTokenizer tokenizer(commandline);
|
|
||||||
if (!tokenizer.hasMoreTokens()) {
|
|
||||||
// Blank line - ignore
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCString command(tokenizer.nextToken());
|
|
||||||
if (command.EqualsLiteral("create")) {
|
|
||||||
if (!tokenizer.hasMoreTokens()) {
|
|
||||||
ERR("No vol_name in %s line %d", filename, n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nsCString volName(tokenizer.nextToken());
|
|
||||||
if (!tokenizer.hasMoreTokens()) {
|
|
||||||
ERR("No mount point for volume '%s'. %s line %d",
|
|
||||||
volName.get(), filename, n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nsCString mountPoint(tokenizer.nextToken());
|
|
||||||
RefPtr<Volume> vol = FindAddVolumeByName(volName);
|
|
||||||
vol->SetFakeVolume(mountPoint);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (command.EqualsLiteral("configure")) {
|
|
||||||
if (!tokenizer.hasMoreTokens()) {
|
|
||||||
ERR("No vol_name in %s line %d", filename, n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nsCString volName(tokenizer.nextToken());
|
|
||||||
if (!tokenizer.hasMoreTokens()) {
|
|
||||||
ERR("No configuration name specified for volume '%s'. %s line %d",
|
|
||||||
volName.get(), filename, n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nsCString configName(tokenizer.nextToken());
|
|
||||||
if (!tokenizer.hasMoreTokens()) {
|
|
||||||
ERR("No value for configuration name '%s'. %s line %d",
|
|
||||||
configName.get(), filename, n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nsCString configValue(tokenizer.nextToken());
|
|
||||||
RefPtr<Volume> vol = FindVolumeByName(volName);
|
|
||||||
if (vol) {
|
|
||||||
vol->SetConfig(configName, configValue);
|
|
||||||
} else {
|
|
||||||
ERR("Invalid volume name '%s'.", volName.get());
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (command.EqualsLiteral("ignore")) {
|
|
||||||
// This command is useful to remove volumes which are being tracked by
|
|
||||||
// vold, but for which we have no interest.
|
|
||||||
if (!tokenizer.hasMoreTokens()) {
|
|
||||||
ERR("No vol_name in %s line %d", filename, n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nsCString volName(tokenizer.nextToken());
|
|
||||||
RemoveVolumeByName(volName);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ERR("Unrecognized command: '%s'", command.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeManager::DefaultConfig()
|
|
||||||
{
|
|
||||||
|
|
||||||
VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
|
|
||||||
if (numVolumes == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (numVolumes == 1) {
|
|
||||||
// This is to cover early shipping phones like the Buri,
|
|
||||||
// which had no internal storage, and only external sdcard.
|
|
||||||
//
|
|
||||||
// Phones line the nexus-4 which only have an internal
|
|
||||||
// storage area will need to have a volume.cfg file with
|
|
||||||
// removable set to false.
|
|
||||||
RefPtr<Volume> vol = VolumeManager::GetVolume(0);
|
|
||||||
vol->SetIsRemovable(true);
|
|
||||||
vol->SetIsHotSwappable(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VolumeManager::VolumeArray::index_type volIndex;
|
|
||||||
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
|
|
||||||
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
|
|
||||||
if (!vol->Name().EqualsLiteral("sdcard")) {
|
|
||||||
vol->SetIsRemovable(true);
|
|
||||||
vol->SetIsHotSwappable(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VolumeListCallback : public VolumeResponseCallback
|
|
||||||
{
|
|
||||||
virtual void ResponseReceived(const VolumeCommand* aCommand)
|
|
||||||
{
|
|
||||||
switch (ResponseCode()) {
|
|
||||||
case ::ResponseCode::VolumeListResult: {
|
|
||||||
// Each line will look something like:
|
|
||||||
//
|
|
||||||
// sdcard /mnt/sdcard 1
|
|
||||||
//
|
|
||||||
// So for each volume that we get back, we update any volumes that
|
|
||||||
// we have of the same name, or add new ones if they don't exist.
|
|
||||||
nsCWhitespaceTokenizer tokenizer(ResponseStr());
|
|
||||||
nsDependentCSubstring volName(tokenizer.nextToken());
|
|
||||||
RefPtr<Volume> vol = VolumeManager::FindAddVolumeByName(volName);
|
|
||||||
vol->HandleVoldResponse(ResponseCode(), tokenizer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ::ResponseCode::CommandOkay: {
|
|
||||||
// We've received the list of volumes. Now read the Volume.cfg
|
|
||||||
// file to perform customizations, and then tell everybody
|
|
||||||
// that we're ready for business.
|
|
||||||
VolumeManager::DefaultConfig();
|
|
||||||
VolumeManager::InitConfig();
|
|
||||||
VolumeManager::Dump("READY");
|
|
||||||
VolumeManager::SetState(VolumeManager::VOLUMES_READY);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
VolumeManager::OpenSocket()
|
|
||||||
{
|
|
||||||
SetState(STARTING);
|
|
||||||
if ((mSocket.rwget() = socket_local_client("vold",
|
|
||||||
ANDROID_SOCKET_NAMESPACE_RESERVED,
|
|
||||||
SOCK_STREAM)) < 0) {
|
|
||||||
ERR("Error connecting to vold: (%s) - will retry", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// add FD_CLOEXEC flag
|
|
||||||
int flags = fcntl(mSocket.get(), F_GETFD);
|
|
||||||
if (flags == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
flags |= FD_CLOEXEC;
|
|
||||||
if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// set non-blocking
|
|
||||||
if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!MessageLoopForIO::current()->
|
|
||||||
WatchFileDescriptor(mSocket.get(),
|
|
||||||
true,
|
|
||||||
MessageLoopForIO::WATCH_READ,
|
|
||||||
&mReadWatcher,
|
|
||||||
this)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG("Connected to vold");
|
|
||||||
PostCommand(new VolumeListCommand(new VolumeListCallback));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
VolumeManager::PostCommand(VolumeCommand* aCommand)
|
|
||||||
{
|
|
||||||
if (!sVolumeManager) {
|
|
||||||
ERR("VolumeManager not initialized. Dropping command '%s'", aCommand->Data());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
aCommand->SetPending(true);
|
|
||||||
|
|
||||||
DBG("Sending command '%s'", aCommand->Data());
|
|
||||||
// vold can only process one command at a time, so add our command
|
|
||||||
// to the end of the command queue.
|
|
||||||
sVolumeManager->mCommands.push(aCommand);
|
|
||||||
if (!sVolumeManager->mCommandPending) {
|
|
||||||
// There aren't any commands currently being processed, so go
|
|
||||||
// ahead and kick this one off.
|
|
||||||
sVolumeManager->mCommandPending = true;
|
|
||||||
sVolumeManager->WriteCommandData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* The WriteCommandData initiates sending of a command to vold. Since
|
|
||||||
* we're running on the IOThread and not allowed to block, WriteCommandData
|
|
||||||
* will write as much data as it can, and if not all of the data can be
|
|
||||||
* written then it will setup a file descriptor watcher and
|
|
||||||
* OnFileCanWriteWithoutBlocking will call WriteCommandData to write out
|
|
||||||
* more of the command data.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
VolumeManager::WriteCommandData()
|
|
||||||
{
|
|
||||||
if (mCommands.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VolumeCommand* cmd = mCommands.front();
|
|
||||||
if (cmd->BytesRemaining() == 0) {
|
|
||||||
// All bytes have been written. We're waiting for a response.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// There are more bytes left to write. Try to write them all.
|
|
||||||
ssize_t bytesWritten = write(mSocket.get(), cmd->Data(), cmd->BytesRemaining());
|
|
||||||
if (bytesWritten < 0) {
|
|
||||||
ERR("Failed to write %d bytes to vold socket", cmd->BytesRemaining());
|
|
||||||
Restart();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DBG("Wrote %d bytes (of %d)", bytesWritten, cmd->BytesRemaining());
|
|
||||||
cmd->ConsumeBytes(bytesWritten);
|
|
||||||
if (cmd->BytesRemaining() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We were unable to write all of the command bytes. Setup a watcher
|
|
||||||
// so we'll get called again when we can write without blocking.
|
|
||||||
if (!MessageLoopForIO::current()->
|
|
||||||
WatchFileDescriptor(mSocket.get(),
|
|
||||||
false, // one-shot
|
|
||||||
MessageLoopForIO::WATCH_WRITE,
|
|
||||||
&mWriteWatcher,
|
|
||||||
this)) {
|
|
||||||
ERR("Failed to setup write watcher for vold socket");
|
|
||||||
Restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeManager::OnLineRead(int aFd, nsDependentCSubstring& aMessage)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aFd == mSocket.get());
|
|
||||||
char* endPtr;
|
|
||||||
int responseCode = strtol(aMessage.Data(), &endPtr, 10);
|
|
||||||
if (*endPtr == ' ') {
|
|
||||||
endPtr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now fish out the rest of the line after the response code
|
|
||||||
nsDependentCString responseLine(endPtr, aMessage.Length() - (endPtr - aMessage.Data()));
|
|
||||||
DBG("Rcvd: %d '%s'", responseCode, responseLine.Data());
|
|
||||||
|
|
||||||
if (responseCode >= ::ResponseCode::UnsolicitedInformational) {
|
|
||||||
// These are unsolicited broadcasts. We intercept these and process
|
|
||||||
// them ourselves
|
|
||||||
HandleBroadcast(responseCode, responseLine);
|
|
||||||
} else {
|
|
||||||
// Everything else is considered to be part of the command response.
|
|
||||||
if (mCommands.size() > 0) {
|
|
||||||
VolumeCommand* cmd = mCommands.front();
|
|
||||||
cmd->HandleResponse(responseCode, responseLine);
|
|
||||||
if (responseCode >= ::ResponseCode::CommandOkay) {
|
|
||||||
// That's a terminating response. We can remove the command.
|
|
||||||
mCommands.pop();
|
|
||||||
mCommandPending = false;
|
|
||||||
// Start the next command, if there is one.
|
|
||||||
WriteCommandData();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ERR("Response with no command");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeManager::OnFileCanWriteWithoutBlocking(int aFd)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aFd == mSocket.get());
|
|
||||||
WriteCommandData();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeManager::HandleBroadcast(int aResponseCode, nsCString& aResponseLine)
|
|
||||||
{
|
|
||||||
// Format of the line is something like:
|
|
||||||
//
|
|
||||||
// Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
|
|
||||||
//
|
|
||||||
// So we parse out the volume name and the state after the string " to "
|
|
||||||
nsCWhitespaceTokenizer tokenizer(aResponseLine);
|
|
||||||
tokenizer.nextToken(); // The word "Volume"
|
|
||||||
nsDependentCSubstring volName(tokenizer.nextToken());
|
|
||||||
|
|
||||||
RefPtr<Volume> vol = FindVolumeByName(volName);
|
|
||||||
if (!vol) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
vol->HandleVoldResponse(aResponseCode, tokenizer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeManager::Restart()
|
|
||||||
{
|
|
||||||
mReadWatcher.StopWatchingFileDescriptor();
|
|
||||||
mWriteWatcher.StopWatchingFileDescriptor();
|
|
||||||
|
|
||||||
while (!mCommands.empty()) {
|
|
||||||
mCommands.pop();
|
|
||||||
}
|
|
||||||
mCommandPending = false;
|
|
||||||
mSocket.dispose();
|
|
||||||
Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void
|
|
||||||
VolumeManager::Start()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
if (!sVolumeManager) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetState(STARTING);
|
|
||||||
if (!sVolumeManager->OpenSocket()) {
|
|
||||||
// Socket open failed, try again in a second.
|
|
||||||
MessageLoopForIO::current()->
|
|
||||||
PostDelayedTask(NewRunnableFunction(VolumeManager::Start),
|
|
||||||
1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeManager::OnError()
|
|
||||||
{
|
|
||||||
Restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************************/
|
|
||||||
|
|
||||||
static void
|
|
||||||
InitVolumeManagerIOThread()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
MOZ_ASSERT(!sVolumeManager);
|
|
||||||
|
|
||||||
sVolumeManager = new VolumeManager();
|
|
||||||
VolumeManager::Start();
|
|
||||||
|
|
||||||
InitVolumeServiceTestIOThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
ShutdownVolumeManagerIOThread()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
sVolumeManager = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Public API
|
|
||||||
*
|
|
||||||
* Since the VolumeManager runs in IO Thread context, we need to switch
|
|
||||||
* to IOThread context before we can do anything.
|
|
||||||
*
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
void
|
|
||||||
InitVolumeManager()
|
|
||||||
{
|
|
||||||
XRE_GetIOMessageLoop()->PostTask(
|
|
||||||
NewRunnableFunction(InitVolumeManagerIOThread));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ShutdownVolumeManager()
|
|
||||||
{
|
|
||||||
ShutdownVolumeServiceTest();
|
|
||||||
|
|
||||||
XRE_GetIOMessageLoop()->PostTask(
|
|
||||||
NewRunnableFunction(ShutdownVolumeManagerIOThread));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
@ -1,192 +0,0 @@
|
||||||
/* 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_system_volumemanager_h__
|
|
||||||
#define mozilla_system_volumemanager_h__
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
#include "base/message_loop.h"
|
|
||||||
#include "mozilla/FileUtils.h"
|
|
||||||
#include "mozilla/Observer.h"
|
|
||||||
#include "nsISupportsImpl.h"
|
|
||||||
#include "nsString.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
|
|
||||||
#include "Volume.h"
|
|
||||||
#include "VolumeCommand.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* All of the public API mentioned in this file (unless otherwise
|
|
||||||
* mentioned) must run from the IOThread.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* The VolumeManager class is a front-end for android's vold service.
|
|
||||||
*
|
|
||||||
* Vold uses a unix socket interface and accepts null-terminated string
|
|
||||||
* commands. The following commands were determined by examining the vold
|
|
||||||
* source code:
|
|
||||||
*
|
|
||||||
* volume list
|
|
||||||
* volume mount <volname>
|
|
||||||
* volume unmount <volname> [force]
|
|
||||||
* volume debug [on|off]
|
|
||||||
* volume format <volname>
|
|
||||||
* volume share <volname> <method>
|
|
||||||
* volume unshare <volname> <method>
|
|
||||||
* volume shared <volname> <method>
|
|
||||||
*
|
|
||||||
* <volname> is the name of the volume as used in /system/etc/vold.fstab
|
|
||||||
* <method> is ums
|
|
||||||
*
|
|
||||||
* dump
|
|
||||||
*
|
|
||||||
* share status <method> (Determines if a particular sharing method is available)
|
|
||||||
* (GB only - not available in ICS)
|
|
||||||
*
|
|
||||||
* storage users (??? always crashes vold ???)
|
|
||||||
*
|
|
||||||
* asec list
|
|
||||||
* asec ...lots more...
|
|
||||||
*
|
|
||||||
* obb list
|
|
||||||
* obb ...lots more...
|
|
||||||
*
|
|
||||||
* xwarp enable
|
|
||||||
* xwarp disable
|
|
||||||
* xwarp status
|
|
||||||
*
|
|
||||||
* There is also a command line tool called vdc, which can be used to send
|
|
||||||
* the above commands to vold.
|
|
||||||
*
|
|
||||||
* Currently, only the volume list, share/unshare, and mount/unmount
|
|
||||||
* commands are being used.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
class VolumeManager final : public MessageLoopForIO::LineWatcher
|
|
||||||
{
|
|
||||||
virtual ~VolumeManager();
|
|
||||||
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_REFCOUNTING(VolumeManager)
|
|
||||||
|
|
||||||
typedef nsTArray<RefPtr<Volume>> VolumeArray;
|
|
||||||
|
|
||||||
VolumeManager();
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// State related methods.
|
|
||||||
//
|
|
||||||
// The VolumeManager starts off in the STARTING state. Once a connection
|
|
||||||
// is established with vold, it asks for a list of volumes, and once the
|
|
||||||
// volume list has been received, then the VolumeManager enters the
|
|
||||||
// VOLUMES_READY state.
|
|
||||||
//
|
|
||||||
// If vold crashes, then the VolumeManager will once again enter the
|
|
||||||
// STARTING state and try to reestablish a connection with vold.
|
|
||||||
|
|
||||||
enum STATE
|
|
||||||
{
|
|
||||||
UNINITIALIZED,
|
|
||||||
STARTING,
|
|
||||||
VOLUMES_READY
|
|
||||||
};
|
|
||||||
|
|
||||||
static STATE State();
|
|
||||||
static const char* StateStr(STATE aState);
|
|
||||||
static const char* StateStr() { return StateStr(State()); }
|
|
||||||
|
|
||||||
class StateChangedEvent
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
StateChangedEvent() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef mozilla::Observer<StateChangedEvent> StateObserver;
|
|
||||||
typedef mozilla::ObserverList<StateChangedEvent> StateObserverList;
|
|
||||||
|
|
||||||
static void RegisterStateObserver(StateObserver* aObserver);
|
|
||||||
static void UnregisterStateObserver(StateObserver* aObserver);
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
|
|
||||||
static void Start();
|
|
||||||
static void Dump(const char* aLabel);
|
|
||||||
|
|
||||||
static VolumeArray::size_type NumVolumes();
|
|
||||||
static already_AddRefed<Volume> GetVolume(VolumeArray::index_type aIndex);
|
|
||||||
static already_AddRefed<Volume> FindVolumeByName(const nsCSubstring& aName);
|
|
||||||
static already_AddRefed<Volume> FindAddVolumeByName(const nsCSubstring& aName);
|
|
||||||
static bool RemoveVolumeByName(const nsCSubstring& aName);
|
|
||||||
static void InitConfig();
|
|
||||||
|
|
||||||
static void PostCommand(VolumeCommand* aCommand);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
virtual void OnLineRead(int aFd, nsDependentCSubstring& aMessage);
|
|
||||||
virtual void OnFileCanWriteWithoutBlocking(int aFd);
|
|
||||||
virtual void OnError();
|
|
||||||
|
|
||||||
static void DefaultConfig();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool OpenSocket();
|
|
||||||
|
|
||||||
friend class VolumeListCallback; // Calls SetState
|
|
||||||
|
|
||||||
static void SetState(STATE aNewState);
|
|
||||||
|
|
||||||
void Restart();
|
|
||||||
void WriteCommandData();
|
|
||||||
void HandleBroadcast(int aResponseCode, nsCString& aResponseLine);
|
|
||||||
|
|
||||||
typedef std::queue<RefPtr<VolumeCommand> > CommandQueue;
|
|
||||||
|
|
||||||
static STATE mState;
|
|
||||||
static StateObserverList mStateObserverList;
|
|
||||||
|
|
||||||
static const int kRcvBufSize = 1024;
|
|
||||||
ScopedClose mSocket;
|
|
||||||
VolumeArray mVolumeArray;
|
|
||||||
CommandQueue mCommands;
|
|
||||||
bool mCommandPending;
|
|
||||||
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
|
|
||||||
MessageLoopForIO::FileDescriptorWatcher mWriteWatcher;
|
|
||||||
RefPtr<VolumeResponseCallback> mBroadcastCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* The initialization/shutdown functions do not need to be called from
|
|
||||||
* the IOThread context.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the Volume Manager. On initialization, the VolumeManager will
|
|
||||||
* attempt to connect with vold and collect the list of volumes that vold
|
|
||||||
* knows about.
|
|
||||||
*/
|
|
||||||
void InitVolumeManager();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shuts down the Volume Manager.
|
|
||||||
*/
|
|
||||||
void ShutdownVolumeManager();
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_volumemanager_h__
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
/* 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_system_volumemanagerlog_h__
|
|
||||||
#define mozilla_system_volumemanagerlog_h__
|
|
||||||
|
|
||||||
#undef USE_DEBUG
|
|
||||||
#define USE_DEBUG 0
|
|
||||||
|
|
||||||
#if !defined(VOLUME_MANAGER_LOG_TAG)
|
|
||||||
#define VOLUME_MANAGER_LOG_TAG "VolumeManager"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef LOG
|
|
||||||
#undef ERR
|
|
||||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, VOLUME_MANAGER_LOG_TAG, ## args)
|
|
||||||
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, VOLUME_MANAGER_LOG_TAG, ## args)
|
|
||||||
|
|
||||||
#undef DBG
|
|
||||||
#if USE_DEBUG
|
|
||||||
#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, VOLUME_MANAGER_LOG_TAG, ## args)
|
|
||||||
#else
|
|
||||||
#define DBG(args...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // mozilla_system_volumemanagerlog_h__
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
/* 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 "VolumeServiceIOThread.h"
|
|
||||||
#include "base/message_loop.h"
|
|
||||||
#include "nsVolumeService.h"
|
|
||||||
#include "nsXULAppAPI.h"
|
|
||||||
#include "Volume.h"
|
|
||||||
#include "VolumeManager.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
VolumeServiceIOThread::VolumeServiceIOThread(nsVolumeService* aVolumeService)
|
|
||||||
: mVolumeService(aVolumeService)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
VolumeManager::RegisterStateObserver(this);
|
|
||||||
Volume::RegisterVolumeObserver(this, "VolumeServiceIOThread");
|
|
||||||
UpdateAllVolumes();
|
|
||||||
}
|
|
||||||
|
|
||||||
VolumeServiceIOThread::~VolumeServiceIOThread()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
Volume::UnregisterVolumeObserver(this, "VolumeServiceIOThread");
|
|
||||||
VolumeManager::UnregisterStateObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeServiceIOThread::Notify(Volume* const & aVolume)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
if (VolumeManager::State() != VolumeManager::VOLUMES_READY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mVolumeService->UpdateVolumeIOThread(aVolume);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeServiceIOThread::Notify(const VolumeManager::StateChangedEvent& aEvent)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
UpdateAllVolumes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VolumeServiceIOThread::UpdateAllVolumes()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
if (VolumeManager::State() != VolumeManager::VOLUMES_READY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
|
|
||||||
VolumeManager::VolumeArray::index_type volIndex;
|
|
||||||
|
|
||||||
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
|
|
||||||
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
|
|
||||||
mVolumeService->UpdateVolumeIOThread(vol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static StaticRefPtr<VolumeServiceIOThread> sVolumeServiceIOThread;
|
|
||||||
|
|
||||||
void
|
|
||||||
InitVolumeServiceIOThread(nsVolumeService* const & aVolumeService)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
sVolumeServiceIOThread = new VolumeServiceIOThread(aVolumeService);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ShutdownVolumeServiceIOThread()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
sVolumeServiceIOThread = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
/* 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_system_volumeserviceiothread_h__
|
|
||||||
#define mozilla_system_volumeserviceiothread_h__
|
|
||||||
|
|
||||||
#include "Volume.h"
|
|
||||||
#include "VolumeManager.h"
|
|
||||||
#include "mozilla/RefPtr.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
class nsVolumeService;
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* The nsVolumeServiceIOThread is a companion class to the nsVolumeService
|
|
||||||
* class, but whose methods are called from IOThread.
|
|
||||||
*/
|
|
||||||
class VolumeServiceIOThread : public VolumeManager::StateObserver,
|
|
||||||
public Volume::EventObserver
|
|
||||||
{
|
|
||||||
~VolumeServiceIOThread();
|
|
||||||
|
|
||||||
public:
|
|
||||||
NS_INLINE_DECL_REFCOUNTING(VolumeServiceIOThread)
|
|
||||||
|
|
||||||
VolumeServiceIOThread(nsVolumeService* aVolumeService);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void UpdateAllVolumes();
|
|
||||||
|
|
||||||
virtual void Notify(const VolumeManager::StateChangedEvent& aEvent);
|
|
||||||
virtual void Notify(Volume* const & aVolume);
|
|
||||||
|
|
||||||
RefPtr<nsVolumeService> mVolumeService;
|
|
||||||
};
|
|
||||||
|
|
||||||
void InitVolumeServiceIOThread(nsVolumeService* const & aVolumeService);
|
|
||||||
void ShutdownVolumeServiceIOThread();
|
|
||||||
void FormatVolume(const nsCString& aVolume);
|
|
||||||
void MountVolume(const nsCString& aVolume);
|
|
||||||
void UnmountVolume(const nsCString& aVolume);
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_volumeserviceiothread_h__
|
|
||||||
|
|
@ -1,202 +0,0 @@
|
||||||
/* 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 "VolumeServiceTest.h"
|
|
||||||
|
|
||||||
#include "base/message_loop.h"
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsIObserver.h"
|
|
||||||
#include "nsIObserverService.h"
|
|
||||||
#include "nsServiceManagerUtils.h"
|
|
||||||
#include "nsThreadUtils.h"
|
|
||||||
#include "nsIVolume.h"
|
|
||||||
#include "nsIVolumeService.h"
|
|
||||||
#include "nsIVolumeStat.h"
|
|
||||||
#include "nsXULAppAPI.h"
|
|
||||||
|
|
||||||
#include "mozilla/Services.h"
|
|
||||||
|
|
||||||
#undef VOLUME_MANAGER_LOG_TAG
|
|
||||||
#define VOLUME_MANAGER_LOG_TAG "VolumeServiceTest"
|
|
||||||
#include "VolumeManagerLog.h"
|
|
||||||
|
|
||||||
using namespace mozilla::services;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
#define TEST_NSVOLUME_OBSERVER 0
|
|
||||||
|
|
||||||
#if TEST_NSVOLUME_OBSERVER
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* A test class to verify that the Observer stuff is working properly.
|
|
||||||
*/
|
|
||||||
class VolumeTestObserver : public nsIObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
|
|
||||||
VolumeTestObserver()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
|
||||||
if (!obs) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
|
|
||||||
}
|
|
||||||
~VolumeTestObserver()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
|
||||||
if (!obs) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogVolume(nsIVolume* vol)
|
|
||||||
{
|
|
||||||
nsString volName;
|
|
||||||
nsString mountPoint;
|
|
||||||
int32_t volState;
|
|
||||||
|
|
||||||
vol->GetName(volName);
|
|
||||||
vol->GetMountPoint(mountPoint);
|
|
||||||
vol->GetState(&volState);
|
|
||||||
|
|
||||||
LOG(" Volume: %s MountPoint: %s State: %s",
|
|
||||||
NS_LossyConvertUTF16toASCII(volName).get(),
|
|
||||||
NS_LossyConvertUTF16toASCII(mountPoint).get(),
|
|
||||||
NS_VolumeStateStr(volState));
|
|
||||||
|
|
||||||
nsCOMPtr<nsIVolumeStat> stat;
|
|
||||||
nsresult rv = vol->GetStats(getter_AddRefs(stat));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
int64_t totalBytes;
|
|
||||||
int64_t freeBytes;
|
|
||||||
|
|
||||||
stat->GetTotalBytes(&totalBytes);
|
|
||||||
stat->GetFreeBytes(&freeBytes);
|
|
||||||
|
|
||||||
LOG(" Total Space: %llu Mb Free Bytes: %llu Mb",
|
|
||||||
totalBytes / (1024LL * 1024LL), freeBytes / (1024LL * 1024LL));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOG(" Unable to retrieve stats");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static nsCOMPtr<VolumeTestObserver> sTestObserver;
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(VolumeTestObserver, nsIObserver)
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
VolumeTestObserver::Observe(nsISupports* aSubject,
|
|
||||||
const char* aTopic,
|
|
||||||
const char16_t* aData)
|
|
||||||
{
|
|
||||||
LOG("TestObserver: topic: %s", aTopic);
|
|
||||||
|
|
||||||
if (strcmp(aTopic, NS_VOLUME_STATE_CHANGED) != 0) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
|
|
||||||
if (vol) {
|
|
||||||
LogVolume(vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since this observe method was called then we know that the service
|
|
||||||
// has been initialized so we can do the VolumeService tests.
|
|
||||||
|
|
||||||
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
|
|
||||||
if (!vs) {
|
|
||||||
ERR("do_GetService('%s') failed", NS_VOLUMESERVICE_CONTRACTID);
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult rv = vs->GetVolumeByName(NS_LITERAL_STRING("sdcard"), getter_AddRefs(vol));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
LOG("GetVolumeByName( 'sdcard' ) succeeded (expected)");
|
|
||||||
LogVolume(vol);
|
|
||||||
} else {
|
|
||||||
ERR("GetVolumeByName( 'sdcard' ) failed (unexpected)");
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = vs->GetVolumeByName(NS_LITERAL_STRING("foo"), getter_AddRefs(vol));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
ERR("GetVolumeByName( 'foo' ) succeeded (unexpected)");
|
|
||||||
} else {
|
|
||||||
LOG("GetVolumeByName( 'foo' ) failed (expected)");
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcard"), getter_AddRefs(vol));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
LOG("GetVolumeByPath( '/mnt/sdcard' ) succeeded (expected)");
|
|
||||||
LogVolume(vol);
|
|
||||||
} else {
|
|
||||||
ERR("GetVolumeByPath( '/mnt/sdcard' ) failed (unexpected");
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcard/foo"), getter_AddRefs(vol));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
LOG("GetVolumeByPath( '/mnt/sdcard/foo' ) succeeded (expected)");
|
|
||||||
LogVolume(vol);
|
|
||||||
} else {
|
|
||||||
LOG("GetVolumeByPath( '/mnt/sdcard/foo' ) failed (unexpected)");
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcardfoo"), getter_AddRefs(vol));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
ERR("GetVolumeByPath( '/mnt/sdcardfoo' ) succeeded (unexpected)");
|
|
||||||
} else {
|
|
||||||
LOG("GetVolumeByPath( '/mnt/sdcardfoo' ) failed (expected)");
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
class InitVolumeServiceTestIO : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_IMETHOD Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
DBG("InitVolumeServiceTest called");
|
|
||||||
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
|
|
||||||
if (!vs) {
|
|
||||||
ERR("do_GetService('%s') failed", NS_VOLUMESERVICE_CONTRACTID);
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
sTestObserver = new VolumeTestObserver();
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif // TEST_NSVOLUME_OBSERVER
|
|
||||||
|
|
||||||
void
|
|
||||||
InitVolumeServiceTestIOThread()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
|
||||||
|
|
||||||
#if TEST_NSVOLUME_OBSERVER
|
|
||||||
// Now that the volume manager is initialized we can go
|
|
||||||
// ahead and do our test (on main thread).
|
|
||||||
NS_DispatchToMainThread(new InitVolumeServiceTestIO());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ShutdownVolumeServiceTest()
|
|
||||||
{
|
|
||||||
#if TEST_NSVOLUME_OBSERVER
|
|
||||||
DBG("ShutdownVolumeServiceTestIOThread called");
|
|
||||||
sTestObserver = nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
/* 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_system_volumeservicetest_h__
|
|
||||||
#define mozilla_system_volumeservicetest_h__
|
|
||||||
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
void InitVolumeServiceTestIOThread();
|
|
||||||
void ShutdownVolumeServiceTest();
|
|
||||||
|
|
||||||
} // system
|
|
||||||
} // mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_system_volumeservicetest_h__
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,489 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ANDROID_AUDIOTRACK_H
|
|
||||||
#define ANDROID_AUDIOTRACK_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "IAudioFlinger.h"
|
|
||||||
#include "IAudioTrack.h"
|
|
||||||
#include "AudioSystem.h"
|
|
||||||
|
|
||||||
#include <utils/RefBase.h>
|
|
||||||
#include <utils/Errors.h>
|
|
||||||
#include <binder/IInterface.h>
|
|
||||||
#include <binder/IMemory.h>
|
|
||||||
#include <utils/threads.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class audio_track_cblk_t;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class AudioTrack
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum channel_index {
|
|
||||||
MONO = 0,
|
|
||||||
LEFT = 0,
|
|
||||||
RIGHT = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Events used by AudioTrack callback function (audio_track_cblk_t).
|
|
||||||
*/
|
|
||||||
enum event_type {
|
|
||||||
EVENT_MORE_DATA = 0, // Request to write more data to PCM buffer.
|
|
||||||
EVENT_UNDERRUN = 1, // PCM buffer underrun occured.
|
|
||||||
EVENT_LOOP_END = 2, // Sample loop end was reached; playback restarted from loop start if loop count was not 0.
|
|
||||||
EVENT_MARKER = 3, // Playback head is at the specified marker position (See setMarkerPosition()).
|
|
||||||
EVENT_NEW_POS = 4, // Playback head is at a new position (See setPositionUpdatePeriod()).
|
|
||||||
EVENT_BUFFER_END = 5 // Playback head is at the end of the buffer.
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Create Buffer on the stack and pass it to obtainBuffer()
|
|
||||||
* and releaseBuffer().
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Buffer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum {
|
|
||||||
MUTE = 0x00000001
|
|
||||||
};
|
|
||||||
uint32_t flags;
|
|
||||||
int channelCount;
|
|
||||||
int format;
|
|
||||||
size_t frameCount;
|
|
||||||
size_t size;
|
|
||||||
union {
|
|
||||||
void* raw;
|
|
||||||
short* i16;
|
|
||||||
int8_t* i8;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* As a convenience, if a callback is supplied, a handler thread
|
|
||||||
* is automatically created with the appropriate priority. This thread
|
|
||||||
* invokes the callback when a new buffer becomes availlable or an underrun condition occurs.
|
|
||||||
* Parameters:
|
|
||||||
*
|
|
||||||
* event: type of event notified (see enum AudioTrack::event_type).
|
|
||||||
* user: Pointer to context for use by the callback receiver.
|
|
||||||
* info: Pointer to optional parameter according to event type:
|
|
||||||
* - EVENT_MORE_DATA: pointer to AudioTrack::Buffer struct. The callback must not write
|
|
||||||
* more bytes than indicated by 'size' field and update 'size' if less bytes are
|
|
||||||
* written.
|
|
||||||
* - EVENT_UNDERRUN: unused.
|
|
||||||
* - EVENT_LOOP_END: pointer to an int indicating the number of loops remaining.
|
|
||||||
* - EVENT_MARKER: pointer to an uin32_t containing the marker position in frames.
|
|
||||||
* - EVENT_NEW_POS: pointer to an uin32_t containing the new position in frames.
|
|
||||||
* - EVENT_BUFFER_END: unused.
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef void (*callback_t)(int event, void* user, void *info);
|
|
||||||
|
|
||||||
/* Returns the minimum frame count required for the successful creation of
|
|
||||||
* an AudioTrack object.
|
|
||||||
* Returned status (from utils/Errors.h) can be:
|
|
||||||
* - NO_ERROR: successful operation
|
|
||||||
* - NO_INIT: audio server or audio hardware not initialized
|
|
||||||
*/
|
|
||||||
|
|
||||||
static status_t getMinFrameCount(int* frameCount,
|
|
||||||
int streamType =-1,
|
|
||||||
uint32_t sampleRate = 0);
|
|
||||||
|
|
||||||
/* Constructs an uninitialized AudioTrack. No connection with
|
|
||||||
* AudioFlinger takes place.
|
|
||||||
*/
|
|
||||||
AudioTrack();
|
|
||||||
|
|
||||||
/* Creates an audio track and registers it with AudioFlinger.
|
|
||||||
* Once created, the track needs to be started before it can be used.
|
|
||||||
* Unspecified values are set to the audio hardware's current
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
*
|
|
||||||
* streamType: Select the type of audio stream this track is attached to
|
|
||||||
* (e.g. AudioSystem::MUSIC).
|
|
||||||
* sampleRate: Track sampling rate in Hz.
|
|
||||||
* format: Audio format (e.g AudioSystem::PCM_16_BIT for signed
|
|
||||||
* 16 bits per sample).
|
|
||||||
* channels: Channel mask: see AudioSystem::audio_channels.
|
|
||||||
* frameCount: Total size of track PCM buffer in frames. This defines the
|
|
||||||
* latency of the track.
|
|
||||||
* flags: Reserved for future use.
|
|
||||||
* cbf: Callback function. If not null, this function is called periodically
|
|
||||||
* to request new PCM data.
|
|
||||||
* notificationFrames: The callback function is called each time notificationFrames PCM
|
|
||||||
* frames have been comsumed from track input buffer.
|
|
||||||
* user Context for use by the callback receiver.
|
|
||||||
*/
|
|
||||||
|
|
||||||
AudioTrack( int streamType,
|
|
||||||
uint32_t sampleRate = 0,
|
|
||||||
int format = 0,
|
|
||||||
int channels = 0,
|
|
||||||
int frameCount = 0,
|
|
||||||
uint32_t flags = 0,
|
|
||||||
callback_t cbf = 0,
|
|
||||||
void* user = 0,
|
|
||||||
int notificationFrames = 0,
|
|
||||||
int sessionId = 0);
|
|
||||||
|
|
||||||
/* Creates an audio track and registers it with AudioFlinger. With this constructor,
|
|
||||||
* The PCM data to be rendered by AudioTrack is passed in a shared memory buffer
|
|
||||||
* identified by the argument sharedBuffer. This prototype is for static buffer playback.
|
|
||||||
* PCM data must be present into memory before the AudioTrack is started.
|
|
||||||
* The Write() and Flush() methods are not supported in this case.
|
|
||||||
* It is recommented to pass a callback function to be notified of playback end by an
|
|
||||||
* EVENT_UNDERRUN event.
|
|
||||||
*/
|
|
||||||
|
|
||||||
AudioTrack( int streamType,
|
|
||||||
uint32_t sampleRate = 0,
|
|
||||||
int format = 0,
|
|
||||||
int channels = 0,
|
|
||||||
const sp<IMemory>& sharedBuffer = 0,
|
|
||||||
uint32_t flags = 0,
|
|
||||||
callback_t cbf = 0,
|
|
||||||
void* user = 0,
|
|
||||||
int notificationFrames = 0,
|
|
||||||
int sessionId = 0);
|
|
||||||
|
|
||||||
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
|
|
||||||
* Also destroys all resources assotiated with the AudioTrack.
|
|
||||||
*/
|
|
||||||
~AudioTrack();
|
|
||||||
|
|
||||||
|
|
||||||
/* Initialize an uninitialized AudioTrack.
|
|
||||||
* Returned status (from utils/Errors.h) can be:
|
|
||||||
* - NO_ERROR: successful intialization
|
|
||||||
* - INVALID_OPERATION: AudioTrack is already intitialized
|
|
||||||
* - BAD_VALUE: invalid parameter (channels, format, sampleRate...)
|
|
||||||
* - NO_INIT: audio server or audio hardware not initialized
|
|
||||||
* */
|
|
||||||
status_t set(int streamType =-1,
|
|
||||||
uint32_t sampleRate = 0,
|
|
||||||
int format = 0,
|
|
||||||
int channels = 0,
|
|
||||||
int frameCount = 0,
|
|
||||||
uint32_t flags = 0,
|
|
||||||
callback_t cbf = 0,
|
|
||||||
void* user = 0,
|
|
||||||
int notificationFrames = 0,
|
|
||||||
const sp<IMemory>& sharedBuffer = 0,
|
|
||||||
bool threadCanCallJava = false,
|
|
||||||
int sessionId = 0);
|
|
||||||
|
|
||||||
|
|
||||||
/* Result of constructing the AudioTrack. This must be checked
|
|
||||||
* before using any AudioTrack API (except for set()), using
|
|
||||||
* an uninitialized AudioTrack produces undefined results.
|
|
||||||
* See set() method above for possible return codes.
|
|
||||||
*/
|
|
||||||
status_t initCheck() const;
|
|
||||||
|
|
||||||
/* Returns this track's latency in milliseconds.
|
|
||||||
* This includes the latency due to AudioTrack buffer size, AudioMixer (if any)
|
|
||||||
* and audio hardware driver.
|
|
||||||
*/
|
|
||||||
uint32_t latency() const;
|
|
||||||
|
|
||||||
/* getters, see constructor */
|
|
||||||
|
|
||||||
int streamType() const;
|
|
||||||
int format() const;
|
|
||||||
int channelCount() const;
|
|
||||||
uint32_t frameCount() const;
|
|
||||||
int frameSize() const;
|
|
||||||
sp<IMemory>& sharedBuffer();
|
|
||||||
|
|
||||||
|
|
||||||
/* After it's created the track is not active. Call start() to
|
|
||||||
* make it active. If set, the callback will start being called.
|
|
||||||
*/
|
|
||||||
void start();
|
|
||||||
|
|
||||||
/* Stop a track. If set, the callback will cease being called and
|
|
||||||
* obtainBuffer returns STOPPED. Note that obtainBuffer() still works
|
|
||||||
* and will fill up buffers until the pool is exhausted.
|
|
||||||
*/
|
|
||||||
void stop();
|
|
||||||
bool stopped() const;
|
|
||||||
|
|
||||||
/* flush a stopped track. All pending buffers are discarded.
|
|
||||||
* This function has no effect if the track is not stoped.
|
|
||||||
*/
|
|
||||||
void flush();
|
|
||||||
|
|
||||||
/* Pause a track. If set, the callback will cease being called and
|
|
||||||
* obtainBuffer returns STOPPED. Note that obtainBuffer() still works
|
|
||||||
* and will fill up buffers until the pool is exhausted.
|
|
||||||
*/
|
|
||||||
void pause();
|
|
||||||
|
|
||||||
/* mute or unmutes this track.
|
|
||||||
* While mutted, the callback, if set, is still called.
|
|
||||||
*/
|
|
||||||
void mute(bool);
|
|
||||||
bool muted() const;
|
|
||||||
|
|
||||||
|
|
||||||
/* set volume for this track, mostly used for games' sound effects
|
|
||||||
* left and right volumes. Levels must be <= 1.0.
|
|
||||||
*/
|
|
||||||
status_t setVolume(float left, float right);
|
|
||||||
void getVolume(float* left, float* right);
|
|
||||||
|
|
||||||
/* set the send level for this track. An auxiliary effect should be attached
|
|
||||||
* to the track with attachEffect(). Level must be <= 1.0.
|
|
||||||
*/
|
|
||||||
status_t setAuxEffectSendLevel(float level);
|
|
||||||
void getAuxEffectSendLevel(float* level);
|
|
||||||
|
|
||||||
/* set sample rate for this track, mostly used for games' sound effects
|
|
||||||
*/
|
|
||||||
status_t setSampleRate(int sampleRate);
|
|
||||||
uint32_t getSampleRate();
|
|
||||||
|
|
||||||
/* Enables looping and sets the start and end points of looping.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
*
|
|
||||||
* loopStart: loop start expressed as the number of PCM frames played since AudioTrack start.
|
|
||||||
* loopEnd: loop end expressed as the number of PCM frames played since AudioTrack start.
|
|
||||||
* loopCount: number of loops to execute. Calling setLoop() with loopCount == 0 cancels any pending or
|
|
||||||
* active loop. loopCount = -1 means infinite looping.
|
|
||||||
*
|
|
||||||
* For proper operation the following condition must be respected:
|
|
||||||
* (loopEnd-loopStart) <= framecount()
|
|
||||||
*/
|
|
||||||
status_t setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount);
|
|
||||||
status_t getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount);
|
|
||||||
|
|
||||||
|
|
||||||
/* Sets marker position. When playback reaches the number of frames specified, a callback with event
|
|
||||||
* type EVENT_MARKER is called. Calling setMarkerPosition with marker == 0 cancels marker notification
|
|
||||||
* callback.
|
|
||||||
* If the AudioTrack has been opened with no callback function associated, the operation will fail.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
*
|
|
||||||
* marker: marker position expressed in frames.
|
|
||||||
*
|
|
||||||
* Returned status (from utils/Errors.h) can be:
|
|
||||||
* - NO_ERROR: successful operation
|
|
||||||
* - INVALID_OPERATION: the AudioTrack has no callback installed.
|
|
||||||
*/
|
|
||||||
status_t setMarkerPosition(uint32_t marker);
|
|
||||||
status_t getMarkerPosition(uint32_t *marker);
|
|
||||||
|
|
||||||
|
|
||||||
/* Sets position update period. Every time the number of frames specified has been played,
|
|
||||||
* a callback with event type EVENT_NEW_POS is called.
|
|
||||||
* Calling setPositionUpdatePeriod with updatePeriod == 0 cancels new position notification
|
|
||||||
* callback.
|
|
||||||
* If the AudioTrack has been opened with no callback function associated, the operation will fail.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
*
|
|
||||||
* updatePeriod: position update notification period expressed in frames.
|
|
||||||
*
|
|
||||||
* Returned status (from utils/Errors.h) can be:
|
|
||||||
* - NO_ERROR: successful operation
|
|
||||||
* - INVALID_OPERATION: the AudioTrack has no callback installed.
|
|
||||||
*/
|
|
||||||
status_t setPositionUpdatePeriod(uint32_t updatePeriod);
|
|
||||||
status_t getPositionUpdatePeriod(uint32_t *updatePeriod);
|
|
||||||
|
|
||||||
|
|
||||||
/* Sets playback head position within AudioTrack buffer. The new position is specified
|
|
||||||
* in number of frames.
|
|
||||||
* This method must be called with the AudioTrack in paused or stopped state.
|
|
||||||
* Note that the actual position set is <position> modulo the AudioTrack buffer size in frames.
|
|
||||||
* Therefore using this method makes sense only when playing a "static" audio buffer
|
|
||||||
* as opposed to streaming.
|
|
||||||
* The getPosition() method on the other hand returns the total number of frames played since
|
|
||||||
* playback start.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
*
|
|
||||||
* position: New playback head position within AudioTrack buffer.
|
|
||||||
*
|
|
||||||
* Returned status (from utils/Errors.h) can be:
|
|
||||||
* - NO_ERROR: successful operation
|
|
||||||
* - INVALID_OPERATION: the AudioTrack is not stopped.
|
|
||||||
* - BAD_VALUE: The specified position is beyond the number of frames present in AudioTrack buffer
|
|
||||||
*/
|
|
||||||
status_t setPosition(uint32_t position);
|
|
||||||
status_t getPosition(uint32_t *position);
|
|
||||||
|
|
||||||
/* Forces AudioTrack buffer full condition. When playing a static buffer, this method avoids
|
|
||||||
* rewriting the buffer before restarting playback after a stop.
|
|
||||||
* This method must be called with the AudioTrack in paused or stopped state.
|
|
||||||
*
|
|
||||||
* Returned status (from utils/Errors.h) can be:
|
|
||||||
* - NO_ERROR: successful operation
|
|
||||||
* - INVALID_OPERATION: the AudioTrack is not stopped.
|
|
||||||
*/
|
|
||||||
status_t reload();
|
|
||||||
|
|
||||||
/* returns a handle on the audio output used by this AudioTrack.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* none.
|
|
||||||
*
|
|
||||||
* Returned value:
|
|
||||||
* handle on audio hardware output
|
|
||||||
*/
|
|
||||||
audio_io_handle_t getOutput();
|
|
||||||
|
|
||||||
/* returns the unique ID associated to this track.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* none.
|
|
||||||
*
|
|
||||||
* Returned value:
|
|
||||||
* AudioTrack ID.
|
|
||||||
*/
|
|
||||||
int getSessionId();
|
|
||||||
|
|
||||||
|
|
||||||
/* Attach track auxiliary output to specified effect. Used effectId = 0
|
|
||||||
* to detach track from effect.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
*
|
|
||||||
* effectId: effectId obtained from AudioEffect::id().
|
|
||||||
*
|
|
||||||
* Returned status (from utils/Errors.h) can be:
|
|
||||||
* - NO_ERROR: successful operation
|
|
||||||
* - INVALID_OPERATION: the effect is not an auxiliary effect.
|
|
||||||
* - BAD_VALUE: The specified effect ID is invalid
|
|
||||||
*/
|
|
||||||
status_t attachAuxEffect(int effectId);
|
|
||||||
|
|
||||||
/* obtains a buffer of "frameCount" frames. The buffer must be
|
|
||||||
* filled entirely. If the track is stopped, obtainBuffer() returns
|
|
||||||
* STOPPED instead of NO_ERROR as long as there are buffers availlable,
|
|
||||||
* at which point NO_MORE_BUFFERS is returned.
|
|
||||||
* Buffers will be returned until the pool (buffercount())
|
|
||||||
* is exhausted, at which point obtainBuffer() will either block
|
|
||||||
* or return WOULD_BLOCK depending on the value of the "blocking"
|
|
||||||
* parameter.
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum {
|
|
||||||
NO_MORE_BUFFERS = 0x80000001,
|
|
||||||
STOPPED = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount);
|
|
||||||
void releaseBuffer(Buffer* audioBuffer);
|
|
||||||
|
|
||||||
|
|
||||||
/* As a convenience we provide a write() interface to the audio buffer.
|
|
||||||
* This is implemented on top of lockBuffer/unlockBuffer. For best
|
|
||||||
* performance
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
ssize_t write(const void* buffer, size_t size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Dumps the state of an audio track.
|
|
||||||
*/
|
|
||||||
status_t dump(int fd, const Vector<String16>& args) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* copying audio tracks is not allowed */
|
|
||||||
AudioTrack(const AudioTrack& other);
|
|
||||||
AudioTrack& operator = (const AudioTrack& other);
|
|
||||||
|
|
||||||
/* a small internal class to handle the callback */
|
|
||||||
class AudioTrackThread : public Thread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AudioTrackThread(AudioTrack& receiver, bool bCanCallJava = false);
|
|
||||||
private:
|
|
||||||
friend class AudioTrack;
|
|
||||||
virtual bool threadLoop();
|
|
||||||
virtual status_t readyToRun();
|
|
||||||
virtual void onFirstRef();
|
|
||||||
AudioTrack& mReceiver;
|
|
||||||
Mutex mLock;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool processAudioBuffer(const sp<AudioTrackThread>& thread);
|
|
||||||
status_t createTrack(int streamType,
|
|
||||||
uint32_t sampleRate,
|
|
||||||
int format,
|
|
||||||
int channelCount,
|
|
||||||
int frameCount,
|
|
||||||
uint32_t flags,
|
|
||||||
const sp<IMemory>& sharedBuffer,
|
|
||||||
audio_io_handle_t output,
|
|
||||||
bool enforceFrameCount);
|
|
||||||
|
|
||||||
sp<IAudioTrack> mAudioTrack;
|
|
||||||
sp<IMemory> mCblkMemory;
|
|
||||||
sp<AudioTrackThread> mAudioTrackThread;
|
|
||||||
|
|
||||||
float mVolume[2];
|
|
||||||
float mSendLevel;
|
|
||||||
uint32_t mFrameCount;
|
|
||||||
|
|
||||||
audio_track_cblk_t* mCblk;
|
|
||||||
uint8_t mStreamType;
|
|
||||||
uint8_t mFormat;
|
|
||||||
uint8_t mChannelCount;
|
|
||||||
uint8_t mMuted;
|
|
||||||
uint32_t mChannels;
|
|
||||||
status_t mStatus;
|
|
||||||
uint32_t mLatency;
|
|
||||||
|
|
||||||
volatile int32_t mActive;
|
|
||||||
|
|
||||||
callback_t mCbf;
|
|
||||||
void* mUserData;
|
|
||||||
uint32_t mNotificationFramesReq; // requested number of frames between each notification callback
|
|
||||||
uint32_t mNotificationFramesAct; // actual number of frames between each notification callback
|
|
||||||
sp<IMemory> mSharedBuffer;
|
|
||||||
int mLoopCount;
|
|
||||||
uint32_t mRemainingFrames;
|
|
||||||
uint32_t mMarkerPosition;
|
|
||||||
bool mMarkerReached;
|
|
||||||
uint32_t mNewPosition;
|
|
||||||
uint32_t mUpdatePeriod;
|
|
||||||
uint32_t mFlags;
|
|
||||||
int mSessionId;
|
|
||||||
int mAuxEffectId;
|
|
||||||
uint32_t mPadding[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}; // namespace android
|
|
||||||
|
|
||||||
#endif // ANDROID_AUDIOTRACK_H
|
|
||||||
|
|
@ -1,798 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ANDROID_EFFECTAPI_H_
|
|
||||||
#define ANDROID_EFFECTAPI_H_
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#if __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Effect control interface
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// The effect control interface is exposed by each effect engine implementation. It consists of
|
|
||||||
// a set of functions controlling the configuration, activation and process of the engine.
|
|
||||||
// The functions are grouped in a structure of type effect_interface_s:
|
|
||||||
// struct effect_interface_s {
|
|
||||||
// effect_process_t process;
|
|
||||||
// effect_command_t command;
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
// effect_interface_t: Effect control interface handle.
|
|
||||||
// The effect_interface_t serves two purposes regarding the implementation of the effect engine:
|
|
||||||
// - 1 it is the address of a pointer to an effect_interface_s structure where the functions
|
|
||||||
// of the effect control API for a particular effect are located.
|
|
||||||
// - 2 it is the address of the context of a particular effect instance.
|
|
||||||
// A typical implementation in the effect library would define a structure as follows:
|
|
||||||
// struct effect_module_s {
|
|
||||||
// const struct effect_interface_s *itfe;
|
|
||||||
// effect_config_t config;
|
|
||||||
// effect_context_t context;
|
|
||||||
// }
|
|
||||||
// The implementation of EffectCreate() function would then allocate a structure of this
|
|
||||||
// type and return its address as effect_interface_t
|
|
||||||
typedef struct effect_interface_s **effect_interface_t;
|
|
||||||
|
|
||||||
|
|
||||||
// Effect API version 1.0
|
|
||||||
#define EFFECT_API_VERSION 0x0100 // Format 0xMMmm MM: Major version, mm: minor version
|
|
||||||
|
|
||||||
// Maximum length of character strings in structures defines by this API.
|
|
||||||
#define EFFECT_STRING_LEN_MAX 64
|
|
||||||
|
|
||||||
//
|
|
||||||
//--- Effect descriptor structure effect_descriptor_t
|
|
||||||
//
|
|
||||||
|
|
||||||
// Unique effect ID (can be generated from the following site:
|
|
||||||
// http://www.itu.int/ITU-T/asn1/uuid.html)
|
|
||||||
// This format is used for both "type" and "uuid" fields of the effect descriptor structure.
|
|
||||||
// - When used for effect type and the engine is implementing and effect corresponding to a standard
|
|
||||||
// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
|
|
||||||
// - When used as uuid, it should be a unique UUID for this particular implementation.
|
|
||||||
typedef struct effect_uuid_s {
|
|
||||||
uint32_t timeLow;
|
|
||||||
uint16_t timeMid;
|
|
||||||
uint16_t timeHiAndVersion;
|
|
||||||
uint16_t clockSeq;
|
|
||||||
uint8_t node[6];
|
|
||||||
} effect_uuid_t;
|
|
||||||
|
|
||||||
// NULL UUID definition (matches SL_IID_NULL_)
|
|
||||||
#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \
|
|
||||||
{ 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
|
|
||||||
static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER;
|
|
||||||
const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_;
|
|
||||||
const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210";
|
|
||||||
|
|
||||||
// The effect descriptor contains necessary information to facilitate the enumeration of the effect
|
|
||||||
// engines present in a library.
|
|
||||||
typedef struct effect_descriptor_s {
|
|
||||||
effect_uuid_t type; // UUID of to the OpenSL ES interface implemented by this effect
|
|
||||||
effect_uuid_t uuid; // UUID for this particular implementation
|
|
||||||
uint16_t apiVersion; // Version of the effect API implemented: matches EFFECT_API_VERSION
|
|
||||||
uint32_t flags; // effect engine capabilities/requirements flags (see below)
|
|
||||||
uint16_t cpuLoad; // CPU load indication (see below)
|
|
||||||
uint16_t memoryUsage; // Data Memory usage (see below)
|
|
||||||
char name[EFFECT_STRING_LEN_MAX]; // human readable effect name
|
|
||||||
char implementor[EFFECT_STRING_LEN_MAX]; // human readable effect implementor name
|
|
||||||
} effect_descriptor_t;
|
|
||||||
|
|
||||||
// CPU load and memory usage indication: each effect implementation must provide an indication of
|
|
||||||
// its CPU and memory usage for the audio effect framework to limit the number of effects
|
|
||||||
// instantiated at a given time on a given platform.
|
|
||||||
// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS.
|
|
||||||
// The memory usage is expressed in KB and includes only dynamically allocated memory
|
|
||||||
|
|
||||||
// Definitions for flags field of effect descriptor.
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | description | bits | values
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | connection mode | 0..1 | 0 insert: after track process
|
|
||||||
// | | | 1 auxiliary: connect to track auxiliary
|
|
||||||
// | | | output and use send level
|
|
||||||
// | | | 2 replace: replaces track process function;
|
|
||||||
// | | | must implement SRC, volume and mono to stereo.
|
|
||||||
// | | | 3 reserved
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | insertion preference | 2..4 | 0 none
|
|
||||||
// | | | 1 first of the chain
|
|
||||||
// | | | 2 last of the chain
|
|
||||||
// | | | 3 exclusive (only effect in the insert chain)
|
|
||||||
// | | | 4..7 reserved
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | Volume management | 5..6 | 0 none
|
|
||||||
// | | | 1 implements volume control
|
|
||||||
// | | | 2 requires volume indication
|
|
||||||
// | | | 3 reserved
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | Device indication | 7..8 | 0 none
|
|
||||||
// | | | 1 requires device updates
|
|
||||||
// | | | 2..3 reserved
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | Sample input mode | 9..10 | 0 direct: process() function or EFFECT_CMD_CONFIGURE
|
|
||||||
// | | | command must specify a buffer descriptor
|
|
||||||
// | | | 1 provider: process() function uses the
|
|
||||||
// | | | bufferProvider indicated by the
|
|
||||||
// | | | EFFECT_CMD_CONFIGURE command to request input.
|
|
||||||
// | | | buffers.
|
|
||||||
// | | | 2 both: both input modes are supported
|
|
||||||
// | | | 3 reserved
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | Sample output mode | 11..12 | 0 direct: process() function or EFFECT_CMD_CONFIGURE
|
|
||||||
// | | | command must specify a buffer descriptor
|
|
||||||
// | | | 1 provider: process() function uses the
|
|
||||||
// | | | bufferProvider indicated by the
|
|
||||||
// | | | EFFECT_CMD_CONFIGURE command to request output
|
|
||||||
// | | | buffers.
|
|
||||||
// | | | 2 both: both output modes are supported
|
|
||||||
// | | | 3 reserved
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | Hardware acceleration | 13..15 | 0 No hardware acceleration
|
|
||||||
// | | | 1 non tunneled hw acceleration: the process() function
|
|
||||||
// | | | reads the samples, send them to HW accelerated
|
|
||||||
// | | | effect processor, reads back the processed samples
|
|
||||||
// | | | and returns them to the output buffer.
|
|
||||||
// | | | 2 tunneled hw acceleration: the process() function is
|
|
||||||
// | | | transparent. The effect interface is only used to
|
|
||||||
// | | | control the effect engine. This mode is relevant for
|
|
||||||
// | | | global effects actually applied by the audio
|
|
||||||
// | | | hardware on the output stream.
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
// | Audio Mode indication | 16..17 | 0 none
|
|
||||||
// | | | 1 requires audio mode updates
|
|
||||||
// | | | 2..3 reserved
|
|
||||||
// +---------------------------+-----------+-----------------------------------
|
|
||||||
|
|
||||||
// Insert mode
|
|
||||||
#define EFFECT_FLAG_TYPE_MASK 0x00000003
|
|
||||||
#define EFFECT_FLAG_TYPE_INSERT 0x00000000
|
|
||||||
#define EFFECT_FLAG_TYPE_AUXILIARY 0x00000001
|
|
||||||
#define EFFECT_FLAG_TYPE_REPLACE 0x00000002
|
|
||||||
|
|
||||||
// Insert preference
|
|
||||||
#define EFFECT_FLAG_INSERT_MASK 0x0000001C
|
|
||||||
#define EFFECT_FLAG_INSERT_ANY 0x00000000
|
|
||||||
#define EFFECT_FLAG_INSERT_FIRST 0x00000004
|
|
||||||
#define EFFECT_FLAG_INSERT_LAST 0x00000008
|
|
||||||
#define EFFECT_FLAG_INSERT_EXCLUSIVE 0x0000000C
|
|
||||||
|
|
||||||
|
|
||||||
// Volume control
|
|
||||||
#define EFFECT_FLAG_VOLUME_MASK 0x00000060
|
|
||||||
#define EFFECT_FLAG_VOLUME_CTRL 0x00000020
|
|
||||||
#define EFFECT_FLAG_VOLUME_IND 0x00000040
|
|
||||||
#define EFFECT_FLAG_VOLUME_NONE 0x00000000
|
|
||||||
|
|
||||||
// Device indication
|
|
||||||
#define EFFECT_FLAG_DEVICE_MASK 0x00000180
|
|
||||||
#define EFFECT_FLAG_DEVICE_IND 0x00000080
|
|
||||||
#define EFFECT_FLAG_DEVICE_NONE 0x00000000
|
|
||||||
|
|
||||||
// Sample input modes
|
|
||||||
#define EFFECT_FLAG_INPUT_MASK 0x00000600
|
|
||||||
#define EFFECT_FLAG_INPUT_DIRECT 0x00000000
|
|
||||||
#define EFFECT_FLAG_INPUT_PROVIDER 0x00000200
|
|
||||||
#define EFFECT_FLAG_INPUT_BOTH 0x00000400
|
|
||||||
|
|
||||||
// Sample output modes
|
|
||||||
#define EFFECT_FLAG_OUTPUT_MASK 0x00001800
|
|
||||||
#define EFFECT_FLAG_OUTPUT_DIRECT 0x00000000
|
|
||||||
#define EFFECT_FLAG_OUTPUT_PROVIDER 0x00000800
|
|
||||||
#define EFFECT_FLAG_OUTPUT_BOTH 0x00001000
|
|
||||||
|
|
||||||
// Hardware acceleration mode
|
|
||||||
#define EFFECT_FLAG_HW_ACC_MASK 0x00006000
|
|
||||||
#define EFFECT_FLAG_HW_ACC_SIMPLE 0x00002000
|
|
||||||
#define EFFECT_FLAG_HW_ACC_TUNNEL 0x00004000
|
|
||||||
|
|
||||||
// Audio mode indication
|
|
||||||
#define EFFECT_FLAG_AUDIO_MODE_MASK 0x00018000
|
|
||||||
#define EFFECT_FLAG_AUDIO_MODE_IND 0x00008000
|
|
||||||
#define EFFECT_FLAG_AUDIO_MODE_NONE 0x00000000
|
|
||||||
|
|
||||||
// Forward definition of type audio_buffer_t
|
|
||||||
typedef struct audio_buffer_s audio_buffer_t;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Function: process
|
|
||||||
//
|
|
||||||
// Description: Effect process function. Takes input samples as specified
|
|
||||||
// (count and location) in input buffer descriptor and output processed
|
|
||||||
// samples as specified in output buffer descriptor. If the buffer descriptor
|
|
||||||
// is not specified the function must use either the buffer or the
|
|
||||||
// buffer provider function installed by the EFFECT_CMD_CONFIGURE command.
|
|
||||||
// The effect framework will call the process() function after the EFFECT_CMD_ENABLE
|
|
||||||
// command is received and until the EFFECT_CMD_DISABLE is received. When the engine
|
|
||||||
// receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully
|
|
||||||
// and when done indicate that it is OK to stop calling the process() function by
|
|
||||||
// returning the -ENODATA status.
|
|
||||||
//
|
|
||||||
// NOTE: the process() function implementation should be "real-time safe" that is
|
|
||||||
// it should not perform blocking calls: malloc/free, sleep, read/write/open/close,
|
|
||||||
// pthread_cond_wait/pthread_mutex_lock...
|
|
||||||
//
|
|
||||||
// Input:
|
|
||||||
// effect_interface_t: handle to the effect interface this function
|
|
||||||
// is called on.
|
|
||||||
// inBuffer: buffer descriptor indicating where to read samples to process.
|
|
||||||
// If NULL, use the configuration passed by EFFECT_CMD_CONFIGURE command.
|
|
||||||
//
|
|
||||||
// inBuffer: buffer descriptor indicating where to write processed samples.
|
|
||||||
// If NULL, use the configuration passed by EFFECT_CMD_CONFIGURE command.
|
|
||||||
//
|
|
||||||
// Output:
|
|
||||||
// returned value: 0 successful operation
|
|
||||||
// -ENODATA the engine has finished the disable phase and the framework
|
|
||||||
// can stop calling process()
|
|
||||||
// -EINVAL invalid interface handle or
|
|
||||||
// invalid input/output buffer description
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
typedef int32_t (*effect_process_t)(effect_interface_t self,
|
|
||||||
audio_buffer_t *inBuffer,
|
|
||||||
audio_buffer_t *outBuffer);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Function: command
|
|
||||||
//
|
|
||||||
// Description: Send a command and receive a response to/from effect engine.
|
|
||||||
//
|
|
||||||
// Input:
|
|
||||||
// effect_interface_t: handle to the effect interface this function
|
|
||||||
// is called on.
|
|
||||||
// cmdCode: command code: the command can be a standardized command defined in
|
|
||||||
// effect_command_e (see below) or a proprietary command.
|
|
||||||
// cmdSize: size of command in bytes
|
|
||||||
// pCmdData: pointer to command data
|
|
||||||
// pReplyData: pointer to reply data
|
|
||||||
//
|
|
||||||
// Input/Output:
|
|
||||||
// replySize: maximum size of reply data as input
|
|
||||||
// actual size of reply data as output
|
|
||||||
//
|
|
||||||
// Output:
|
|
||||||
// returned value: 0 successful operation
|
|
||||||
// -EINVAL invalid interface handle or
|
|
||||||
// invalid command/reply size or format according to command code
|
|
||||||
// The return code should be restricted to indicate problems related to the this
|
|
||||||
// API specification. Status related to the execution of a particular command should be
|
|
||||||
// indicated as part of the reply field.
|
|
||||||
//
|
|
||||||
// *pReplyData updated with command response
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
typedef int32_t (*effect_command_t)(effect_interface_t self,
|
|
||||||
uint32_t cmdCode,
|
|
||||||
uint32_t cmdSize,
|
|
||||||
void *pCmdData,
|
|
||||||
uint32_t *replySize,
|
|
||||||
void *pReplyData);
|
|
||||||
|
|
||||||
|
|
||||||
// Effect control interface definition
|
|
||||||
struct effect_interface_s {
|
|
||||||
effect_process_t process;
|
|
||||||
effect_command_t command;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
//--- Standardized command codes for command() function
|
|
||||||
//
|
|
||||||
enum effect_command_e {
|
|
||||||
EFFECT_CMD_INIT, // initialize effect engine
|
|
||||||
EFFECT_CMD_CONFIGURE, // configure effect engine (see effect_config_t)
|
|
||||||
EFFECT_CMD_RESET, // reset effect engine
|
|
||||||
EFFECT_CMD_ENABLE, // enable effect process
|
|
||||||
EFFECT_CMD_DISABLE, // disable effect process
|
|
||||||
EFFECT_CMD_SET_PARAM, // set parameter immediately (see effect_param_t)
|
|
||||||
EFFECT_CMD_SET_PARAM_DEFERRED, // set parameter deferred
|
|
||||||
EFFECT_CMD_SET_PARAM_COMMIT, // commit previous set parameter deferred
|
|
||||||
EFFECT_CMD_GET_PARAM, // get parameter
|
|
||||||
EFFECT_CMD_SET_DEVICE, // set audio device (see audio_device_e)
|
|
||||||
EFFECT_CMD_SET_VOLUME, // set volume
|
|
||||||
EFFECT_CMD_SET_AUDIO_MODE, // set the audio mode (normal, ring, ...)
|
|
||||||
EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
|
|
||||||
};
|
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_INIT
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Initialize effect engine: All configurations return to default
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: sizeof(int)
|
|
||||||
// data: status
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_CONFIGURE
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Apply new audio parameters configurations for input and output buffers
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: sizeof(effect_config_t)
|
|
||||||
// data: effect_config_t
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: sizeof(int)
|
|
||||||
// data: status
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_RESET
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Reset the effect engine. Keep configuration but resets state and buffer content
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_ENABLE
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Enable the process. Called by the framework before the first call to process()
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: sizeof(int)
|
|
||||||
// data: status
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_DISABLE
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Disable the process. Called by the framework after the last call to process()
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: sizeof(int)
|
|
||||||
// data: status
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_SET_PARAM
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Set a parameter and apply it immediately
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: sizeof(effect_param_t) + size of param and value
|
|
||||||
// data: effect_param_t + param + value. See effect_param_t definition below for value offset
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: sizeof(int)
|
|
||||||
// data: status
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_SET_PARAM_DEFERRED
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: sizeof(effect_param_t) + size of param and value
|
|
||||||
// data: effect_param_t + param + value. See effect_param_t definition below for value offset
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_SET_PARAM_COMMIT
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: sizeof(int)
|
|
||||||
// data: status
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_GET_PARAM
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Get a parameter value
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: sizeof(effect_param_t) + size of param
|
|
||||||
// data: effect_param_t + param
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: sizeof(effect_param_t) + size of param and value
|
|
||||||
// data: effect_param_t + param + value. See effect_param_t definition below for value offset
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_SET_DEVICE
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Set the rendering device the audio output path is connected to. See audio_device_e for device
|
|
||||||
// values.
|
|
||||||
// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
|
|
||||||
// command when the device changes
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: sizeof(uint32_t)
|
|
||||||
// data: audio_device_e
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_SET_VOLUME
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Set and get volume. Used by audio framework to delegate volume control to effect engine.
|
|
||||||
// The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in
|
|
||||||
// its descriptor to receive this command before every call to process() function
|
|
||||||
// If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return
|
|
||||||
// the volume that should be applied before the effect is processed. The overall volume (the volume
|
|
||||||
// actually applied by the effect engine multiplied by the returned value) should match the value
|
|
||||||
// indicated in the command.
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: n * sizeof(uint32_t)
|
|
||||||
// data: volume for each channel defined in effect_config_t for output buffer expressed in
|
|
||||||
// 8.24 fixed point format
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: n * sizeof(uint32_t) / 0
|
|
||||||
// data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor:
|
|
||||||
// volume for each channel defined in effect_config_t for output buffer expressed in
|
|
||||||
// 8.24 fixed point format
|
|
||||||
// - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor:
|
|
||||||
// N/A
|
|
||||||
// It is legal to receive a null pointer as pReplyData in which case the effect framework has
|
|
||||||
// delegated volume control to another effect
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_SET_AUDIO_MODE
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its
|
|
||||||
// descriptor to receive this command when the audio mode changes.
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// command format:
|
|
||||||
// size: sizeof(uint32_t)
|
|
||||||
// data: audio_mode_e
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// reply format:
|
|
||||||
// size: 0
|
|
||||||
// data: N/A
|
|
||||||
//==================================================================================================
|
|
||||||
// command: EFFECT_CMD_FIRST_PROPRIETARY
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// description:
|
|
||||||
// All proprietary effect commands must use command codes above this value. The size and format of
|
|
||||||
// command and response fields is free in this case
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
|
|
||||||
// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t
|
|
||||||
// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with
|
|
||||||
// regard to the channel mask definition in audio_channels_e e.g :
|
|
||||||
// Stereo: left, right
|
|
||||||
// 5 point 1: front left, front right, front center, low frequency, back left, back right
|
|
||||||
// The buffer size is expressed in frame count, a frame being composed of samples for all
|
|
||||||
// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by
|
|
||||||
// definition
|
|
||||||
struct audio_buffer_s {
|
|
||||||
size_t frameCount; // number of frames in buffer
|
|
||||||
union {
|
|
||||||
void* raw; // raw pointer to start of buffer
|
|
||||||
int32_t* s32; // pointer to signed 32 bit data at start of buffer
|
|
||||||
int16_t* s16; // pointer to signed 16 bit data at start of buffer
|
|
||||||
uint8_t* u8; // pointer to unsigned 8 bit data at start of buffer
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// The buffer_provider_s structure contains functions that can be used
|
|
||||||
// by the effect engine process() function to query and release input
|
|
||||||
// or output audio buffer.
|
|
||||||
// The getBuffer() function is called to retrieve a buffer where data
|
|
||||||
// should read from or written to by process() function.
|
|
||||||
// The releaseBuffer() function MUST be called when the buffer retrieved
|
|
||||||
// with getBuffer() is not needed anymore.
|
|
||||||
// The process function should use the buffer provider mechanism to retrieve
|
|
||||||
// input or output buffer if the inBuffer or outBuffer passed as argument is NULL
|
|
||||||
// and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_CONFIGURE
|
|
||||||
// command did not specify an audio buffer.
|
|
||||||
|
|
||||||
typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer);
|
|
||||||
|
|
||||||
typedef struct buffer_provider_s {
|
|
||||||
buffer_function_t getBuffer; // retrieve next buffer
|
|
||||||
buffer_function_t releaseBuffer; // release used buffer
|
|
||||||
void *cookie; // for use by client of buffer provider functions
|
|
||||||
} buffer_provider_t;
|
|
||||||
|
|
||||||
|
|
||||||
// The buffer_config_s structure specifies the input or output audio format
|
|
||||||
// to be used by the effect engine. It is part of the effect_config_t
|
|
||||||
// structure that defines both input and output buffer configurations and is
|
|
||||||
// passed by the EFFECT_CMD_CONFIGURE command.
|
|
||||||
typedef struct buffer_config_s {
|
|
||||||
audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly
|
|
||||||
uint32_t samplingRate; // sampling rate
|
|
||||||
uint32_t channels; // channel mask (see audio_channels_e)
|
|
||||||
buffer_provider_t bufferProvider; // buffer provider
|
|
||||||
uint8_t format; // Audio format (see audio_format_e)
|
|
||||||
uint8_t accessMode; // read/write or accumulate in buffer (effect_buffer_access_e)
|
|
||||||
uint16_t mask; // indicates which of the above fields is valid
|
|
||||||
} buffer_config_t;
|
|
||||||
|
|
||||||
// Sample format
|
|
||||||
enum audio_format_e {
|
|
||||||
SAMPLE_FORMAT_PCM_S15, // PCM signed 16 bits
|
|
||||||
SAMPLE_FORMAT_PCM_U8, // PCM unsigned 8 bits
|
|
||||||
SAMPLE_FORMAT_PCM_S7_24, // PCM signed 7.24 fixed point representation
|
|
||||||
SAMPLE_FORMAT_OTHER // other format (e.g. compressed)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Channel mask
|
|
||||||
enum audio_channels_e {
|
|
||||||
CHANNEL_FRONT_LEFT = 0x1, // front left channel
|
|
||||||
CHANNEL_FRONT_RIGHT = 0x2, // front right channel
|
|
||||||
CHANNEL_FRONT_CENTER = 0x4, // front center channel
|
|
||||||
CHANNEL_LOW_FREQUENCY = 0x8, // low frequency channel
|
|
||||||
CHANNEL_BACK_LEFT = 0x10, // back left channel
|
|
||||||
CHANNEL_BACK_RIGHT = 0x20, // back right channel
|
|
||||||
CHANNEL_FRONT_LEFT_OF_CENTER = 0x40, // front left of center channel
|
|
||||||
CHANNEL_FRONT_RIGHT_OF_CENTER = 0x80, // front right of center channel
|
|
||||||
CHANNEL_BACK_CENTER = 0x100, // back center channel
|
|
||||||
CHANNEL_MONO = CHANNEL_FRONT_LEFT,
|
|
||||||
CHANNEL_STEREO = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT),
|
|
||||||
CHANNEL_QUAD = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
|
|
||||||
CHANNEL_SURROUND = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER),
|
|
||||||
CHANNEL_5POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
|
|
||||||
CHANNEL_7POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
|
|
||||||
CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render device
|
|
||||||
enum audio_device_e {
|
|
||||||
DEVICE_EARPIECE = 0x1, // earpiece
|
|
||||||
DEVICE_SPEAKER = 0x2, // speaker
|
|
||||||
DEVICE_WIRED_HEADSET = 0x4, // wired headset, with microphone
|
|
||||||
DEVICE_WIRED_HEADPHONE = 0x8, // wired headphone, without microphone
|
|
||||||
DEVICE_BLUETOOTH_SCO = 0x10, // generic bluetooth SCO
|
|
||||||
DEVICE_BLUETOOTH_SCO_HEADSET = 0x20, // bluetooth SCO headset
|
|
||||||
DEVICE_BLUETOOTH_SCO_CARKIT = 0x40, // bluetooth SCO car kit
|
|
||||||
DEVICE_BLUETOOTH_A2DP = 0x80, // generic bluetooth A2DP
|
|
||||||
DEVICE_BLUETOOTH_A2DP_HEADPHONES = 0x100, // bluetooth A2DP headphones
|
|
||||||
DEVICE_BLUETOOTH_A2DP_SPEAKER = 0x200, // bluetooth A2DP speakers
|
|
||||||
DEVICE_AUX_DIGITAL = 0x400, // digital output
|
|
||||||
DEVICE_EXTERNAL_SPEAKER = 0x800 // external speaker (stereo and High quality)
|
|
||||||
};
|
|
||||||
|
|
||||||
#if ANDROID_VERSION < 17
|
|
||||||
// Audio mode
|
|
||||||
enum audio_mode_e {
|
|
||||||
AUDIO_MODE_NORMAL, // device idle
|
|
||||||
AUDIO_MODE_RINGTONE, // device ringing
|
|
||||||
AUDIO_MODE_IN_CALL // audio call connected (VoIP or telephony)
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Values for "accessMode" field of buffer_config_t:
|
|
||||||
// overwrite, read only, accumulate (read/modify/write)
|
|
||||||
enum effect_buffer_access_e {
|
|
||||||
EFFECT_BUFFER_ACCESS_WRITE,
|
|
||||||
EFFECT_BUFFER_ACCESS_READ,
|
|
||||||
EFFECT_BUFFER_ACCESS_ACCUMULATE
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
|
|
||||||
// in buffer_config_t must be taken into account when executing the EFFECT_CMD_CONFIGURE command
|
|
||||||
#define EFFECT_CONFIG_BUFFER 0x0001 // buffer field must be taken into account
|
|
||||||
#define EFFECT_CONFIG_SMP_RATE 0x0002 // samplingRate field must be taken into account
|
|
||||||
#define EFFECT_CONFIG_CHANNELS 0x0004 // channels field must be taken into account
|
|
||||||
#define EFFECT_CONFIG_FORMAT 0x0008 // format field must be taken into account
|
|
||||||
#define EFFECT_CONFIG_ACC_MODE 0x0010 // accessMode field must be taken into account
|
|
||||||
#define EFFECT_CONFIG_PROVIDER 0x0020 // bufferProvider field must be taken into account
|
|
||||||
#define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \
|
|
||||||
EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \
|
|
||||||
EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER)
|
|
||||||
|
|
||||||
|
|
||||||
// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_CONFIGURE
|
|
||||||
// command to configure audio parameters and buffers for effect engine input and output.
|
|
||||||
typedef struct effect_config_s {
|
|
||||||
buffer_config_t inputCfg;
|
|
||||||
buffer_config_t outputCfg;;
|
|
||||||
} effect_config_t;
|
|
||||||
|
|
||||||
|
|
||||||
// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
|
|
||||||
// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
|
|
||||||
// psize and vsize represent the actual size of parameter and value.
|
|
||||||
//
|
|
||||||
// NOTE: the start of value field inside the data field is always on a 32 bit boundary:
|
|
||||||
//
|
|
||||||
// +-----------+
|
|
||||||
// | status | sizeof(int)
|
|
||||||
// +-----------+
|
|
||||||
// | psize | sizeof(int)
|
|
||||||
// +-----------+
|
|
||||||
// | vsize | sizeof(int)
|
|
||||||
// +-----------+
|
|
||||||
// | | | |
|
|
||||||
// ~ parameter ~ > psize |
|
|
||||||
// | | | > ((psize - 1)/sizeof(int) + 1) * sizeof(int)
|
|
||||||
// +-----------+ |
|
|
||||||
// | padding | |
|
|
||||||
// +-----------+
|
|
||||||
// | | |
|
|
||||||
// ~ value ~ > vsize
|
|
||||||
// | | |
|
|
||||||
// +-----------+
|
|
||||||
|
|
||||||
typedef struct effect_param_s {
|
|
||||||
int32_t status; // Transaction status (unused for command, used for reply)
|
|
||||||
uint32_t psize; // Parameter size
|
|
||||||
uint32_t vsize; // Value size
|
|
||||||
char data[]; // Start of Parameter + Value data
|
|
||||||
} effect_param_t;
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Effect library interface
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// An effect library is required to implement and expose the following functions
|
|
||||||
// to enable effect enumeration and instantiation. The name of these functions must be as
|
|
||||||
// specified here as the effect framework will get the function address with dlsym():
|
|
||||||
//
|
|
||||||
// - effect_QueryNumberEffects_t EffectQueryNumberEffects;
|
|
||||||
// - effect_QueryEffect_t EffectQueryEffect;
|
|
||||||
// - effect_CreateEffect_t EffectCreate;
|
|
||||||
// - effect_ReleaseEffect_t EffectRelease;
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Function: EffectQueryNumberEffects
|
|
||||||
//
|
|
||||||
// Description: Returns the number of different effects exposed by the
|
|
||||||
// library. Each effect must have a unique effect uuid (see
|
|
||||||
// effect_descriptor_t). This function together with EffectQueryEffect()
|
|
||||||
// is used to enumerate all effects present in the library.
|
|
||||||
//
|
|
||||||
// Input/Output:
|
|
||||||
// pNumEffects: address where the number of effects should be returned.
|
|
||||||
//
|
|
||||||
// Output:
|
|
||||||
// returned value: 0 successful operation.
|
|
||||||
// -ENODEV library failed to initialize
|
|
||||||
// -EINVAL invalid pNumEffects
|
|
||||||
// *pNumEffects: updated with number of effects in library
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
typedef int32_t (*effect_QueryNumberEffects_t)(uint32_t *pNumEffects);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Function: EffectQueryEffect
|
|
||||||
//
|
|
||||||
// Description: Returns the descriptor of the effect engine which index is
|
|
||||||
// given as first argument.
|
|
||||||
// See effect_descriptor_t for details on effect descriptors.
|
|
||||||
// This function together with EffectQueryNumberEffects() is used to enumerate all
|
|
||||||
// effects present in the library. The enumeration sequence is:
|
|
||||||
// EffectQueryNumberEffects(&num_effects);
|
|
||||||
// for (i = 0; i < num_effects; i++)
|
|
||||||
// EffectQueryEffect(i,...);
|
|
||||||
//
|
|
||||||
// Input/Output:
|
|
||||||
// index: index of the effect
|
|
||||||
// pDescriptor: address where to return the effect descriptor.
|
|
||||||
//
|
|
||||||
// Output:
|
|
||||||
// returned value: 0 successful operation.
|
|
||||||
// -ENODEV library failed to initialize
|
|
||||||
// -EINVAL invalid pDescriptor or index
|
|
||||||
// -ENOSYS effect list has changed since last execution of
|
|
||||||
// EffectQueryNumberEffects()
|
|
||||||
// -ENOENT no more effect available
|
|
||||||
// *pDescriptor: updated with the effect descriptor.
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
typedef int32_t (*effect_QueryEffect_t)(uint32_t index,
|
|
||||||
effect_descriptor_t *pDescriptor);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Function: EffectCreate
|
|
||||||
//
|
|
||||||
// Description: Creates an effect engine of the specified type and returns an
|
|
||||||
// effect control interface on this engine. The function will allocate the
|
|
||||||
// resources for an instance of the requested effect engine and return
|
|
||||||
// a handle on the effect control interface.
|
|
||||||
//
|
|
||||||
// Input:
|
|
||||||
// uuid: pointer to the effect uuid.
|
|
||||||
// sessionId: audio session to which this effect instance will be attached. All effects
|
|
||||||
// created with the same session ID are connected in series and process the same signal
|
|
||||||
// stream. Knowing that two effects are part of the same effect chain can help the
|
|
||||||
// library implement some kind of optimizations.
|
|
||||||
// ioId: identifies the output or input stream this effect is directed to at audio HAL.
|
|
||||||
// For future use especially with tunneled HW accelerated effects
|
|
||||||
//
|
|
||||||
// Input/Output:
|
|
||||||
// pInterface: address where to return the effect interface.
|
|
||||||
//
|
|
||||||
// Output:
|
|
||||||
// returned value: 0 successful operation.
|
|
||||||
// -ENODEV library failed to initialize
|
|
||||||
// -EINVAL invalid pEffectUuid or pInterface
|
|
||||||
// -ENOENT no effect with this uuid found
|
|
||||||
// *pInterface: updated with the effect interface handle.
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
typedef int32_t (*effect_CreateEffect_t)(effect_uuid_t *uuid,
|
|
||||||
int32_t sessionId,
|
|
||||||
int32_t ioId,
|
|
||||||
effect_interface_t *pInterface);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Function: EffectRelease
|
|
||||||
//
|
|
||||||
// Description: Releases the effect engine whose handle is given as argument.
|
|
||||||
// All resources allocated to this particular instance of the effect are
|
|
||||||
// released.
|
|
||||||
//
|
|
||||||
// Input:
|
|
||||||
// interface: handle on the effect interface to be released.
|
|
||||||
//
|
|
||||||
// Output:
|
|
||||||
// returned value: 0 successful operation.
|
|
||||||
// -ENODEV library failed to initialize
|
|
||||||
// -EINVAL invalid interface handle
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
typedef int32_t (*effect_ReleaseEffect_t)(effect_interface_t interface);
|
|
||||||
|
|
||||||
|
|
||||||
#if __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif /*ANDROID_EFFECTAPI_H_*/
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ANDROID_IAUDIOFLINGER_H
|
|
||||||
#define ANDROID_IAUDIOFLINGER_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <utils/RefBase.h>
|
|
||||||
#include <utils/Errors.h>
|
|
||||||
#include <binder/IInterface.h>
|
|
||||||
#include "IAudioTrack.h"
|
|
||||||
#include "IAudioRecord.h"
|
|
||||||
#include "IAudioFlingerClient.h"
|
|
||||||
#include "EffectApi.h"
|
|
||||||
#include "IEffect.h"
|
|
||||||
#include "IEffectClient.h"
|
|
||||||
#include <utils/String8.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class IAudioFlinger : public IInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DECLARE_META_INTERFACE(AudioFlinger);
|
|
||||||
|
|
||||||
/* create an audio track and registers it with AudioFlinger.
|
|
||||||
* return null if the track cannot be created.
|
|
||||||
*/
|
|
||||||
virtual sp<IAudioTrack> createTrack(
|
|
||||||
pid_t pid,
|
|
||||||
int streamType,
|
|
||||||
uint32_t sampleRate,
|
|
||||||
int format,
|
|
||||||
int channelCount,
|
|
||||||
int frameCount,
|
|
||||||
uint32_t flags,
|
|
||||||
const sp<IMemory>& sharedBuffer,
|
|
||||||
int output,
|
|
||||||
int *sessionId,
|
|
||||||
status_t *status) = 0;
|
|
||||||
|
|
||||||
virtual sp<IAudioRecord> openRecord(
|
|
||||||
pid_t pid,
|
|
||||||
int input,
|
|
||||||
uint32_t sampleRate,
|
|
||||||
int format,
|
|
||||||
int channelCount,
|
|
||||||
int frameCount,
|
|
||||||
uint32_t flags,
|
|
||||||
int *sessionId,
|
|
||||||
status_t *status) = 0;
|
|
||||||
|
|
||||||
/* query the audio hardware state. This state never changes,
|
|
||||||
* and therefore can be cached.
|
|
||||||
*/
|
|
||||||
virtual uint32_t sampleRate(int output) const = 0;
|
|
||||||
virtual int channelCount(int output) const = 0;
|
|
||||||
virtual int format(int output) const = 0;
|
|
||||||
virtual size_t frameCount(int output) const = 0;
|
|
||||||
virtual uint32_t latency(int output) const = 0;
|
|
||||||
|
|
||||||
/* set/get the audio hardware state. This will probably be used by
|
|
||||||
* the preference panel, mostly.
|
|
||||||
*/
|
|
||||||
virtual status_t setMasterVolume(float value) = 0;
|
|
||||||
virtual status_t setMasterMute(bool muted) = 0;
|
|
||||||
|
|
||||||
virtual float masterVolume() const = 0;
|
|
||||||
virtual bool masterMute() const = 0;
|
|
||||||
|
|
||||||
/* set/get stream type state. This will probably be used by
|
|
||||||
* the preference panel, mostly.
|
|
||||||
*/
|
|
||||||
virtual status_t setStreamVolume(int stream, float value, int output) = 0;
|
|
||||||
virtual status_t setStreamMute(int stream, bool muted) = 0;
|
|
||||||
|
|
||||||
virtual float streamVolume(int stream, int output) const = 0;
|
|
||||||
virtual bool streamMute(int stream) const = 0;
|
|
||||||
|
|
||||||
// set audio mode
|
|
||||||
virtual status_t setMode(int mode) = 0;
|
|
||||||
|
|
||||||
// mic mute/state
|
|
||||||
virtual status_t setMicMute(bool state) = 0;
|
|
||||||
virtual bool getMicMute() const = 0;
|
|
||||||
|
|
||||||
// is any track active on this stream?
|
|
||||||
virtual bool isStreamActive(int stream) const = 0;
|
|
||||||
|
|
||||||
virtual status_t setParameters(int ioHandle, const String8& keyValuePairs) = 0;
|
|
||||||
virtual String8 getParameters(int ioHandle, const String8& keys) = 0;
|
|
||||||
|
|
||||||
// register a current process for audio output change notifications
|
|
||||||
virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
|
|
||||||
|
|
||||||
// retrieve the audio recording buffer size
|
|
||||||
virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount) = 0;
|
|
||||||
|
|
||||||
virtual int openOutput(uint32_t *pDevices,
|
|
||||||
uint32_t *pSamplingRate,
|
|
||||||
uint32_t *pFormat,
|
|
||||||
uint32_t *pChannels,
|
|
||||||
uint32_t *pLatencyMs,
|
|
||||||
uint32_t flags) = 0;
|
|
||||||
virtual int openDuplicateOutput(int output1, int output2) = 0;
|
|
||||||
virtual status_t closeOutput(int output) = 0;
|
|
||||||
virtual status_t suspendOutput(int output) = 0;
|
|
||||||
virtual status_t restoreOutput(int output) = 0;
|
|
||||||
|
|
||||||
virtual int openInput(uint32_t *pDevices,
|
|
||||||
uint32_t *pSamplingRate,
|
|
||||||
uint32_t *pFormat,
|
|
||||||
uint32_t *pChannels,
|
|
||||||
uint32_t acoustics) = 0;
|
|
||||||
virtual status_t closeInput(int input) = 0;
|
|
||||||
|
|
||||||
virtual status_t setStreamOutput(uint32_t stream, int output) = 0;
|
|
||||||
|
|
||||||
virtual status_t setVoiceVolume(float volume) = 0;
|
|
||||||
|
|
||||||
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output) = 0;
|
|
||||||
|
|
||||||
virtual unsigned int getInputFramesLost(int ioHandle) = 0;
|
|
||||||
|
|
||||||
virtual int newAudioSessionId() = 0;
|
|
||||||
|
|
||||||
virtual status_t loadEffectLibrary(const char *libPath, int *handle) = 0;
|
|
||||||
|
|
||||||
virtual status_t unloadEffectLibrary(int handle) = 0;
|
|
||||||
|
|
||||||
virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
|
|
||||||
|
|
||||||
virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) = 0;
|
|
||||||
|
|
||||||
virtual status_t getEffectDescriptor(effect_uuid_t *pEffectUUID, effect_descriptor_t *pDescriptor) = 0;
|
|
||||||
|
|
||||||
virtual sp<IEffect> createEffect(pid_t pid,
|
|
||||||
effect_descriptor_t *pDesc,
|
|
||||||
const sp<IEffectClient>& client,
|
|
||||||
int32_t priority,
|
|
||||||
int output,
|
|
||||||
int sessionId,
|
|
||||||
status_t *status,
|
|
||||||
int *id,
|
|
||||||
int *enabled) = 0;
|
|
||||||
|
|
||||||
virtual status_t moveEffects(int session, int srcOutput, int dstOutput) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BnAudioFlinger : public BnInterface<IAudioFlinger>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual status_t onTransact( uint32_t code,
|
|
||||||
const Parcel& data,
|
|
||||||
Parcel* reply,
|
|
||||||
uint32_t flags = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
}; // namespace android
|
|
||||||
|
|
||||||
#endif // ANDROID_IAUDIOFLINGER_H
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ANDROID_IAUDIOFLINGERCLIENT_H
|
|
||||||
#define ANDROID_IAUDIOFLINGERCLIENT_H
|
|
||||||
|
|
||||||
|
|
||||||
#include <utils/RefBase.h>
|
|
||||||
#include <binder/IInterface.h>
|
|
||||||
#include <utils/KeyedVector.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class IAudioFlingerClient : public IInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DECLARE_META_INTERFACE(AudioFlingerClient);
|
|
||||||
|
|
||||||
// Notifies a change of audio input/output configuration.
|
|
||||||
virtual void ioConfigChanged(int event, int ioHandle, void *param2) = 0;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BnAudioFlingerClient : public BnInterface<IAudioFlingerClient>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual status_t onTransact( uint32_t code,
|
|
||||||
const Parcel& data,
|
|
||||||
Parcel* reply,
|
|
||||||
uint32_t flags = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
}; // namespace android
|
|
||||||
|
|
||||||
#endif // ANDROID_IAUDIOFLINGERCLIENT_H
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IAUDIORECORD_H_
|
|
||||||
#define IAUDIORECORD_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <utils/RefBase.h>
|
|
||||||
#include <utils/Errors.h>
|
|
||||||
#include <binder/IInterface.h>
|
|
||||||
#include <binder/IMemory.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class IAudioRecord : public IInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DECLARE_META_INTERFACE(AudioRecord);
|
|
||||||
|
|
||||||
/* After it's created the track is not active. Call start() to
|
|
||||||
* make it active. If set, the callback will start being called.
|
|
||||||
*/
|
|
||||||
virtual status_t start() = 0;
|
|
||||||
|
|
||||||
/* Stop a track. If set, the callback will cease being called and
|
|
||||||
* obtainBuffer will return an error. Buffers that are already released
|
|
||||||
* will be processed, unless flush() is called.
|
|
||||||
*/
|
|
||||||
virtual void stop() = 0;
|
|
||||||
|
|
||||||
/* get this tracks control block */
|
|
||||||
virtual sp<IMemory> getCblk() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BnAudioRecord : public BnInterface<IAudioRecord>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual status_t onTransact( uint32_t code,
|
|
||||||
const Parcel& data,
|
|
||||||
Parcel* reply,
|
|
||||||
uint32_t flags = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
}; // namespace android
|
|
||||||
|
|
||||||
#endif /*IAUDIORECORD_H_*/
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ANDROID_IAUDIOTRACK_H
|
|
||||||
#define ANDROID_IAUDIOTRACK_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <utils/RefBase.h>
|
|
||||||
#include <utils/Errors.h>
|
|
||||||
#include <binder/IInterface.h>
|
|
||||||
#include <binder/IMemory.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class IAudioTrack : public IInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DECLARE_META_INTERFACE(AudioTrack);
|
|
||||||
|
|
||||||
/* After it's created the track is not active. Call start() to
|
|
||||||
* make it active. If set, the callback will start being called.
|
|
||||||
*/
|
|
||||||
virtual status_t start() = 0;
|
|
||||||
|
|
||||||
/* Stop a track. If set, the callback will cease being called and
|
|
||||||
* obtainBuffer will return an error. Buffers that are already released
|
|
||||||
* will be processed, unless flush() is called.
|
|
||||||
*/
|
|
||||||
virtual void stop() = 0;
|
|
||||||
|
|
||||||
/* flush a stopped track. All pending buffers are discarded.
|
|
||||||
* This function has no effect if the track is not stoped.
|
|
||||||
*/
|
|
||||||
virtual void flush() = 0;
|
|
||||||
|
|
||||||
/* mute or unmutes this track.
|
|
||||||
* While mutted, the callback, if set, is still called.
|
|
||||||
*/
|
|
||||||
virtual void mute(bool) = 0;
|
|
||||||
|
|
||||||
/* Pause a track. If set, the callback will cease being called and
|
|
||||||
* obtainBuffer will return an error. Buffers that are already released
|
|
||||||
* will be processed, unless flush() is called.
|
|
||||||
*/
|
|
||||||
virtual void pause() = 0;
|
|
||||||
|
|
||||||
/* Attach track auxiliary output to specified effect. Use effectId = 0
|
|
||||||
* to detach track from effect.
|
|
||||||
*/
|
|
||||||
virtual status_t attachAuxEffect(int effectId) = 0;
|
|
||||||
|
|
||||||
/* get this tracks control block */
|
|
||||||
virtual sp<IMemory> getCblk() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BnAudioTrack : public BnInterface<IAudioTrack>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual status_t onTransact( uint32_t code,
|
|
||||||
const Parcel& data,
|
|
||||||
Parcel* reply,
|
|
||||||
uint32_t flags = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
}; // namespace android
|
|
||||||
|
|
||||||
#endif // ANDROID_IAUDIOTRACK_H
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ANDROID_IEFFECT_H
|
|
||||||
#define ANDROID_IEFFECT_H
|
|
||||||
|
|
||||||
#include <utils/RefBase.h>
|
|
||||||
#include <binder/IInterface.h>
|
|
||||||
#include <binder/Parcel.h>
|
|
||||||
#include <binder/IMemory.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
class IEffect: public IInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DECLARE_META_INTERFACE(Effect);
|
|
||||||
|
|
||||||
virtual status_t enable() = 0;
|
|
||||||
|
|
||||||
virtual status_t disable() = 0;
|
|
||||||
|
|
||||||
virtual status_t command(uint32_t cmdCode,
|
|
||||||
uint32_t cmdSize,
|
|
||||||
void *pCmdData,
|
|
||||||
uint32_t *pReplySize,
|
|
||||||
void *pReplyData) = 0;
|
|
||||||
|
|
||||||
virtual void disconnect() = 0;
|
|
||||||
|
|
||||||
virtual sp<IMemory> getCblk() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BnEffect: public BnInterface<IEffect>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual status_t onTransact( uint32_t code,
|
|
||||||
const Parcel& data,
|
|
||||||
Parcel* reply,
|
|
||||||
uint32_t flags = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
}; // namespace android
|
|
||||||
|
|
||||||
#endif // ANDROID_IEFFECT_H
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ANDROID_IEFFECTCLIENT_H
|
|
||||||
#define ANDROID_IEFFECTCLIENT_H
|
|
||||||
|
|
||||||
#include <utils/RefBase.h>
|
|
||||||
#include <binder/IInterface.h>
|
|
||||||
#include <binder/Parcel.h>
|
|
||||||
#include <binder/IMemory.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
class IEffectClient: public IInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DECLARE_META_INTERFACE(EffectClient);
|
|
||||||
|
|
||||||
virtual void controlStatusChanged(bool controlGranted) = 0;
|
|
||||||
virtual void enableStatusChanged(bool enabled) = 0;
|
|
||||||
virtual void commandExecuted(uint32_t cmdCode,
|
|
||||||
uint32_t cmdSize,
|
|
||||||
void *pCmdData,
|
|
||||||
uint32_t replySize,
|
|
||||||
void *pReplyData) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BnEffectClient: public BnInterface<IEffectClient>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual status_t onTransact( uint32_t code,
|
|
||||||
const Parcel& data,
|
|
||||||
Parcel* reply,
|
|
||||||
uint32_t flags = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
}; // namespace android
|
|
||||||
|
|
||||||
#endif // ANDROID_IEFFECTCLIENT_H
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
|
||||||
# vim: set filetype=python:
|
|
||||||
# Copyright 2013 Mozilla Foundation and Mozilla contributors
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
XPIDL_SOURCES += [
|
|
||||||
'nsIAudioManager.idl',
|
|
||||||
'nsINetworkInterface.idl',
|
|
||||||
'nsINetworkInterfaceListService.idl',
|
|
||||||
'nsINetworkManager.idl',
|
|
||||||
'nsINetworkService.idl',
|
|
||||||
'nsINetworkWorker.idl',
|
|
||||||
'nsISystemWorkerManager.idl',
|
|
||||||
'nsITetheringService.idl',
|
|
||||||
'nsIVolume.idl',
|
|
||||||
'nsIVolumeMountLock.idl',
|
|
||||||
'nsIVolumeService.idl',
|
|
||||||
'nsIVolumeStat.idl',
|
|
||||||
'nsIWorkerHolder.idl',
|
|
||||||
]
|
|
||||||
|
|
||||||
XPIDL_MODULE = 'dom_system_gonk'
|
|
||||||
|
|
||||||
EXPORTS += [
|
|
||||||
'GeolocationUtil.h',
|
|
||||||
'GonkGPSGeolocationProvider.h',
|
|
||||||
'nsVolume.h',
|
|
||||||
'nsVolumeService.h',
|
|
||||||
'SystemProperty.h',
|
|
||||||
]
|
|
||||||
UNIFIED_SOURCES += [
|
|
||||||
'AudioChannelManager.cpp',
|
|
||||||
'AudioManager.cpp',
|
|
||||||
'AutoMounter.cpp',
|
|
||||||
'AutoMounterSetting.cpp',
|
|
||||||
'GeolocationUtil.cpp',
|
|
||||||
'GonkGPSGeolocationProvider.cpp',
|
|
||||||
'MozMtpDatabase.cpp',
|
|
||||||
'MozMtpServer.cpp',
|
|
||||||
'MozMtpStorage.cpp',
|
|
||||||
'NetIdManager.cpp',
|
|
||||||
'NetworkUtils.cpp',
|
|
||||||
'NetworkWorker.cpp',
|
|
||||||
'nsVolume.cpp',
|
|
||||||
'nsVolumeMountLock.cpp',
|
|
||||||
'nsVolumeService.cpp',
|
|
||||||
'nsVolumeStat.cpp',
|
|
||||||
'OpenFileFinder.cpp',
|
|
||||||
'SystemProperty.cpp',
|
|
||||||
'SystemWorkerManager.cpp',
|
|
||||||
'TimeZoneSettingObserver.cpp',
|
|
||||||
'Volume.cpp',
|
|
||||||
'VolumeCommand.cpp',
|
|
||||||
'VolumeManager.cpp',
|
|
||||||
'VolumeServiceIOThread.cpp',
|
|
||||||
'VolumeServiceTest.cpp',
|
|
||||||
]
|
|
||||||
|
|
||||||
if CONFIG['ANDROID_VERSION'] >= '17':
|
|
||||||
LOCAL_INCLUDES += ['%' + '%s/frameworks/av/media/mtp' % CONFIG['ANDROID_SOURCE']]
|
|
||||||
else:
|
|
||||||
LOCAL_INCLUDES += ['%' + '%s/frameworks/base/media/mtp' % CONFIG['ANDROID_SOURCE']]
|
|
||||||
|
|
||||||
if CONFIG['ENABLE_TESTS']:
|
|
||||||
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
|
|
||||||
|
|
||||||
EXTRA_COMPONENTS += [
|
|
||||||
'NetworkInterfaceListService.js',
|
|
||||||
'NetworkInterfaceListService.manifest',
|
|
||||||
'NetworkManager.js',
|
|
||||||
'NetworkManager.manifest',
|
|
||||||
'NetworkService.js',
|
|
||||||
'NetworkService.manifest',
|
|
||||||
'TetheringService.js',
|
|
||||||
'TetheringService.manifest',
|
|
||||||
]
|
|
||||||
EXTRA_JS_MODULES += [
|
|
||||||
'systemlibs.js',
|
|
||||||
]
|
|
||||||
|
|
||||||
include('/ipc/chromium/chromium-config.mozbuild')
|
|
||||||
|
|
||||||
DEFINES['HAVE_ANDROID_OS'] = True
|
|
||||||
|
|
||||||
LOCAL_INCLUDES += [
|
|
||||||
'/dom/base',
|
|
||||||
'/dom/bluetooth/common',
|
|
||||||
'/dom/geolocation',
|
|
||||||
'/dom/wifi',
|
|
||||||
]
|
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
|
||||||
|
|
||||||
FINAL_TARGET_FILES.modules.workers += [
|
|
||||||
'worker_buf.js',
|
|
||||||
]
|
|
||||||
|
|
@ -1,426 +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 "MozStumbler.h"
|
|
||||||
#include "nsDataHashtable.h"
|
|
||||||
#include "nsGeoPosition.h"
|
|
||||||
#include "nsNetCID.h"
|
|
||||||
#include "nsPrintfCString.h"
|
|
||||||
#include "StumblerLogging.h"
|
|
||||||
#include "WriteStumbleOnThread.h"
|
|
||||||
#include "../GeolocationUtil.h"
|
|
||||||
|
|
||||||
#include "nsIInterfaceRequestor.h"
|
|
||||||
#include "nsIInterfaceRequestorUtils.h"
|
|
||||||
#include "nsIMobileConnectionInfo.h"
|
|
||||||
#include "nsIMobileConnectionService.h"
|
|
||||||
#include "nsIMobileCellInfo.h"
|
|
||||||
#include "nsIMobileNetworkInfo.h"
|
|
||||||
#include "nsINetworkInterface.h"
|
|
||||||
#include "nsIRadioInterfaceLayer.h"
|
|
||||||
|
|
||||||
using namespace mozilla;
|
|
||||||
using namespace mozilla::dom;
|
|
||||||
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(StumblerInfo, nsICellInfoListCallback, nsIWifiScanResultsReady)
|
|
||||||
|
|
||||||
class RequestCellInfoEvent : public Runnable {
|
|
||||||
public:
|
|
||||||
RequestCellInfoEvent(StumblerInfo *callback)
|
|
||||||
: mRequestCallback(callback)
|
|
||||||
{}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override {
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
// Get Cell Info
|
|
||||||
nsCOMPtr<nsIMobileConnectionService> service =
|
|
||||||
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
|
|
||||||
|
|
||||||
if (!service) {
|
|
||||||
STUMBLER_ERR("Stumbler-can not get nsIMobileConnectionService \n");
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
nsCOMPtr<nsIMobileConnection> connection;
|
|
||||||
uint32_t numberOfRilServices = 1, cellInfoNum = 0;
|
|
||||||
|
|
||||||
service->GetNumItems(&numberOfRilServices);
|
|
||||||
for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) {
|
|
||||||
service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection));
|
|
||||||
if (!connection) {
|
|
||||||
STUMBLER_ERR("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum);
|
|
||||||
} else {
|
|
||||||
cellInfoNum++;
|
|
||||||
connection->GetCellInfoList(mRequestCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum);
|
|
||||||
|
|
||||||
// Get Wifi AP Info
|
|
||||||
nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1");
|
|
||||||
nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir);
|
|
||||||
if (!wifi) {
|
|
||||||
mRequestCallback->SetWifiInfoResponseReceived();
|
|
||||||
STUMBLER_ERR("Stumbler-can not get nsIWifi interface\n");
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
wifi->GetWifiScanResults(mRequestCallback);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
RefPtr<StumblerInfo> mRequestCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
MozStumble(nsGeoPosition* position)
|
|
||||||
{
|
|
||||||
if (WriteStumbleOnThread::IsFileWaitingForUpload()) {
|
|
||||||
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
|
||||||
MOZ_ASSERT(target);
|
|
||||||
// Knowing that file is waiting to upload, and no collection will take place,
|
|
||||||
// just trigger the thread with an empty string.
|
|
||||||
nsCOMPtr<nsIRunnable> event = new WriteStumbleOnThread(EmptyCString());
|
|
||||||
target->Dispatch(event, NS_DISPATCH_NORMAL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
|
|
||||||
position->GetCoords(getter_AddRefs(coords));
|
|
||||||
if (!coords) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double latitude, longitude;
|
|
||||||
coords->GetLatitude(&latitude);
|
|
||||||
coords->GetLongitude(&longitude);
|
|
||||||
|
|
||||||
const double kMinChangeInMeters = 30;
|
|
||||||
static int64_t lastTime_ms = 0;
|
|
||||||
static double sLastLat = 0;
|
|
||||||
static double sLastLon = 0;
|
|
||||||
double delta = -1.0;
|
|
||||||
int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms;
|
|
||||||
|
|
||||||
if (0 != sLastLon || 0 != sLastLat) {
|
|
||||||
delta = CalculateDeltaInMeter(latitude, longitude, sLastLat, sLastLon);
|
|
||||||
}
|
|
||||||
STUMBLER_DBG("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n",
|
|
||||||
longitude, latitude, timediff, delta);
|
|
||||||
|
|
||||||
// Consecutive GPS locations must be 30 meters and 3 seconds apart
|
|
||||||
if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){
|
|
||||||
lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC);
|
|
||||||
sLastLat = latitude;
|
|
||||||
sLastLon = longitude;
|
|
||||||
RefPtr<StumblerInfo> requestCallback = new StumblerInfo(position);
|
|
||||||
RefPtr<RequestCellInfoEvent> runnable = new RequestCellInfoEvent(requestCallback);
|
|
||||||
NS_DispatchToMainThread(runnable);
|
|
||||||
} else {
|
|
||||||
STUMBLER_DBG("Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StumblerInfo::SetWifiInfoResponseReceived()
|
|
||||||
{
|
|
||||||
mIsWifiInfoResponseReceived = true;
|
|
||||||
|
|
||||||
if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
|
|
||||||
STUMBLER_DBG("Call DumpStumblerInfo from SetWifiInfoResponseReceived\n");
|
|
||||||
DumpStumblerInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StumblerInfo::SetCellInfoResponsesExpected(uint8_t count)
|
|
||||||
{
|
|
||||||
mCellInfoResponsesExpected = count;
|
|
||||||
STUMBLER_DBG("SetCellInfoNum (%d)\n", count);
|
|
||||||
|
|
||||||
if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
|
|
||||||
STUMBLER_DBG("Call DumpStumblerInfo from SetCellInfoResponsesExpected\n");
|
|
||||||
DumpStumblerInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define TEXT_LAT NS_LITERAL_CSTRING("latitude")
|
|
||||||
#define TEXT_LON NS_LITERAL_CSTRING("longitude")
|
|
||||||
#define TEXT_ACC NS_LITERAL_CSTRING("accuracy")
|
|
||||||
#define TEXT_ALT NS_LITERAL_CSTRING("altitude")
|
|
||||||
#define TEXT_ALTACC NS_LITERAL_CSTRING("altitudeAccuracy")
|
|
||||||
#define TEXT_HEAD NS_LITERAL_CSTRING("heading")
|
|
||||||
#define TEXT_SPD NS_LITERAL_CSTRING("speed")
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
StumblerInfo::LocationInfoToString(nsACString& aLocDesc)
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
|
|
||||||
mPosition->GetCoords(getter_AddRefs(coords));
|
|
||||||
if (!coords) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsDataHashtable<nsCStringHashKey, double> info;
|
|
||||||
|
|
||||||
double val;
|
|
||||||
coords->GetLatitude(&val);
|
|
||||||
info.Put(TEXT_LAT, val);
|
|
||||||
coords->GetLongitude(&val);
|
|
||||||
info.Put(TEXT_LON, val);
|
|
||||||
coords->GetAccuracy(&val);
|
|
||||||
info.Put(TEXT_ACC, val);
|
|
||||||
coords->GetAltitude(&val);
|
|
||||||
info.Put(TEXT_ALT, val);
|
|
||||||
coords->GetAltitudeAccuracy(&val);
|
|
||||||
info.Put(TEXT_ALTACC, val);
|
|
||||||
coords->GetHeading(&val);
|
|
||||||
info.Put(TEXT_HEAD, val);
|
|
||||||
coords->GetSpeed(&val);
|
|
||||||
info.Put(TEXT_SPD, val);
|
|
||||||
|
|
||||||
for (auto it = info.Iter(); !it.Done(); it.Next()) {
|
|
||||||
const nsACString& key = it.Key();
|
|
||||||
val = it.UserData();
|
|
||||||
if (!IsNaN(val)) {
|
|
||||||
aLocDesc += nsPrintfCString("\"%s\":%f,", key.BeginReading(), val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aLocDesc += nsPrintfCString("\"timestamp\":%lld,", PR_Now() / PR_USEC_PER_MSEC).get();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TEXT_RADIOTYPE NS_LITERAL_CSTRING("radioType")
|
|
||||||
#define TEXT_MCC NS_LITERAL_CSTRING("mobileCountryCode")
|
|
||||||
#define TEXT_MNC NS_LITERAL_CSTRING("mobileNetworkCode")
|
|
||||||
#define TEXT_LAC NS_LITERAL_CSTRING("locationAreaCode")
|
|
||||||
#define TEXT_CID NS_LITERAL_CSTRING("cellId")
|
|
||||||
#define TEXT_PSC NS_LITERAL_CSTRING("psc")
|
|
||||||
#define TEXT_STRENGTH_ASU NS_LITERAL_CSTRING("asu")
|
|
||||||
#define TEXT_STRENGTH_DBM NS_LITERAL_CSTRING("signalStrength")
|
|
||||||
#define TEXT_REGISTERED NS_LITERAL_CSTRING("serving")
|
|
||||||
#define TEXT_TIMEING_ADVANCE NS_LITERAL_CSTRING("timingAdvance")
|
|
||||||
|
|
||||||
template <class T> void
|
|
||||||
ExtractCommonNonCDMACellInfoItems(nsCOMPtr<T>& cell, nsDataHashtable<nsCStringHashKey, int32_t>& info)
|
|
||||||
{
|
|
||||||
int32_t mcc, mnc, cid, sig;
|
|
||||||
|
|
||||||
cell->GetMcc(&mcc);
|
|
||||||
cell->GetMnc(&mnc);
|
|
||||||
cell->GetCid(&cid);
|
|
||||||
cell->GetSignalStrength(&sig);
|
|
||||||
|
|
||||||
info.Put(TEXT_MCC, mcc);
|
|
||||||
info.Put(TEXT_MNC, mnc);
|
|
||||||
info.Put(TEXT_CID, cid);
|
|
||||||
info.Put(TEXT_STRENGTH_ASU, sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StumblerInfo::CellNetworkInfoToString(nsACString& aCellDesc)
|
|
||||||
{
|
|
||||||
aCellDesc += "\"cellTowers\": [";
|
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < mCellInfo.Length() ; idx++) {
|
|
||||||
const char* radioType = 0;
|
|
||||||
int32_t type;
|
|
||||||
mCellInfo[idx]->GetType(&type);
|
|
||||||
bool registered;
|
|
||||||
mCellInfo[idx]->GetRegistered(®istered);
|
|
||||||
if (idx) {
|
|
||||||
aCellDesc += ",{";
|
|
||||||
} else {
|
|
||||||
aCellDesc += "{";
|
|
||||||
}
|
|
||||||
|
|
||||||
STUMBLER_DBG("type=%d\n", type);
|
|
||||||
|
|
||||||
nsDataHashtable<nsCStringHashKey, int32_t> info;
|
|
||||||
info.Put(TEXT_REGISTERED, registered);
|
|
||||||
|
|
||||||
if(type == nsICellInfo::CELL_INFO_TYPE_GSM) {
|
|
||||||
radioType = "gsm";
|
|
||||||
nsCOMPtr<nsIGsmCellInfo> gsmCellInfo = do_QueryInterface(mCellInfo[idx]);
|
|
||||||
ExtractCommonNonCDMACellInfoItems(gsmCellInfo, info);
|
|
||||||
int32_t lac;
|
|
||||||
gsmCellInfo->GetLac(&lac);
|
|
||||||
info.Put(TEXT_LAC, lac);
|
|
||||||
} else if (type == nsICellInfo::CELL_INFO_TYPE_WCDMA) {
|
|
||||||
radioType = "wcdma";
|
|
||||||
nsCOMPtr<nsIWcdmaCellInfo> wcdmaCellInfo = do_QueryInterface(mCellInfo[idx]);
|
|
||||||
ExtractCommonNonCDMACellInfoItems(wcdmaCellInfo, info);
|
|
||||||
int32_t lac, psc;
|
|
||||||
wcdmaCellInfo->GetLac(&lac);
|
|
||||||
wcdmaCellInfo->GetPsc(&psc);
|
|
||||||
info.Put(TEXT_LAC, lac);
|
|
||||||
info.Put(TEXT_PSC, psc);
|
|
||||||
} else if (type == nsICellInfo::CELL_INFO_TYPE_CDMA) {
|
|
||||||
radioType = "cdma";
|
|
||||||
nsCOMPtr<nsICdmaCellInfo> cdmaCellInfo = do_QueryInterface(mCellInfo[idx]);
|
|
||||||
int32_t mnc, lac, cid, sig;
|
|
||||||
cdmaCellInfo->GetSystemId(&mnc);
|
|
||||||
cdmaCellInfo->GetNetworkId(&lac);
|
|
||||||
cdmaCellInfo->GetBaseStationId(&cid);
|
|
||||||
info.Put(TEXT_MNC, mnc);
|
|
||||||
info.Put(TEXT_LAC, lac);
|
|
||||||
info.Put(TEXT_CID, cid);
|
|
||||||
|
|
||||||
cdmaCellInfo->GetEvdoDbm(&sig);
|
|
||||||
if (sig < 0 || sig == nsICellInfo::UNKNOWN_VALUE) {
|
|
||||||
cdmaCellInfo->GetCdmaDbm(&sig);
|
|
||||||
}
|
|
||||||
if (sig > -1 && sig != nsICellInfo::UNKNOWN_VALUE) {
|
|
||||||
sig *= -1;
|
|
||||||
info.Put(TEXT_STRENGTH_DBM, sig);
|
|
||||||
}
|
|
||||||
} else if (type == nsICellInfo::CELL_INFO_TYPE_LTE) {
|
|
||||||
radioType = "lte";
|
|
||||||
nsCOMPtr<nsILteCellInfo> lteCellInfo = do_QueryInterface(mCellInfo[idx]);
|
|
||||||
ExtractCommonNonCDMACellInfoItems(lteCellInfo, info);
|
|
||||||
int32_t lac, timingAdvance, pcid, rsrp;
|
|
||||||
lteCellInfo->GetTac(&lac);
|
|
||||||
lteCellInfo->GetTimingAdvance(&timingAdvance);
|
|
||||||
lteCellInfo->GetPcid(&pcid);
|
|
||||||
lteCellInfo->GetRsrp(&rsrp);
|
|
||||||
info.Put(TEXT_LAC, lac);
|
|
||||||
info.Put(TEXT_TIMEING_ADVANCE, timingAdvance);
|
|
||||||
info.Put(TEXT_PSC, pcid);
|
|
||||||
if (rsrp != nsICellInfo::UNKNOWN_VALUE) {
|
|
||||||
info.Put(TEXT_STRENGTH_DBM, rsrp * -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aCellDesc += nsPrintfCString("\"%s\":\"%s\"", TEXT_RADIOTYPE.get(), radioType);
|
|
||||||
for (auto it = info.Iter(); !it.Done(); it.Next()) {
|
|
||||||
const nsACString& key = it.Key();
|
|
||||||
int32_t value = it.UserData();
|
|
||||||
if (value != nsICellInfo::UNKNOWN_VALUE) {
|
|
||||||
aCellDesc += nsPrintfCString(",\"%s\":%d", key.BeginReading(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aCellDesc += "}";
|
|
||||||
}
|
|
||||||
aCellDesc += "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StumblerInfo::DumpStumblerInfo()
|
|
||||||
{
|
|
||||||
if (!mIsWifiInfoResponseReceived || mCellInfoResponsesReceived != mCellInfoResponsesExpected) {
|
|
||||||
STUMBLER_DBG("CellInfoReceived=%d (Expected=%d), WifiInfoResponseReceived=%d\n",
|
|
||||||
mCellInfoResponsesReceived, mCellInfoResponsesExpected, mIsWifiInfoResponseReceived);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIsWifiInfoResponseReceived = false;
|
|
||||||
mCellInfoResponsesReceived = 0;
|
|
||||||
|
|
||||||
nsAutoCString desc;
|
|
||||||
nsresult rv = LocationInfoToString(desc);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
STUMBLER_ERR("LocationInfoToString failed, skip this dump");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CellNetworkInfoToString(desc);
|
|
||||||
desc += mWifiDesc;
|
|
||||||
|
|
||||||
STUMBLER_DBG("dispatch write event to thread\n");
|
|
||||||
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
|
||||||
MOZ_ASSERT(target);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> event = new WriteStumbleOnThread(desc);
|
|
||||||
target->Dispatch(event, NS_DISPATCH_NORMAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
StumblerInfo::NotifyGetCellInfoList(uint32_t count, nsICellInfo** aCellInfos)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
STUMBLER_DBG("There are %d cellinfo in the result\n", count);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
|
||||||
mCellInfo.AppendElement(aCellInfos[i]);
|
|
||||||
}
|
|
||||||
mCellInfoResponsesReceived++;
|
|
||||||
DumpStumblerInfo();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP StumblerInfo::NotifyGetCellInfoListFailed(const nsAString& error)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
mCellInfoResponsesReceived++;
|
|
||||||
STUMBLER_ERR("NotifyGetCellInfoListFailedm CellInfoReadyNum=%d, mCellInfoResponsesExpected=%d, mIsWifiInfoResponseReceived=%d",
|
|
||||||
mCellInfoResponsesReceived, mCellInfoResponsesExpected, mIsWifiInfoResponseReceived);
|
|
||||||
DumpStumblerInfo();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
StumblerInfo::Onready(uint32_t count, nsIWifiScanResult** results)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
STUMBLER_DBG("There are %d wifiAPinfo in the result\n",count);
|
|
||||||
|
|
||||||
mWifiDesc += ",\"wifiAccessPoints\": [";
|
|
||||||
bool firstItem = true;
|
|
||||||
for (uint32_t i = 0 ; i < count ; i++) {
|
|
||||||
nsString ssid;
|
|
||||||
results[i]->GetSsid(ssid);
|
|
||||||
if (ssid.IsEmpty()) {
|
|
||||||
STUMBLER_DBG("no ssid, skip this AP\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ssid.Length() >= 6) {
|
|
||||||
if (StringEndsWith(ssid, NS_LITERAL_STRING("_nomap"))) {
|
|
||||||
STUMBLER_DBG("end with _nomap. skip this AP(ssid :%s)\n", ssid.get());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstItem) {
|
|
||||||
mWifiDesc += "{";
|
|
||||||
firstItem = false;
|
|
||||||
} else {
|
|
||||||
mWifiDesc += ",{";
|
|
||||||
}
|
|
||||||
|
|
||||||
// mac address
|
|
||||||
nsString bssid;
|
|
||||||
results[i]->GetBssid(bssid);
|
|
||||||
// 00:00:00:00:00:00 --> 000000000000
|
|
||||||
bssid.StripChars(":");
|
|
||||||
mWifiDesc += "\"macAddress\":\"";
|
|
||||||
mWifiDesc += NS_ConvertUTF16toUTF8(bssid);
|
|
||||||
|
|
||||||
uint32_t signal;
|
|
||||||
results[i]->GetSignalStrength(&signal);
|
|
||||||
mWifiDesc += "\",\"signalStrength\":";
|
|
||||||
mWifiDesc.AppendInt(signal);
|
|
||||||
|
|
||||||
mWifiDesc += "}";
|
|
||||||
}
|
|
||||||
mWifiDesc += "]";
|
|
||||||
|
|
||||||
mIsWifiInfoResponseReceived = true;
|
|
||||||
DumpStumblerInfo();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
StumblerInfo::Onfailure()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
STUMBLER_ERR("GetWifiScanResults Onfailure\n");
|
|
||||||
mIsWifiInfoResponseReceived = true;
|
|
||||||
DumpStumblerInfo();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue