mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 13:58:49 +00:00
@@ -46,17 +46,6 @@ var PointerRelay = { // jshint ignore:line
|
||||
'touchend' : true };
|
||||
break;
|
||||
|
||||
case 'gonk':
|
||||
this._eventsOfInterest = {
|
||||
'touchstart' : true,
|
||||
'touchmove' : true,
|
||||
'touchend' : true,
|
||||
'mousedown' : false,
|
||||
'mousemove' : false,
|
||||
'mouseup': false,
|
||||
'click': false };
|
||||
break;
|
||||
|
||||
default:
|
||||
// Desktop.
|
||||
this._eventsOfInterest = {
|
||||
|
||||
+6
-13
@@ -79,22 +79,15 @@ if os == 'WINNT':
|
||||
MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_BUILD'] else 32,
|
||||
)
|
||||
elif os == 'Android':
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
gyp_vars['build_with_gonk'] = 1
|
||||
gyp_vars['moz_widget_toolkit_gonk'] = 1
|
||||
gyp_vars['opus_complexity'] = 1
|
||||
if int(CONFIG['ANDROID_VERSION']) >= 18:
|
||||
gyp_vars['moz_webrtc_omx'] = 1
|
||||
else:
|
||||
gyp_vars.update(
|
||||
gtest_target_type='executable',
|
||||
moz_webrtc_mediacodec=1,
|
||||
android_toolchain=CONFIG.get('ANDROID_TOOLCHAIN', ''),
|
||||
)
|
||||
gyp_vars.update(
|
||||
gtest_target_type='executable',
|
||||
moz_webrtc_mediacodec=1,
|
||||
android_toolchain=CONFIG.get('ANDROID_TOOLCHAIN', ''),
|
||||
)
|
||||
|
||||
flavors = {
|
||||
'WINNT': 'win',
|
||||
'Android': 'linux' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' else 'android',
|
||||
'Android': 'android',
|
||||
'Linux': 'linux',
|
||||
'Darwin': 'mac' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' else 'ios',
|
||||
'SunOS': 'solaris',
|
||||
|
||||
@@ -181,18 +181,7 @@ LocalDevice.prototype = {
|
||||
* Triggers the |name| setter to persist if needed.
|
||||
*/
|
||||
_generate: function () {
|
||||
if (Services.appinfo.widgetToolkit == "gonk") {
|
||||
// For Firefox OS devices, create one from the device name plus a little
|
||||
// randomness. The goal is just to distinguish devices in an office
|
||||
// environment where many people may have the same device model for
|
||||
// testing purposes (which would otherwise all report the same name).
|
||||
let name = libcutils.property_get("ro.product.device");
|
||||
// Pick a random number from [0, 2^32)
|
||||
let randomID = Math.floor(Math.random() * Math.pow(2, 32));
|
||||
// To hex and zero pad
|
||||
randomID = ("00000000" + randomID.toString(16)).slice(-8);
|
||||
this.name = name + "-" + randomID;
|
||||
} else if (Services.appinfo.widgetToolkit == "android") {
|
||||
if (Services.appinfo.widgetToolkit == "android") {
|
||||
// For Firefox for Android, use the device's model name.
|
||||
// TODO: Bug 1180997: Find the right way to expose an editable name
|
||||
this.name = sysInfo.get("device");
|
||||
|
||||
@@ -462,11 +462,6 @@ LOCAL_INCLUDES += [
|
||||
'/xpcom/ds',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
LOCAL_INCLUDES += [
|
||||
'../system/gonk',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/sctp/datachannel',
|
||||
|
||||
@@ -81,9 +81,6 @@ DriverInfo = (function() {
|
||||
var versionMatch = /Mac OS X (\d+.\d+)/.exec(navigator.userAgent);
|
||||
version = versionMatch ? parseFloat(versionMatch[1]) : null;
|
||||
|
||||
} else if (runtime.widgetToolkit == 'gonk') {
|
||||
os = OS.B2G;
|
||||
|
||||
} else if (navigator.appVersion.indexOf('Android') != -1) {
|
||||
os = OS.ANDROID;
|
||||
// From layout/tools/reftest/reftest.js:
|
||||
|
||||
@@ -153,10 +153,5 @@ LOCAL_INCLUDES += [
|
||||
'/layout/xul/tree/',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/wifi',
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
|
||||
@@ -30,10 +30,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/system/android',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/system/gonk',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/system/mac',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
dupe-manifest =
|
||||
head = xpcshell-head-child-process.js
|
||||
tail =
|
||||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
skip-if = toolkit == 'android'
|
||||
support-files =
|
||||
GlobalObjectsChild.js
|
||||
GlobalObjectsComponent.js
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
dupe-manifest =
|
||||
head = xpcshell-head-parent-process.js
|
||||
tail =
|
||||
skip-if = toolkit == 'gonk'
|
||||
support-files =
|
||||
bug1056939_profile.zip
|
||||
defaultStorageUpgrade_profile.zip
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ if CONFIG['OS_ARCH'] != 'WINNT':
|
||||
|
||||
DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gonk'):
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2'):
|
||||
DEFINES['MOZ_ENABLE_FREETYPE'] = True
|
||||
|
||||
if CONFIG['MOZ_TOOLKIT_SEARCH']:
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 "ISOControl.h"
|
||||
#include "ISOMediaBoxes.h"
|
||||
#include "AMRBox.h"
|
||||
#include "ISOTrackMetadata.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsresult
|
||||
AMRSampleEntry::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
uint32_t box_size;
|
||||
nsresult rv = amr_special_box->Generate(&box_size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
size += box_size;
|
||||
|
||||
*aBoxSize = size;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AMRSampleEntry::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
nsresult rv;
|
||||
rv = AudioSampleEntry::Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = amr_special_box->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AMRSampleEntry::AMRSampleEntry(ISOControl* aControl)
|
||||
: AudioSampleEntry(NS_LITERAL_CSTRING("samr"), aControl)
|
||||
{
|
||||
amr_special_box = new AMRSpecificBox(aControl);
|
||||
MOZ_COUNT_CTOR(AMRSampleEntry);
|
||||
}
|
||||
|
||||
AMRSampleEntry::~AMRSampleEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AMRSampleEntry);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AMRSpecificBox::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
nsresult rv;
|
||||
FragmentBuffer* frag = mControl->GetFragment(Audio_Track);
|
||||
rv = frag->GetCSD(amrDecSpecInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
size += amrDecSpecInfo.Length();
|
||||
*aBoxSize = size;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AMRSpecificBox::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
Box::Write();
|
||||
mControl->Write(amrDecSpecInfo.Elements(), amrDecSpecInfo.Length());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AMRSpecificBox::AMRSpecificBox(ISOControl* aControl)
|
||||
: Box(NS_LITERAL_CSTRING("damr"), aControl)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AMRSpecificBox);
|
||||
}
|
||||
|
||||
AMRSpecificBox::~AMRSpecificBox()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AMRSpecificBox);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 AMRBOX_h_
|
||||
#define AMRBOX_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "MuxerOperation.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ISOControl;
|
||||
|
||||
// 3GPP TS 26.244 6.7 'AMRSpecificBox field for AMRSampleEntry box'
|
||||
// Box type: 'damr'
|
||||
class AMRSpecificBox : public Box {
|
||||
public:
|
||||
// 3GPP members
|
||||
nsTArray<uint8_t> amrDecSpecInfo;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// AMRSpecificBox methods
|
||||
AMRSpecificBox(ISOControl* aControl);
|
||||
~AMRSpecificBox();
|
||||
};
|
||||
|
||||
// 3GPP TS 26.244 6.5 'AMRSampleEntry box'
|
||||
// Box type: 'sawb'
|
||||
class AMRSampleEntry : public AudioSampleEntry {
|
||||
public:
|
||||
// 3GPP members
|
||||
RefPtr<AMRSpecificBox> amr_special_box;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// AMRSampleEntry methods
|
||||
AMRSampleEntry(ISOControl* aControl);
|
||||
~AMRSampleEntry();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // AMRBOX_h_
|
||||
@@ -1,87 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 <climits>
|
||||
#include "ISOControl.h"
|
||||
#include "ISOMediaBoxes.h"
|
||||
#include "AVCBox.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsresult
|
||||
AVCSampleEntry::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
uint32_t avc_box_size = 0;
|
||||
nsresult rv;
|
||||
rv = avcConfigBox->Generate(&avc_box_size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
size += avc_box_size;
|
||||
|
||||
*aBoxSize = size;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AVCSampleEntry::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
nsresult rv;
|
||||
rv = VisualSampleEntry::Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = avcConfigBox->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AVCSampleEntry::AVCSampleEntry(ISOControl* aControl)
|
||||
: VisualSampleEntry(NS_LITERAL_CSTRING("avc1"), aControl)
|
||||
{
|
||||
avcConfigBox = new AVCConfigurationBox(aControl);
|
||||
MOZ_COUNT_CTOR(AVCSampleEntry);
|
||||
}
|
||||
|
||||
AVCSampleEntry::~AVCSampleEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AVCSampleEntry);
|
||||
}
|
||||
|
||||
AVCConfigurationBox::AVCConfigurationBox(ISOControl* aControl)
|
||||
: Box(NS_LITERAL_CSTRING("avcC"), aControl)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AVCConfigurationBox);
|
||||
}
|
||||
|
||||
AVCConfigurationBox::~AVCConfigurationBox()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AVCConfigurationBox);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AVCConfigurationBox::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
nsresult rv;
|
||||
FragmentBuffer* frag = mControl->GetFragment(Video_Track);
|
||||
rv = frag->GetCSD(avcConfig);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
size += avcConfig.Length();
|
||||
*aBoxSize = size;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AVCConfigurationBox::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
Box::Write();
|
||||
|
||||
mControl->Write(avcConfig.Elements(), avcConfig.Length());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 AVCBox_h_
|
||||
#define AVCBox_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "ISOMediaBoxes.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ISOControl;
|
||||
|
||||
// 14496-12 8.5.2.2
|
||||
#define resolution_72_dpi 0x00480000
|
||||
#define video_depth 0x0018
|
||||
|
||||
// 14496-15 5.3.4.1 'Sample description name and format'
|
||||
// Box type: 'avcC'
|
||||
class AVCConfigurationBox : public Box {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
|
||||
// avcConfig is CodecSpecificData from 14496-15 '5.3.4.1 Sample description
|
||||
// name and format.
|
||||
// These data are generated by encoder and we encapsulated the generated
|
||||
// bitstream into box directly.
|
||||
nsTArray<uint8_t> avcConfig;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// AVCConfigurationBox methods
|
||||
AVCConfigurationBox(ISOControl* aControl);
|
||||
~AVCConfigurationBox();
|
||||
};
|
||||
|
||||
// 14496-15 5.3.4.1 'Sample description name and format'
|
||||
// Box type: 'avc1'
|
||||
class AVCSampleEntry : public VisualSampleEntry {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
RefPtr<AVCConfigurationBox> avcConfigBox;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// VisualSampleEntry methods
|
||||
AVCSampleEntry(ISOControl* aControl);
|
||||
~AVCSampleEntry();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // AVCBox_h_
|
||||
@@ -1,84 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 "ISOControl.h"
|
||||
#include "ISOMediaBoxes.h"
|
||||
#include "EVRCBox.h"
|
||||
#include "ISOTrackMetadata.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsresult
|
||||
EVRCSampleEntry::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
uint32_t box_size;
|
||||
nsresult rv = evrc_special_box->Generate(&box_size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
size += box_size;
|
||||
|
||||
*aBoxSize = size;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EVRCSampleEntry::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
nsresult rv;
|
||||
rv = AudioSampleEntry::Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = evrc_special_box->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EVRCSampleEntry::EVRCSampleEntry(ISOControl* aControl)
|
||||
: AudioSampleEntry(NS_LITERAL_CSTRING("sevc"), aControl)
|
||||
{
|
||||
evrc_special_box = new EVRCSpecificBox(aControl);
|
||||
MOZ_COUNT_CTOR(EVRCSampleEntry);
|
||||
}
|
||||
|
||||
EVRCSampleEntry::~EVRCSampleEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(EVRCSampleEntry);
|
||||
}
|
||||
|
||||
nsresult
|
||||
EVRCSpecificBox::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
nsresult rv;
|
||||
FragmentBuffer* frag = mControl->GetFragment(Audio_Track);
|
||||
rv = frag->GetCSD(evrcDecSpecInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
size += evrcDecSpecInfo.Length();
|
||||
*aBoxSize = size;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EVRCSpecificBox::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
Box::Write();
|
||||
mControl->Write(evrcDecSpecInfo.Elements(), evrcDecSpecInfo.Length());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EVRCSpecificBox::EVRCSpecificBox(ISOControl* aControl)
|
||||
: Box(NS_LITERAL_CSTRING("devc"), aControl)
|
||||
{
|
||||
MOZ_COUNT_CTOR(EVRCSpecificBox);
|
||||
}
|
||||
|
||||
EVRCSpecificBox::~EVRCSpecificBox()
|
||||
{
|
||||
MOZ_COUNT_DTOR(EVRCSpecificBox);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 EVRCBOX_h_
|
||||
#define EVRCBOX_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "MuxerOperation.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ISOControl;
|
||||
|
||||
// 3GPP TS 26.244 6.7 'EVRCSpecificBox field for EVRCSampleEntry box'
|
||||
// Box type: 'devc'
|
||||
class EVRCSpecificBox : public Box {
|
||||
public:
|
||||
// 3GPP members
|
||||
nsTArray<uint8_t> evrcDecSpecInfo;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// EVRCSpecificBox methods
|
||||
EVRCSpecificBox(ISOControl* aControl);
|
||||
~EVRCSpecificBox();
|
||||
};
|
||||
|
||||
// 3GPP TS 26.244 6.5 'EVRCSampleEntry box'
|
||||
// Box type: 'sevc'
|
||||
class EVRCSampleEntry : public AudioSampleEntry {
|
||||
public:
|
||||
// 3GPP members
|
||||
RefPtr<EVRCSpecificBox> evrc_special_box;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// EVRCSampleEntry methods
|
||||
EVRCSampleEntry(ISOControl* aControl);
|
||||
~EVRCSampleEntry();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EVRCBOX_h_
|
||||
@@ -1,415 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 <time.h>
|
||||
#include "nsAutoPtr.h"
|
||||
#include "ISOControl.h"
|
||||
#include "ISOMediaBoxes.h"
|
||||
#include "EncodedFrameContainer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// For MP4 creation_time and modification_time offset from January 1, 1904 to
|
||||
// January 1, 1970.
|
||||
#define iso_time_offset 2082844800
|
||||
|
||||
FragmentBuffer::FragmentBuffer(uint32_t aTrackType, uint32_t aFragDuration)
|
||||
: mTrackType(aTrackType)
|
||||
, mFragDuration(aFragDuration)
|
||||
, mMediaStartTime(0)
|
||||
, mFragmentNumber(0)
|
||||
, mLastFrameTimeOfLastFragment(0)
|
||||
, mEOS(false)
|
||||
{
|
||||
mFragArray.AppendElement();
|
||||
MOZ_COUNT_CTOR(FragmentBuffer);
|
||||
}
|
||||
|
||||
FragmentBuffer::~FragmentBuffer()
|
||||
{
|
||||
MOZ_COUNT_DTOR(FragmentBuffer);
|
||||
}
|
||||
|
||||
bool
|
||||
FragmentBuffer::HasEnoughData()
|
||||
{
|
||||
// Audio or video frame is enough to form a moof.
|
||||
return (mFragArray.Length() > 1);
|
||||
}
|
||||
|
||||
nsresult
|
||||
FragmentBuffer::GetCSD(nsTArray<uint8_t>& aCSD)
|
||||
{
|
||||
if (!mCSDFrame) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aCSD.AppendElements(mCSDFrame->GetFrameData().Elements(),
|
||||
mCSDFrame->GetFrameData().Length());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FragmentBuffer::AddFrame(EncodedFrame* aFrame)
|
||||
{
|
||||
// already EOS, it rejects all new data.
|
||||
if (mEOS) {
|
||||
MOZ_ASSERT(0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EncodedFrame::FrameType type = aFrame->GetFrameType();
|
||||
if (type == EncodedFrame::AAC_CSD || type == EncodedFrame::AVC_CSD ||
|
||||
type == EncodedFrame::AMR_AUDIO_CSD || type == EncodedFrame::EVRC_AUDIO_CSD) {
|
||||
mCSDFrame = aFrame;
|
||||
// Use CSD's timestamp as the start time. Encoder should send CSD frame first
|
||||
// and then data frames.
|
||||
mMediaStartTime = aFrame->GetTimeStamp();
|
||||
mFragmentNumber = 1;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// if the timestamp is incorrect, abort it.
|
||||
if (aFrame->GetTimeStamp() < mMediaStartTime) {
|
||||
MOZ_ASSERT(false);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mFragArray.LastElement().AppendElement(aFrame);
|
||||
|
||||
// check if current fragment is reach the fragment duration.
|
||||
if ((aFrame->GetTimeStamp() - mMediaStartTime) >= (mFragDuration * mFragmentNumber)) {
|
||||
mFragArray.AppendElement();
|
||||
mFragmentNumber++;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FragmentBuffer::GetFirstFragment(nsTArray<RefPtr<EncodedFrame>>& aFragment,
|
||||
bool aFlush)
|
||||
{
|
||||
// It should be called only if there is a complete fragment in mFragArray.
|
||||
if (mFragArray.Length() <= 1 && !mEOS) {
|
||||
MOZ_ASSERT(false);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (aFlush) {
|
||||
aFragment.SwapElements(mFragArray.ElementAt(0));
|
||||
mFragArray.RemoveElementAt(0);
|
||||
} else {
|
||||
aFragment.AppendElements(mFragArray.ElementAt(0));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
FragmentBuffer::GetFirstFragmentSampleNumber()
|
||||
{
|
||||
return mFragArray.ElementAt(0).Length();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
FragmentBuffer::GetFirstFragmentSampleSize()
|
||||
{
|
||||
uint32_t size = 0;
|
||||
uint32_t len = mFragArray.ElementAt(0).Length();
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
size += mFragArray.ElementAt(0).ElementAt(i)->GetFrameData().Length();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
ISOControl::ISOControl(uint32_t aMuxingType)
|
||||
: mMuxingType(aMuxingType)
|
||||
, mAudioFragmentBuffer(nullptr)
|
||||
, mVideoFragmentBuffer(nullptr)
|
||||
, mFragNum(0)
|
||||
, mOutputSize(0)
|
||||
, mBitCount(0)
|
||||
, mBit(0)
|
||||
{
|
||||
// Create a data array for first mp4 Box, ftyp.
|
||||
mOutBuffers.SetLength(1);
|
||||
MOZ_COUNT_CTOR(ISOControl);
|
||||
}
|
||||
|
||||
ISOControl::~ISOControl()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ISOControl);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::GetNextTrackID()
|
||||
{
|
||||
return (mMetaArray.Length() + 1);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::GetTrackID(TrackMetadataBase::MetadataKind aKind)
|
||||
{
|
||||
for (uint32_t i = 0; i < mMetaArray.Length(); i++) {
|
||||
if (mMetaArray[i]->GetKind() == aKind) {
|
||||
return (i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Track ID shouldn't be 0. It must be something wrong here.
|
||||
MOZ_ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::SetMetadata(TrackMetadataBase* aTrackMeta)
|
||||
{
|
||||
if (aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AMR ||
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC ||
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
|
||||
mMetaArray.AppendElement(aTrackMeta);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::GetAudioMetadata(RefPtr<AudioTrackMetadata>& aAudMeta)
|
||||
{
|
||||
for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
|
||||
if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
||||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR ||
|
||||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_EVRC) {
|
||||
aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get());
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::GetVideoMetadata(RefPtr<VideoTrackMetadata>& aVidMeta)
|
||||
{
|
||||
for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
|
||||
if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AVC) {
|
||||
aVidMeta = static_cast<VideoTrackMetadata*>(mMetaArray[i].get());
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool
|
||||
ISOControl::HasAudioTrack()
|
||||
{
|
||||
RefPtr<AudioTrackMetadata> audMeta;
|
||||
GetAudioMetadata(audMeta);
|
||||
return audMeta;
|
||||
}
|
||||
|
||||
bool
|
||||
ISOControl::HasVideoTrack()
|
||||
{
|
||||
RefPtr<VideoTrackMetadata> vidMeta;
|
||||
GetVideoMetadata(vidMeta);
|
||||
return vidMeta;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::SetFragment(FragmentBuffer* aFragment)
|
||||
{
|
||||
if (aFragment->GetType() == Audio_Track) {
|
||||
mAudioFragmentBuffer = aFragment;
|
||||
} else {
|
||||
mVideoFragmentBuffer = aFragment;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
FragmentBuffer*
|
||||
ISOControl::GetFragment(uint32_t aType)
|
||||
{
|
||||
if (aType == Audio_Track) {
|
||||
return mAudioFragmentBuffer;
|
||||
} else if (aType == Video_Track){
|
||||
return mVideoFragmentBuffer;
|
||||
}
|
||||
MOZ_ASSERT(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::GetBufs(nsTArray<nsTArray<uint8_t>>* aOutputBufs)
|
||||
{
|
||||
uint32_t len = mOutBuffers.Length();
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
mOutBuffers[i].SwapElements(*aOutputBufs->AppendElement());
|
||||
}
|
||||
return FlushBuf();
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::FlushBuf()
|
||||
{
|
||||
mOutBuffers.SetLength(1);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::WriteAVData(nsTArray<uint8_t>& aArray)
|
||||
{
|
||||
MOZ_ASSERT(!mBitCount);
|
||||
|
||||
uint32_t len = aArray.Length();
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mOutputSize += len;
|
||||
|
||||
// The last element already has data, allocated a new element for pointer
|
||||
// swapping.
|
||||
if (mOutBuffers.LastElement().Length()) {
|
||||
mOutBuffers.AppendElement();
|
||||
}
|
||||
// Swap the video/audio data pointer.
|
||||
mOutBuffers.LastElement().SwapElements(aArray);
|
||||
// Following data could be boxes, so appending a new uint8_t array here.
|
||||
mOutBuffers.AppendElement();
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::WriteBits(uint64_t aBits, size_t aNumBits)
|
||||
{
|
||||
uint8_t output_byte = 0;
|
||||
|
||||
MOZ_ASSERT(aNumBits <= 64);
|
||||
// TODO: rewritten following with bitset?
|
||||
for (size_t i = aNumBits; i > 0; i--) {
|
||||
mBit |= (((aBits >> (i - 1)) & 1) << (8 - ++mBitCount));
|
||||
if (mBitCount == 8) {
|
||||
Write(&mBit, sizeof(uint8_t));
|
||||
mBit = 0;
|
||||
mBitCount = 0;
|
||||
output_byte++;
|
||||
}
|
||||
}
|
||||
return output_byte;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::Write(uint8_t* aBuf, uint32_t aSize)
|
||||
{
|
||||
mOutBuffers.LastElement().AppendElements(aBuf, aSize);
|
||||
mOutputSize += aSize;
|
||||
return aSize;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::Write(uint8_t aData)
|
||||
{
|
||||
MOZ_ASSERT(!mBitCount);
|
||||
Write((uint8_t*)&aData, sizeof(uint8_t));
|
||||
return sizeof(uint8_t);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::GetBufPos()
|
||||
{
|
||||
uint32_t len = mOutBuffers.Length();
|
||||
uint32_t pos = 0;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
pos += mOutBuffers.ElementAt(i).Length();
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::WriteFourCC(const char* aType)
|
||||
{
|
||||
// Bit operation should be aligned to byte before writing any byte data.
|
||||
MOZ_ASSERT(!mBitCount);
|
||||
|
||||
uint32_t size = strlen(aType);
|
||||
if (size == 4) {
|
||||
return Write((uint8_t*)aType, size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::GenerateFtyp()
|
||||
{
|
||||
nsresult rv;
|
||||
uint32_t size;
|
||||
nsAutoPtr<FileTypeBox> type_box(new FileTypeBox(this));
|
||||
rv = type_box->Generate(&size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = type_box->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::GenerateMoov()
|
||||
{
|
||||
nsresult rv;
|
||||
uint32_t size;
|
||||
nsAutoPtr<MovieBox> moov_box(new MovieBox(this));
|
||||
rv = moov_box->Generate(&size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = moov_box->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOControl::GenerateMoof(uint32_t aTrackType)
|
||||
{
|
||||
mFragNum++;
|
||||
|
||||
nsresult rv;
|
||||
uint32_t size;
|
||||
uint64_t first_sample_offset = mOutputSize;
|
||||
nsAutoPtr<MovieFragmentBox> moof_box(new MovieFragmentBox(aTrackType, this));
|
||||
nsAutoPtr<MediaDataBox> mdat_box(new MediaDataBox(aTrackType, this));
|
||||
|
||||
rv = moof_box->Generate(&size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
first_sample_offset += size;
|
||||
rv = mdat_box->Generate(&size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
first_sample_offset += mdat_box->FirstSampleOffsetInMediaDataBox();
|
||||
|
||||
// correct offset info
|
||||
nsTArray<RefPtr<MuxerOperation>> tfhds;
|
||||
rv = moof_box->Find(NS_LITERAL_CSTRING("tfhd"), tfhds);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
uint32_t len = tfhds.Length();
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
TrackFragmentHeaderBox* tfhd = (TrackFragmentHeaderBox*) tfhds.ElementAt(i).get();
|
||||
rv = tfhd->UpdateBaseDataOffset(first_sample_offset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
rv = moof_box->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mdat_box->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ISOControl::GetTime()
|
||||
{
|
||||
return (uint64_t)time(nullptr) + iso_time_offset;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 ISOCOMPOSITOR_H_
|
||||
#define ISOCOMPOSITOR_H_
|
||||
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "ISOTrackMetadata.h"
|
||||
#include "EncodedFrameContainer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class Box;
|
||||
class ISOControl;
|
||||
|
||||
/**
|
||||
* This class collects elementary stream data to form a fragment.
|
||||
* ISOMediaWriter will check if the data is enough; if yes, the corresponding
|
||||
* moof will be created and write to ISOControl.
|
||||
* Each audio and video has its own fragment and only one during the whole
|
||||
* life cycle, when a fragment is formed in ISOControl, Flush() needs to
|
||||
* be called to reset it.
|
||||
*/
|
||||
class FragmentBuffer {
|
||||
public:
|
||||
// aTrackType: it could be Audio_Track or Video_Track.
|
||||
// aFragDuration: it is the fragment duration. (microsecond per unit)
|
||||
// Audio and video have the same fragment duration.
|
||||
FragmentBuffer(uint32_t aTrackType, uint32_t aFragDuration);
|
||||
~FragmentBuffer();
|
||||
|
||||
// Get samples of first fragment, that will swap all the elements in the
|
||||
// mFragArray[0] when aFlush = true, and caller is responsible for drop
|
||||
// EncodedFrame reference count.
|
||||
nsresult GetFirstFragment(nsTArray<RefPtr<EncodedFrame>>& aFragment,
|
||||
bool aFlush = false);
|
||||
|
||||
// Add sample frame to the last element fragment of mFragArray. If sample
|
||||
// number is enough, it will append a new fragment element. And the new
|
||||
// sample will be added to the new fragment element of mFragArray.
|
||||
nsresult AddFrame(EncodedFrame* aFrame);
|
||||
|
||||
// Get total sample size of first complete fragment size.
|
||||
uint32_t GetFirstFragmentSampleSize();
|
||||
|
||||
// Get sample number of first complete fragment.
|
||||
uint32_t GetFirstFragmentSampleNumber();
|
||||
|
||||
// Check if it accumulates enough frame data.
|
||||
// It returns true when data is enough to form a fragment.
|
||||
bool HasEnoughData();
|
||||
|
||||
// Called by ISOMediaWriter when TrackEncoder has sent the last frame. The
|
||||
// remains frame data will form the last moof and move the state machine to
|
||||
// in ISOMediaWriter to last phrase.
|
||||
nsresult SetEndOfStream() {
|
||||
mEOS = true;
|
||||
return NS_OK;
|
||||
}
|
||||
bool EOS() { return mEOS; }
|
||||
|
||||
// CSD (codec specific data), it is generated by encoder and the data depends
|
||||
// on codec type. This data will be sent as a special frame from encoder to
|
||||
// ISOMediaWriter and pass to this class via AddFrame().
|
||||
nsresult GetCSD(nsTArray<uint8_t>& aCSD);
|
||||
|
||||
bool HasCSD() { return mCSDFrame; }
|
||||
|
||||
uint32_t GetType() { return mTrackType; }
|
||||
|
||||
void SetLastFragmentLastFrameTime(uint32_t aTime) {
|
||||
mLastFrameTimeOfLastFragment = aTime;
|
||||
}
|
||||
|
||||
uint32_t GetLastFragmentLastFrameTime() {
|
||||
return mLastFrameTimeOfLastFragment;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t mTrackType;
|
||||
|
||||
// Fragment duration, microsecond per unit.
|
||||
uint32_t mFragDuration;
|
||||
|
||||
// Media start time, microsecond per unit.
|
||||
// Together with mFragDuration, mFragmentNumber and EncodedFrame->GetTimeStamp(),
|
||||
// when the difference between current frame time and mMediaStartTime is
|
||||
// exceeded current fragment ceiling timeframe, that means current fragment has
|
||||
// enough data and a new element in mFragArray will be added.
|
||||
uint64_t mMediaStartTime;
|
||||
|
||||
// Current fragment number. It will be increase when a new element of
|
||||
// mFragArray is created.
|
||||
// Note:
|
||||
// It only means the fragment number of current accumulated frames, not
|
||||
// the current 'creating' fragment mFragNum in ISOControl.
|
||||
uint32_t mFragmentNumber;
|
||||
|
||||
// The last frame time stamp of last fragment. It is for calculating the
|
||||
// play duration of first frame in current fragment. The frame duration is
|
||||
// defined as "current frame timestamp - last frame timestamp" here. So it
|
||||
// needs to keep the last timestamp of last fragment.
|
||||
uint32_t mLastFrameTimeOfLastFragment;
|
||||
|
||||
// Array of fragments, each element has enough samples to form a
|
||||
// complete fragment.
|
||||
nsTArray<nsTArray<RefPtr<EncodedFrame>>> mFragArray;
|
||||
|
||||
// Codec specific data frame, it will be generated by encoder and send to
|
||||
// ISOMediaWriter through WriteEncodedTrack(). The data will be vary depends
|
||||
// on codec type.
|
||||
RefPtr<EncodedFrame> mCSDFrame;
|
||||
|
||||
// END_OF_STREAM from ContainerWriter
|
||||
bool mEOS;
|
||||
};
|
||||
|
||||
/**
|
||||
* ISOControl will be carried to each box when box is created. It is the main
|
||||
* bridge for box to output stream to ContainerWriter and retrieve information.
|
||||
* ISOControl acts 3 different roles:
|
||||
* 1. Holds the pointer of audio metadata, video metadata, fragment and
|
||||
* pass them to boxes.
|
||||
* 2. Provide the functions to generate the base structure of MP4; they are
|
||||
* GenerateFtyp, GenerateMoov, GenerateMoof, and GenerateMfra.
|
||||
* 3. The actually writer used by MuxOperation::Write() in each box. It provides
|
||||
* writing methods for different kind of data; they are Write, WriteArray,
|
||||
* WriteBits...etc.
|
||||
*/
|
||||
class ISOControl {
|
||||
|
||||
friend class Box;
|
||||
|
||||
public:
|
||||
ISOControl(uint32_t aMuxingType);
|
||||
~ISOControl();
|
||||
|
||||
nsresult GenerateFtyp();
|
||||
nsresult GenerateMoov();
|
||||
nsresult GenerateMoof(uint32_t aTrackType);
|
||||
|
||||
// Swap elementary stream pointer to output buffers.
|
||||
uint32_t WriteAVData(nsTArray<uint8_t>& aArray);
|
||||
|
||||
uint32_t Write(uint8_t* aBuf, uint32_t aSize);
|
||||
|
||||
uint32_t Write(uint8_t aData);
|
||||
|
||||
template <typename T>
|
||||
uint32_t Write(T aData) {
|
||||
MOZ_ASSERT(!mBitCount);
|
||||
|
||||
aData = NativeEndian::swapToNetworkOrder(aData);
|
||||
Write((uint8_t*)&aData, sizeof(T));
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
uint32_t WriteArray(const T &aArray, uint32_t aSize) {
|
||||
MOZ_ASSERT(!mBitCount);
|
||||
|
||||
uint32_t size = 0;
|
||||
for (uint32_t i = 0; i < aSize; i++) {
|
||||
size += Write(aArray[i]);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
uint32_t WriteFourCC(const char* aType);
|
||||
|
||||
// Bit writing. Note: it needs to be byte-boundary before using
|
||||
// others non-bit writing function.
|
||||
uint32_t WriteBits(uint64_t aBits, size_t aNumBits);
|
||||
|
||||
// This is called by GetContainerData and swap all the buffers to aOutputBuffers.
|
||||
nsresult GetBufs(nsTArray<nsTArray<uint8_t>>* aOutputBufs);
|
||||
|
||||
// Presentation time in seconds since midnight, Jan. 1, 1904, in UTC time.
|
||||
uint32_t GetTime();
|
||||
|
||||
// current fragment number
|
||||
uint32_t GetCurFragmentNumber() { return mFragNum; }
|
||||
|
||||
nsresult SetFragment(FragmentBuffer* aFragment);
|
||||
FragmentBuffer* GetFragment(uint32_t aType);
|
||||
|
||||
uint32_t GetMuxingType() { return mMuxingType; }
|
||||
|
||||
nsresult SetMetadata(TrackMetadataBase* aTrackMeta);
|
||||
nsresult GetAudioMetadata(RefPtr<AudioTrackMetadata>& aAudMeta);
|
||||
nsresult GetVideoMetadata(RefPtr<VideoTrackMetadata>& aVidMeta);
|
||||
|
||||
// Track ID is the Metadata index in mMetaArray. It allows only 1 audio
|
||||
// track and 1 video track in this muxer. In this muxer, it is prohibt to have
|
||||
// mutiple audio track or video track in the same file.
|
||||
uint32_t GetTrackID(TrackMetadataBase::MetadataKind aKind);
|
||||
uint32_t GetNextTrackID();
|
||||
|
||||
bool HasAudioTrack();
|
||||
bool HasVideoTrack();
|
||||
|
||||
private:
|
||||
uint32_t GetBufPos();
|
||||
nsresult FlushBuf();
|
||||
|
||||
// One of value in TYPE_XXX, defined in ISOMediaWriter.
|
||||
uint32_t mMuxingType;
|
||||
|
||||
// Audio and video fragments are owned by ISOMediaWriter.
|
||||
// They don't need to worry about pointer going stale because ISOMediaWriter's
|
||||
// lifetime is longer than ISOControl.
|
||||
FragmentBuffer* mAudioFragmentBuffer;
|
||||
FragmentBuffer* mVideoFragmentBuffer;
|
||||
|
||||
// Generated fragment number
|
||||
uint32_t mFragNum;
|
||||
|
||||
// The (index + 1) will be the track ID.
|
||||
nsTArray<RefPtr<TrackMetadataBase>> mMetaArray;
|
||||
|
||||
// Array of output buffers.
|
||||
// To save memory usage, audio/video sample will be swapped into a new element
|
||||
// of this array.
|
||||
//
|
||||
// For example,
|
||||
// mOutBuffers[0] --> boxes (allocated by muxer)
|
||||
// mOutBuffers[1] --> video raw data (allocated by encoder)
|
||||
// mOutBuffers[2] --> video raw data (allocated by encoder)
|
||||
// mOutBuffers[3] --> video raw data (allocated by encoder)
|
||||
// mOutBuffers[4] --> boxes (allocated by muxer)
|
||||
// mOutBuffers[5] --> audio raw data (allocated by encoder)
|
||||
// ...etc.
|
||||
//
|
||||
nsTArray<nsTArray<uint8_t>> mOutBuffers;
|
||||
|
||||
// Accumulate output size from Write().
|
||||
uint64_t mOutputSize;
|
||||
|
||||
// Bit writing operation. Note: the mBitCount should be 0 before any
|
||||
// byte-boundary writing method be called (Write(uint32_t), Write(uint16_t)...etc);
|
||||
// otherwise, there will be assertion on these functions.
|
||||
uint8_t mBitCount;
|
||||
uint8_t mBit;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,781 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 ISOMediaBoxes_h_
|
||||
#define ISOMediaBoxes_h_
|
||||
|
||||
#include <bitset>
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "MuxerOperation.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#define WRITE_FULLBOX(_compositor, _size) \
|
||||
BoxSizeChecker checker(_compositor, _size); \
|
||||
FullBox::Write();
|
||||
|
||||
#define FOURCC(a, b, c, d) ( ((a) << 24) | ((b) << 16) | ((c) << 8) | (d) )
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* track type from spec 8.4.3.3
|
||||
*/
|
||||
#define Audio_Track 0x01
|
||||
#define Video_Track 0x02
|
||||
|
||||
class AudioTrackMetadata;
|
||||
class VideoTrackMetadata;
|
||||
class ISOControl;
|
||||
|
||||
/**
|
||||
* This is the base class for all ISO media format boxes.
|
||||
* It provides the fields of box type(four CC) and size.
|
||||
* The data members in the beginning of a Box (or its descendants)
|
||||
* are the 14496-12 defined member. Other members prefix with 'm'
|
||||
* are private control data.
|
||||
*
|
||||
* This class is for inherited only, it shouldn't be instanced directly.
|
||||
*/
|
||||
class Box : public MuxerOperation {
|
||||
protected:
|
||||
// ISO BMFF members
|
||||
uint32_t size; // 14496-12 4-2 'Object Structure'. Size of this box.
|
||||
nsCString boxType; // four CC name, all table names are listed in
|
||||
// 14496-12 table 1.
|
||||
|
||||
public:
|
||||
// MuxerOperation methods
|
||||
nsresult Write() override;
|
||||
nsresult Find(const nsACString& aType,
|
||||
nsTArray<RefPtr<MuxerOperation>>& aOperations) override;
|
||||
|
||||
// This helper class will compare the written size in Write() and the size in
|
||||
// Generate(). If their are not equal, it will assert.
|
||||
class BoxSizeChecker {
|
||||
public:
|
||||
BoxSizeChecker(ISOControl* aControl, uint32_t aSize);
|
||||
~BoxSizeChecker();
|
||||
|
||||
uint32_t ori_size;
|
||||
uint32_t box_size;
|
||||
ISOControl* mControl;
|
||||
};
|
||||
|
||||
protected:
|
||||
Box() = delete;
|
||||
Box(const nsACString& aType, ISOControl* aControl);
|
||||
|
||||
ISOControl* mControl;
|
||||
RefPtr<AudioTrackMetadata> mAudioMeta;
|
||||
RefPtr<VideoTrackMetadata> mVideoMeta;
|
||||
};
|
||||
|
||||
/**
|
||||
* FullBox (and its descendants) is the box which contains the 'real' data
|
||||
* members. It is the edge in the ISO box structure and it doesn't contain
|
||||
* any box.
|
||||
*
|
||||
* This class is for inherited only, it shouldn't be instanced directly.
|
||||
*/
|
||||
class FullBox : public Box {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint8_t version; // 14496-12 4.2 'Object Structure'
|
||||
std::bitset<24> flags; //
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Write() override;
|
||||
|
||||
protected:
|
||||
// FullBox methods
|
||||
FullBox(const nsACString& aType, uint8_t aVersion, uint32_t aFlags,
|
||||
ISOControl* aControl);
|
||||
FullBox() = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* The default implementation of the container box.
|
||||
* Basically, the container box inherits this class and overrides the
|
||||
* constructor only.
|
||||
*
|
||||
* According to 14496-12 3.1.1 'container box', a container box is
|
||||
* 'box whose sole purpose is to contain and group a set of related boxes'
|
||||
*
|
||||
* This class is for inherited only, it shouldn't be instanced directly.
|
||||
*/
|
||||
class DefaultContainerImpl : public Box {
|
||||
public:
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
nsresult Find(const nsACString& aType,
|
||||
nsTArray<RefPtr<MuxerOperation>>& aOperations) override;
|
||||
|
||||
protected:
|
||||
// DefaultContainerImpl methods
|
||||
DefaultContainerImpl(const nsACString& aType, ISOControl* aControl);
|
||||
DefaultContainerImpl() = delete;
|
||||
|
||||
nsTArray<RefPtr<MuxerOperation>> boxes;
|
||||
};
|
||||
|
||||
// 14496-12 4.3 'File Type Box'
|
||||
// Box type: 'ftyp'
|
||||
class FileTypeBox : public Box {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
nsCString major_brand; // four chars
|
||||
uint32_t minor_version;
|
||||
nsTArray<nsCString> compatible_brands;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// FileTypeBox methods
|
||||
FileTypeBox(ISOControl* aControl);
|
||||
~FileTypeBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.2.1 'Movie Box'
|
||||
// Box type: 'moov'
|
||||
// MovieBox contains MovieHeaderBox, TrackBox and MovieExtendsBox.
|
||||
class MovieBox : public DefaultContainerImpl {
|
||||
public:
|
||||
MovieBox(ISOControl* aControl);
|
||||
~MovieBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.2.2 'Movie Header Box'
|
||||
// Box type: 'mvhd'
|
||||
class MovieHeaderBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t creation_time;
|
||||
uint32_t modification_time;
|
||||
uint32_t timescale;
|
||||
uint32_t duration;
|
||||
uint32_t rate;
|
||||
uint16_t volume;
|
||||
uint16_t reserved16;
|
||||
uint32_t reserved32[2];
|
||||
uint32_t matrix[9];
|
||||
uint32_t pre_defined[6];
|
||||
uint32_t next_track_ID;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// MovieHeaderBox methods
|
||||
MovieHeaderBox(ISOControl* aControl);
|
||||
~MovieHeaderBox();
|
||||
uint32_t GetTimeScale();
|
||||
};
|
||||
|
||||
// 14496-12 8.4.2 'Media Header Box'
|
||||
// Box type: 'mdhd'
|
||||
class MediaHeaderBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t creation_time;
|
||||
uint32_t modification_time;
|
||||
uint32_t timescale;
|
||||
uint32_t duration;
|
||||
std::bitset<1> pad;
|
||||
std::bitset<5> lang1;
|
||||
std::bitset<5> lang2;
|
||||
std::bitset<5> lang3;
|
||||
uint16_t pre_defined;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// MediaHeaderBox methods
|
||||
MediaHeaderBox(uint32_t aType, ISOControl* aControl);
|
||||
~MediaHeaderBox();
|
||||
uint32_t GetTimeScale();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.3.1 'Track Box'
|
||||
// Box type: 'trak'
|
||||
// TrackBox contains TrackHeaderBox and MediaBox.
|
||||
class TrackBox : public DefaultContainerImpl {
|
||||
public:
|
||||
TrackBox(uint32_t aTrackType, ISOControl* aControl);
|
||||
~TrackBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.1.1 'Media Data Box'
|
||||
// Box type: 'mdat'
|
||||
class MediaDataBox : public Box {
|
||||
public:
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// MediaDataBox methods
|
||||
uint32_t GetAllSampleSize() { return mAllSampleSize; }
|
||||
uint32_t FirstSampleOffsetInMediaDataBox() { return mFirstSampleOffset; }
|
||||
MediaDataBox(uint32_t aTrackType, ISOControl* aControl);
|
||||
~MediaDataBox();
|
||||
|
||||
protected:
|
||||
uint32_t mAllSampleSize; // All audio and video sample size in this box.
|
||||
uint32_t mFirstSampleOffset; // The offset of first sample in this box from
|
||||
// the beginning of this mp4 file.
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// flags for TrackRunBox::flags, 14496-12 8.8.8.1.
|
||||
#define flags_data_offset_present 0x000001
|
||||
#define flags_first_sample_flags_present 0x000002
|
||||
#define flags_sample_duration_present 0x000100
|
||||
#define flags_sample_size_present 0x000200
|
||||
#define flags_sample_flags_present 0x000400
|
||||
#define flags_sample_composition_time_offsets_present 0x000800
|
||||
|
||||
// flag for TrackRunBox::tbl::sample_flags and TrackExtendsBox::default_sample_flags
|
||||
// which is defined in 14496-12 8.8.3.1.
|
||||
uint32_t set_sample_flags(bool aSync);
|
||||
|
||||
// 14496-12 8.8.8 'Track Fragment Run Box'
|
||||
// Box type: 'trun'
|
||||
class TrackRunBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
typedef struct {
|
||||
uint32_t sample_duration;
|
||||
uint32_t sample_size;
|
||||
uint32_t sample_flags;
|
||||
uint32_t sample_composition_time_offset;
|
||||
} tbl;
|
||||
|
||||
uint32_t sample_count;
|
||||
// the following are optional fields
|
||||
uint32_t data_offset; // data offset exists when audio/video are present in file.
|
||||
uint32_t first_sample_flags;
|
||||
UniquePtr<tbl[]> sample_info_table;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// TrackRunBox methods
|
||||
uint32_t GetAllSampleSize() { return mAllSampleSize; }
|
||||
nsresult SetDataOffset(uint32_t aOffset);
|
||||
|
||||
TrackRunBox(uint32_t aType, uint32_t aFlags, ISOControl* aControl);
|
||||
~TrackRunBox();
|
||||
|
||||
protected:
|
||||
uint32_t fillSampleTable();
|
||||
|
||||
uint32_t mAllSampleSize;
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// tf_flags in TrackFragmentHeaderBox, 14496-12 8.8.7.1.
|
||||
#define base_data_offset_present 0x000001
|
||||
#define sample_description_index_present 0x000002
|
||||
#define default_sample_duration_present 0x000008
|
||||
#define default_sample_size_present 0x000010
|
||||
#define default_sample_flags_present 0x000020
|
||||
#define duration_is_empty 0x010000
|
||||
#define default_base_is_moof 0x020000
|
||||
|
||||
// 14496-12 8.8.7 'Track Fragment Header Box'
|
||||
// Box type: 'tfhd'
|
||||
class TrackFragmentHeaderBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t track_ID;
|
||||
uint64_t base_data_offset;
|
||||
uint32_t default_sample_duration;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// TrackFragmentHeaderBox methods
|
||||
nsresult UpdateBaseDataOffset(uint64_t aOffset); // The offset of the first
|
||||
// sample in file.
|
||||
|
||||
TrackFragmentHeaderBox(uint32_t aType, uint32_t aFlags, ISOControl* aControl);
|
||||
~TrackFragmentHeaderBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.8.6 'Track Fragment Box'
|
||||
// Box type: 'traf'
|
||||
// TrackFragmentBox cotains TrackFragmentHeaderBox and TrackRunBox.
|
||||
class TrackFragmentBox : public DefaultContainerImpl {
|
||||
public:
|
||||
TrackFragmentBox(uint32_t aType, ISOControl* aControl);
|
||||
~TrackFragmentBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.8.5 'Movie Fragment Header Box'
|
||||
// Box type: 'mfhd'
|
||||
class MovieFragmentHeaderBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t sequence_number;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// MovieFragmentHeaderBox methods
|
||||
MovieFragmentHeaderBox(uint32_t aType, ISOControl* aControl);
|
||||
~MovieFragmentHeaderBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.8.4 'Movie Fragment Box'
|
||||
// Box type: 'moof'
|
||||
// MovieFragmentBox contains MovieFragmentHeaderBox and TrackFragmentBox.
|
||||
class MovieFragmentBox : public DefaultContainerImpl {
|
||||
public:
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
|
||||
// MovieFragmentBox methods
|
||||
MovieFragmentBox(uint32_t aType, ISOControl* aControl);
|
||||
~MovieFragmentBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.8.3 'Track Extends Box'
|
||||
// Box type: 'trex'
|
||||
class TrackExtendsBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t track_ID;
|
||||
uint32_t default_sample_description_index;
|
||||
uint32_t default_sample_duration;
|
||||
uint32_t default_sample_size;
|
||||
uint32_t default_sample_flags;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// TrackExtendsBox methods
|
||||
TrackExtendsBox(uint32_t aType, ISOControl* aControl);
|
||||
~TrackExtendsBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.8.1 'Movie Extends Box'
|
||||
// Box type: 'mvex'
|
||||
// MovieExtendsBox contains TrackExtendsBox.
|
||||
class MovieExtendsBox : public DefaultContainerImpl {
|
||||
public:
|
||||
MovieExtendsBox(ISOControl* aControl);
|
||||
~MovieExtendsBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.7.5 'Chunk Offset Box'
|
||||
// Box type: 'stco'
|
||||
class ChunkOffsetBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
typedef struct {
|
||||
uint32_t chunk_offset;
|
||||
} tbl;
|
||||
|
||||
uint32_t entry_count;
|
||||
UniquePtr<tbl[]> sample_tbl;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// ChunkOffsetBox methods
|
||||
ChunkOffsetBox(uint32_t aType, ISOControl* aControl);
|
||||
~ChunkOffsetBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.7.4 'Sample To Chunk Box'
|
||||
// Box type: 'stsc'
|
||||
class SampleToChunkBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
typedef struct {
|
||||
uint32_t first_chunk;
|
||||
uint32_t sample_per_chunk;
|
||||
uint32_t sample_description_index;
|
||||
} tbl;
|
||||
|
||||
uint32_t entry_count;
|
||||
UniquePtr<tbl[]> sample_tbl;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// SampleToChunkBox methods
|
||||
SampleToChunkBox(uint32_t aType, ISOControl* aControl);
|
||||
~SampleToChunkBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.6.1.2 'Decoding Time to Sample Box'
|
||||
// Box type: 'stts'
|
||||
class TimeToSampleBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
typedef struct {
|
||||
uint32_t sample_count;
|
||||
uint32_t sample_delta;
|
||||
} tbl;
|
||||
|
||||
uint32_t entry_count;
|
||||
UniquePtr<tbl[]> sample_tbl;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// TimeToSampleBox methods
|
||||
TimeToSampleBox(uint32_t aType, ISOControl* aControl);
|
||||
~TimeToSampleBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
/**
|
||||
* 14496-12 8.5.2 'Sample Description Box'
|
||||
* This is the base class for VisualSampleEntry and AudioSampleEntry.
|
||||
*
|
||||
* This class is for inherited only, it shouldn't be instanced directly.
|
||||
*
|
||||
* The inhertied tree of a codec box should be:
|
||||
*
|
||||
* +--> AVCSampleEntry
|
||||
* +--> VisualSampleEntryBox +
|
||||
* | +--> ...
|
||||
* SampleEntryBox +
|
||||
* | +--> MP4AudioSampleEntry
|
||||
* +--> AudioSampleEntryBox +
|
||||
* +--> AMRSampleEntry
|
||||
* +
|
||||
* +--> ...
|
||||
*
|
||||
*/
|
||||
class SampleEntryBox : public Box {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint8_t reserved[6];
|
||||
uint16_t data_reference_index;
|
||||
|
||||
// sampleentrybox methods
|
||||
SampleEntryBox(const nsACString& aFormat, ISOControl* aControl);
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Write() override;
|
||||
|
||||
protected:
|
||||
SampleEntryBox() = delete;
|
||||
};
|
||||
|
||||
// 14496-12 8.5.2 'Sample Description Box'
|
||||
// Box type: 'stsd'
|
||||
class SampleDescriptionBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t entry_count;
|
||||
RefPtr<SampleEntryBox> sample_entry_box;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// SampleDescriptionBox methods
|
||||
SampleDescriptionBox(uint32_t aType, ISOControl* aControl);
|
||||
~SampleDescriptionBox();
|
||||
|
||||
protected:
|
||||
nsresult CreateAudioSampleEntry(RefPtr<SampleEntryBox>& aSampleEntry);
|
||||
nsresult CreateVideoSampleEntry(RefPtr<SampleEntryBox>& aSampleEntry);
|
||||
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.5.2.2
|
||||
// The base class for audio codec box.
|
||||
// This class is for inherited only, it shouldn't be instanced directly.
|
||||
class AudioSampleEntry : public SampleEntryBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint16_t sound_version;
|
||||
uint8_t reserved2[6];
|
||||
uint16_t channels;
|
||||
uint16_t sample_size;
|
||||
uint16_t compressionId;
|
||||
uint16_t packet_size;
|
||||
uint32_t timeScale; // (sample rate of media) <<16
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Write() override;
|
||||
|
||||
~AudioSampleEntry();
|
||||
|
||||
protected:
|
||||
AudioSampleEntry(const nsACString& aFormat, ISOControl* aControl);
|
||||
};
|
||||
|
||||
// 14496-12 8.5.2.2
|
||||
// The base class for video codec box.
|
||||
// This class is for inherited only, it shouldn't be instanced directly.
|
||||
class VisualSampleEntry : public SampleEntryBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint8_t reserved[16];
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
|
||||
uint32_t horizresolution; // 72 dpi
|
||||
uint32_t vertresolution; // 72 dpi
|
||||
uint32_t reserved2;
|
||||
uint16_t frame_count; // 1, defined in 14496-12 8.5.2.2
|
||||
|
||||
uint8_t compressorName[32];
|
||||
uint16_t depth; // 0x0018, defined in 14496-12 8.5.2.2;
|
||||
uint16_t pre_defined; // -1, defined in 14496-12 8.5.2.2;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Write() override;
|
||||
|
||||
// VisualSampleEntry methods
|
||||
~VisualSampleEntry();
|
||||
|
||||
protected:
|
||||
VisualSampleEntry(const nsACString& aFormat, ISOControl* aControl);
|
||||
};
|
||||
|
||||
// 14496-12 8.7.3.2 'Sample Size Box'
|
||||
// Box type: 'stsz'
|
||||
class SampleSizeBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t sample_size;
|
||||
uint32_t sample_count;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// SampleSizeBox methods
|
||||
SampleSizeBox(ISOControl* aControl);
|
||||
~SampleSizeBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.5.1 'Sample Table Box'
|
||||
// Box type: 'stbl'
|
||||
//
|
||||
// SampleTableBox contains SampleDescriptionBox,
|
||||
// TimeToSampleBox,
|
||||
// SampleToChunkBox,
|
||||
// SampleSizeBox and
|
||||
// ChunkOffsetBox.
|
||||
class SampleTableBox : public DefaultContainerImpl {
|
||||
public:
|
||||
SampleTableBox(uint32_t aType, ISOControl* aControl);
|
||||
~SampleTableBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.7.2 'Data Reference Box'
|
||||
// Box type: 'url '
|
||||
class DataEntryUrlBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
// flags in DataEntryUrlBox::flags
|
||||
const static uint16_t flags_media_at_the_same_file = 0x0001;
|
||||
|
||||
nsCString location;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// DataEntryUrlBox methods
|
||||
DataEntryUrlBox();
|
||||
DataEntryUrlBox(ISOControl* aControl);
|
||||
DataEntryUrlBox(const DataEntryUrlBox& aBox);
|
||||
~DataEntryUrlBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.7.2 'Data Reference Box'
|
||||
// Box type: 'dref'
|
||||
class DataReferenceBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t entry_count;
|
||||
nsTArray<nsAutoPtr<DataEntryUrlBox>> urls;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// DataReferenceBox methods
|
||||
DataReferenceBox(ISOControl* aControl);
|
||||
~DataReferenceBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.7.1 'Data Information Box'
|
||||
// Box type: 'dinf'
|
||||
// DataInformationBox contains DataReferenceBox.
|
||||
class DataInformationBox : public DefaultContainerImpl {
|
||||
public:
|
||||
DataInformationBox(ISOControl* aControl);
|
||||
~DataInformationBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.4.5.2 'Video Media Header Box'
|
||||
// Box type: 'vmhd'
|
||||
class VideoMediaHeaderBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint16_t graphicsmode;
|
||||
uint16_t opcolor[3];
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// VideoMediaHeaderBox methods
|
||||
VideoMediaHeaderBox(ISOControl* aControl);
|
||||
~VideoMediaHeaderBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.4.5.3 'Sound Media Header Box'
|
||||
// Box type: 'smhd'
|
||||
class SoundMediaHeaderBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint16_t balance;
|
||||
uint16_t reserved;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// SoundMediaHeaderBox methods
|
||||
SoundMediaHeaderBox(ISOControl* aControl);
|
||||
~SoundMediaHeaderBox();
|
||||
};
|
||||
|
||||
// 14496-12 8.4.4 'Media Information Box'
|
||||
// Box type: 'minf'
|
||||
// MediaInformationBox contains SoundMediaHeaderBox, DataInformationBox and
|
||||
// SampleTableBox.
|
||||
class MediaInformationBox : public DefaultContainerImpl {
|
||||
public:
|
||||
MediaInformationBox(uint32_t aType, ISOControl* aControl);
|
||||
~MediaInformationBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// flags for TrackHeaderBox::flags.
|
||||
#define flags_track_enabled 0x000001
|
||||
#define flags_track_in_movie 0x000002
|
||||
#define flags_track_in_preview 0x000004
|
||||
|
||||
// 14496-12 8.3.2 'Track Header Box'
|
||||
// Box type: 'tkhd'
|
||||
class TrackHeaderBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
// version = 0
|
||||
uint32_t creation_time;
|
||||
uint32_t modification_time;
|
||||
uint32_t track_ID;
|
||||
uint32_t reserved;
|
||||
uint32_t duration;
|
||||
|
||||
uint32_t reserved2[2];
|
||||
uint16_t layer;
|
||||
uint16_t alternate_group;
|
||||
uint16_t volume;
|
||||
uint16_t reserved3;
|
||||
uint32_t matrix[9];
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// TrackHeaderBox methods
|
||||
TrackHeaderBox(uint32_t aType, ISOControl* aControl);
|
||||
~TrackHeaderBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.4.3 'Handler Reference Box'
|
||||
// Box type: 'hdlr'
|
||||
class HandlerBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint32_t pre_defined;
|
||||
uint32_t handler_type;
|
||||
uint32_t reserved[3];
|
||||
nsCString name;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// HandlerBox methods
|
||||
HandlerBox(uint32_t aType, ISOControl* aControl);
|
||||
~HandlerBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
// 14496-12 8.4.1 'Media Box'
|
||||
// Box type: 'mdia'
|
||||
// MediaBox contains MediaHeaderBox, HandlerBox, and MediaInformationBox.
|
||||
class MediaBox : public DefaultContainerImpl {
|
||||
public:
|
||||
MediaBox(uint32_t aType, ISOControl* aControl);
|
||||
~MediaBox();
|
||||
|
||||
protected:
|
||||
uint32_t mTrackType;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // ISOMediaBoxes_h_
|
||||
@@ -1,229 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 "ISOMediaWriter.h"
|
||||
#include "ISOControl.h"
|
||||
#include "ISOMediaBoxes.h"
|
||||
#include "ISOTrackMetadata.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "MediaEncoder.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args, ...)
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
const static uint32_t FRAG_DURATION = 2 * USECS_PER_S; // microsecond per unit
|
||||
|
||||
ISOMediaWriter::ISOMediaWriter(uint32_t aType, uint32_t aHint)
|
||||
: ContainerWriter()
|
||||
, mState(MUXING_HEAD)
|
||||
, mBlobReady(false)
|
||||
, mType(0)
|
||||
{
|
||||
if (aType & CREATE_AUDIO_TRACK) {
|
||||
mType |= Audio_Track;
|
||||
}
|
||||
if (aType & CREATE_VIDEO_TRACK) {
|
||||
mType |= Video_Track;
|
||||
}
|
||||
mControl = new ISOControl(aHint);
|
||||
MOZ_COUNT_CTOR(ISOMediaWriter);
|
||||
}
|
||||
|
||||
ISOMediaWriter::~ISOMediaWriter()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ISOMediaWriter);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOMediaWriter::RunState()
|
||||
{
|
||||
nsresult rv;
|
||||
switch (mState) {
|
||||
case MUXING_HEAD:
|
||||
{
|
||||
rv = mControl->GenerateFtyp();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mControl->GenerateMoov();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mState = MUXING_FRAG;
|
||||
break;
|
||||
}
|
||||
case MUXING_FRAG:
|
||||
{
|
||||
rv = mControl->GenerateMoof(mType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool EOS;
|
||||
if (ReadyToRunState(EOS) && EOS) {
|
||||
mState = MUXING_DONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MUXING_DONE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
mBlobReady = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOMediaWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
PROFILER_LABEL("ISOMediaWriter", "WriteEncodedTrack",
|
||||
js::ProfileEntry::Category::OTHER);
|
||||
// Muxing complete, it doesn't allowed to reentry again.
|
||||
if (mState == MUXING_DONE) {
|
||||
MOZ_ASSERT(false);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
FragmentBuffer* frag = nullptr;
|
||||
uint32_t len = aData.GetEncodedFrames().Length();
|
||||
|
||||
if (!len) {
|
||||
// no frame? why bother to WriteEncodedTrack
|
||||
return NS_OK;
|
||||
}
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
RefPtr<EncodedFrame> frame(aData.GetEncodedFrames()[i]);
|
||||
EncodedFrame::FrameType type = frame->GetFrameType();
|
||||
if (type == EncodedFrame::AAC_AUDIO_FRAME ||
|
||||
type == EncodedFrame::AAC_CSD ||
|
||||
type == EncodedFrame::AMR_AUDIO_FRAME ||
|
||||
type == EncodedFrame::AMR_AUDIO_CSD ||
|
||||
type == EncodedFrame::EVRC_AUDIO_FRAME ||
|
||||
type == EncodedFrame::EVRC_AUDIO_CSD) {
|
||||
frag = mAudioFragmentBuffer;
|
||||
} else if (type == EncodedFrame::AVC_I_FRAME ||
|
||||
type == EncodedFrame::AVC_P_FRAME ||
|
||||
type == EncodedFrame::AVC_B_FRAME ||
|
||||
type == EncodedFrame::AVC_CSD) {
|
||||
frag = mVideoFragmentBuffer;
|
||||
} else {
|
||||
MOZ_ASSERT(0);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
frag->AddFrame(frame);
|
||||
}
|
||||
|
||||
// Encoder should send CSD (codec specific data) frame before sending the
|
||||
// audio/video frames. When CSD data is ready, it is sufficient to generate a
|
||||
// moov data. If encoder doesn't send CSD yet, muxer needs to wait before
|
||||
// generating anything.
|
||||
if (mType & Audio_Track && (!mAudioFragmentBuffer ||
|
||||
!mAudioFragmentBuffer->HasCSD())) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (mType & Video_Track && (!mVideoFragmentBuffer ||
|
||||
!mVideoFragmentBuffer->HasCSD())) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Only one FrameType in EncodedFrameContainer so it doesn't need to be
|
||||
// inside the for-loop.
|
||||
if (frag && (aFlags & END_OF_STREAM)) {
|
||||
frag->SetEndOfStream();
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
bool EOS;
|
||||
if (ReadyToRunState(EOS)) {
|
||||
// Because track encoder won't generate new data after EOS, it needs to make
|
||||
// sure the state reaches MUXING_DONE when EOS is signaled.
|
||||
do {
|
||||
rv = RunState();
|
||||
} while (EOS && mState != MUXING_DONE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
ISOMediaWriter::ReadyToRunState(bool& aEOS)
|
||||
{
|
||||
aEOS = false;
|
||||
bool bReadyToMux = true;
|
||||
if ((mType & Audio_Track) && (mType & Video_Track)) {
|
||||
if (!mAudioFragmentBuffer->HasEnoughData()) {
|
||||
bReadyToMux = false;
|
||||
}
|
||||
if (!mVideoFragmentBuffer->HasEnoughData()) {
|
||||
bReadyToMux = false;
|
||||
}
|
||||
|
||||
if (mAudioFragmentBuffer->EOS() && mVideoFragmentBuffer->EOS()) {
|
||||
aEOS = true;
|
||||
bReadyToMux = true;
|
||||
}
|
||||
} else if (mType == Audio_Track) {
|
||||
if (!mAudioFragmentBuffer->HasEnoughData()) {
|
||||
bReadyToMux = false;
|
||||
}
|
||||
if (mAudioFragmentBuffer->EOS()) {
|
||||
aEOS = true;
|
||||
bReadyToMux = true;
|
||||
}
|
||||
} else if (mType == Video_Track) {
|
||||
if (!mVideoFragmentBuffer->HasEnoughData()) {
|
||||
bReadyToMux = false;
|
||||
}
|
||||
if (mVideoFragmentBuffer->EOS()) {
|
||||
aEOS = true;
|
||||
bReadyToMux = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bReadyToMux;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOMediaWriter::GetContainerData(nsTArray<nsTArray<uint8_t>>* aOutputBufs,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
PROFILER_LABEL("ISOMediaWriter", "GetContainerData",
|
||||
js::ProfileEntry::Category::OTHER);
|
||||
if (mBlobReady) {
|
||||
if (mState == MUXING_DONE) {
|
||||
mIsWritingComplete = true;
|
||||
}
|
||||
mBlobReady = false;
|
||||
return mControl->GetBufs(aOutputBufs);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata)
|
||||
{
|
||||
PROFILER_LABEL("ISOMediaWriter", "SetMetadata",
|
||||
js::ProfileEntry::Category::OTHER);
|
||||
if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
||||
aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR ||
|
||||
aMetadata->GetKind() == TrackMetadataBase::METADATA_EVRC) {
|
||||
mControl->SetMetadata(aMetadata);
|
||||
mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION);
|
||||
mControl->SetFragment(mAudioFragmentBuffer);
|
||||
return NS_OK;
|
||||
}
|
||||
if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AVC) {
|
||||
mControl->SetMetadata(aMetadata);
|
||||
mVideoFragmentBuffer = new FragmentBuffer(Video_Track, FRAG_DURATION);
|
||||
mControl->SetFragment(mVideoFragmentBuffer);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
@@ -1,108 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 ISOMediaWriter_h_
|
||||
#define ISOMediaWriter_h_
|
||||
|
||||
#include "ContainerWriter.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIRunnable.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ISOControl;
|
||||
class FragmentBuffer;
|
||||
|
||||
class ISOMediaWriter : public ContainerWriter
|
||||
{
|
||||
public:
|
||||
// Generate an fragmented MP4 stream, ISO/IEC 14496-12.
|
||||
// Brand names in 'ftyp' box are 'isom' and 'mp42'.
|
||||
const static uint32_t TYPE_FRAG_MP4 = 1 << 0;
|
||||
|
||||
// Generate an fragmented 3GP stream, 3GPP TS 26.244,
|
||||
// '5.4.3 Basic profile'.
|
||||
// Brand names in 'ftyp' box are '3gp9' and 'isom'.
|
||||
const static uint32_t TYPE_FRAG_3GP = 1 << 1;
|
||||
|
||||
// Generate an fragmented 3G2 stream, 3GPP2 C.S0050-B
|
||||
// Brand names in 'ftyp' box are '3g2c' and 'isom'
|
||||
const static uint32_t TYPE_FRAG_3G2 = 1 << 2;
|
||||
|
||||
// aType is the combination of CREATE_AUDIO_TRACK and CREATE_VIDEO_TRACK.
|
||||
// It is a hint to muxer that the output streaming contains audio, video
|
||||
// or both.
|
||||
//
|
||||
// aHint is one of the value in TYPE_XXXXXXXX. It is a hint to muxer what kind
|
||||
// of ISO format should be generated.
|
||||
ISOMediaWriter(uint32_t aType, uint32_t aHint = TYPE_FRAG_MP4);
|
||||
~ISOMediaWriter();
|
||||
|
||||
// ContainerWriter methods
|
||||
nsresult WriteEncodedTrack(const EncodedFrameContainer &aData,
|
||||
uint32_t aFlags = 0) override;
|
||||
|
||||
nsresult GetContainerData(nsTArray<nsTArray<uint8_t>>* aOutputBufs,
|
||||
uint32_t aFlags = 0) override;
|
||||
|
||||
nsresult SetMetadata(TrackMetadataBase* aMetadata) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The state of each state will generate one or more blob.
|
||||
* Each blob will be a moov, moof, moof... until receiving EOS.
|
||||
* The generated sequence is:
|
||||
*
|
||||
* moov -> moof -> moof -> ... -> moof -> moof
|
||||
*
|
||||
* Following is the details of each state.
|
||||
* MUXING_HEAD:
|
||||
* It collects the metadata to generate a moov. The state transits to
|
||||
* MUXING_HEAD after output moov blob.
|
||||
*
|
||||
* MUXING_FRAG:
|
||||
* It collects enough audio/video data to generate a fragment blob. This
|
||||
* will be repeated until END_OF_STREAM and then transiting to MUXING_DONE.
|
||||
*
|
||||
* MUXING_DONE:
|
||||
* End of ISOMediaWriter life cycle.
|
||||
*/
|
||||
enum MuxState {
|
||||
MUXING_HEAD,
|
||||
MUXING_FRAG,
|
||||
MUXING_DONE,
|
||||
};
|
||||
|
||||
private:
|
||||
nsresult RunState();
|
||||
|
||||
// True if one of following conditions hold:
|
||||
// 1. Audio/Video accumulates enough data to generate a moof.
|
||||
// 2. Get EOS signal.
|
||||
// aEOS will be assigned to true if it gets EOS signal.
|
||||
bool ReadyToRunState(bool& aEOS);
|
||||
|
||||
// The main class to generate and iso box. Its life time is same as
|
||||
// ISOMediaWriter and deleted only if ISOMediaWriter is destroyed.
|
||||
nsAutoPtr<ISOControl> mControl;
|
||||
|
||||
// Buffers to keep audio/video data frames, they are created when metadata is
|
||||
// received. Only one instance for each media type is allowed and they will be
|
||||
// deleted only if ISOMediaWriter is destroyed.
|
||||
nsAutoPtr<FragmentBuffer> mAudioFragmentBuffer;
|
||||
nsAutoPtr<FragmentBuffer> mVideoFragmentBuffer;
|
||||
|
||||
MuxState mState;
|
||||
|
||||
// A flag to indicate the output buffer is ready to blob out.
|
||||
bool mBlobReady;
|
||||
|
||||
// Combination of Audio_Track or Video_Track.
|
||||
uint32_t mType;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ISOMediaWriter_h_
|
||||
@@ -1,131 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 ISOTrackMetadata_h_
|
||||
#define ISOTrackMetadata_h_
|
||||
|
||||
#include "TrackMetadataBase.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AACTrackMetadata : public AudioTrackMetadata {
|
||||
public:
|
||||
// AudioTrackMetadata members
|
||||
uint32_t GetAudioFrameDuration() override { return mFrameDuration; }
|
||||
uint32_t GetAudioFrameSize() override { return mFrameSize; }
|
||||
uint32_t GetAudioSampleRate() override { return mSampleRate; }
|
||||
uint32_t GetAudioChannels() override { return mChannels; }
|
||||
|
||||
// TrackMetadataBase member
|
||||
MetadataKind GetKind() const override { return METADATA_AAC; }
|
||||
|
||||
// AACTrackMetadata members
|
||||
AACTrackMetadata()
|
||||
: mSampleRate(0)
|
||||
, mFrameDuration(0)
|
||||
, mFrameSize(0)
|
||||
, mChannels(0) {
|
||||
MOZ_COUNT_CTOR(AACTrackMetadata);
|
||||
}
|
||||
~AACTrackMetadata() { MOZ_COUNT_DTOR(AACTrackMetadata); }
|
||||
|
||||
uint32_t mSampleRate; // From 14496-3 table 1.16, it could be 7350 ~ 96000.
|
||||
uint32_t mFrameDuration; // Audio frame duration based on SampleRate.
|
||||
uint32_t mFrameSize; // Audio frame size, 0 is variant size.
|
||||
uint32_t mChannels; // Channel number, it should be 1 or 2.
|
||||
};
|
||||
|
||||
// AVC clock rate is 90k Hz.
|
||||
#define AVC_CLOCK_RATE 90000
|
||||
|
||||
class AVCTrackMetadata : public VideoTrackMetadata {
|
||||
public:
|
||||
// VideoTrackMetadata members
|
||||
uint32_t GetVideoHeight() override { return mHeight; }
|
||||
uint32_t GetVideoWidth() override {return mWidth; }
|
||||
uint32_t GetVideoDisplayHeight() override { return mDisplayHeight; }
|
||||
uint32_t GetVideoDisplayWidth() override { return mDisplayWidth; }
|
||||
uint32_t GetVideoClockRate() override { return AVC_CLOCK_RATE; }
|
||||
uint32_t GetVideoFrameRate() override { return mFrameRate; }
|
||||
|
||||
// TrackMetadataBase member
|
||||
MetadataKind GetKind() const override { return METADATA_AVC; }
|
||||
|
||||
// AVCTrackMetadata
|
||||
AVCTrackMetadata()
|
||||
: mHeight(0)
|
||||
, mWidth(0)
|
||||
, mDisplayHeight(0)
|
||||
, mDisplayWidth(0)
|
||||
, mFrameRate(0) {
|
||||
MOZ_COUNT_CTOR(AVCTrackMetadata);
|
||||
}
|
||||
~AVCTrackMetadata() { MOZ_COUNT_DTOR(AVCTrackMetadata); }
|
||||
|
||||
uint32_t mHeight;
|
||||
uint32_t mWidth;
|
||||
uint32_t mDisplayHeight;
|
||||
uint32_t mDisplayWidth;
|
||||
uint32_t mFrameRate; // frames per second
|
||||
};
|
||||
|
||||
|
||||
// AMR sample rate is 8000 samples/s.
|
||||
#define AMR_SAMPLE_RATE 8000
|
||||
|
||||
// Channel number is always 1.
|
||||
#define AMR_CHANNELS 1
|
||||
|
||||
// AMR speech codec, 3GPP TS 26.071. Encoder and continer support AMR-NB only
|
||||
// currently.
|
||||
class AMRTrackMetadata : public AudioTrackMetadata {
|
||||
public:
|
||||
// AudioTrackMetadata members
|
||||
//
|
||||
// The number of sample sets generates by encoder is variant. So the
|
||||
// frame duration and frame size are both 0.
|
||||
uint32_t GetAudioFrameDuration() override { return 0; }
|
||||
uint32_t GetAudioFrameSize() override { return 0; }
|
||||
uint32_t GetAudioSampleRate() override { return AMR_SAMPLE_RATE; }
|
||||
uint32_t GetAudioChannels() override { return AMR_CHANNELS; }
|
||||
|
||||
// TrackMetadataBase member
|
||||
MetadataKind GetKind() const override { return METADATA_AMR; }
|
||||
|
||||
// AMRTrackMetadata members
|
||||
AMRTrackMetadata() { MOZ_COUNT_CTOR(AMRTrackMetadata); }
|
||||
~AMRTrackMetadata() { MOZ_COUNT_DTOR(AMRTrackMetadata); }
|
||||
};
|
||||
|
||||
// EVRC sample rate is 8000 samples/s.
|
||||
#define EVRC_SAMPLE_RATE 8000
|
||||
|
||||
class EVRCTrackMetadata : public AudioTrackMetadata {
|
||||
public:
|
||||
// AudioTrackMetadata members
|
||||
//
|
||||
// The number of sample sets generates by encoder is variant. So the
|
||||
// frame duration and frame size are both 0.
|
||||
uint32_t GetAudioFrameDuration() override { return 0; }
|
||||
uint32_t GetAudioFrameSize() override { return 0; }
|
||||
uint32_t GetAudioSampleRate() override { return EVRC_SAMPLE_RATE; }
|
||||
uint32_t GetAudioChannels() override { return mChannels; }
|
||||
|
||||
// TrackMetadataBase member
|
||||
MetadataKind GetKind() const override { return METADATA_EVRC; }
|
||||
|
||||
// EVRCTrackMetadata members
|
||||
EVRCTrackMetadata()
|
||||
: mChannels(0) {
|
||||
MOZ_COUNT_CTOR(EVRCTrackMetadata);
|
||||
}
|
||||
~EVRCTrackMetadata() { MOZ_COUNT_DTOR(EVRCTrackMetadata); }
|
||||
|
||||
uint32_t mChannels; // Channel number, it should be 1 or 2.
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ISOTrackMetadata_h_
|
||||
@@ -1,138 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 <climits>
|
||||
#include "ISOControl.h"
|
||||
#include "ISOMediaBoxes.h"
|
||||
#include "MP4ESDS.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsresult
|
||||
MP4AudioSampleEntry::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
uint32_t box_size;
|
||||
nsresult rv = es->Generate(&box_size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
size += box_size;
|
||||
|
||||
*aBoxSize = size;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MP4AudioSampleEntry::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
nsresult rv;
|
||||
rv = AudioSampleEntry::Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = es->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MP4AudioSampleEntry::MP4AudioSampleEntry(ISOControl* aControl)
|
||||
: AudioSampleEntry(NS_LITERAL_CSTRING("mp4a"), aControl)
|
||||
{
|
||||
es = new ESDBox(aControl);
|
||||
MOZ_COUNT_CTOR(MP4AudioSampleEntry);
|
||||
}
|
||||
|
||||
MP4AudioSampleEntry::~MP4AudioSampleEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MP4AudioSampleEntry);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ESDBox::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
uint32_t box_size;
|
||||
es_descriptor->Generate(&box_size);
|
||||
size += box_size;
|
||||
*aBoxSize = size;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ESDBox::Write()
|
||||
{
|
||||
WRITE_FULLBOX(mControl, size)
|
||||
es_descriptor->Write();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ESDBox::ESDBox(ISOControl* aControl)
|
||||
: FullBox(NS_LITERAL_CSTRING("esds"), 0, 0, aControl)
|
||||
{
|
||||
es_descriptor = new ES_Descriptor(aControl);
|
||||
MOZ_COUNT_CTOR(ESDBox);
|
||||
}
|
||||
|
||||
ESDBox::~ESDBox()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ESDBox);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ES_Descriptor::Find(const nsACString& aType,
|
||||
nsTArray<RefPtr<MuxerOperation>>& aOperations)
|
||||
{
|
||||
// ES_Descriptor is not a real ISOMediaBox, so we return nothing here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ES_Descriptor::Write()
|
||||
{
|
||||
mControl->Write(tag);
|
||||
mControl->Write(length);
|
||||
mControl->Write(ES_ID);
|
||||
mControl->WriteBits(streamDependenceFlag.to_ulong(), streamDependenceFlag.size());
|
||||
mControl->WriteBits(URL_Flag.to_ulong(), URL_Flag.size());
|
||||
mControl->WriteBits(reserved.to_ulong(), reserved.size());
|
||||
mControl->WriteBits(streamPriority.to_ulong(), streamPriority.size());
|
||||
mControl->Write(DecodeSpecificInfo.Elements(), DecodeSpecificInfo.Length());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ES_Descriptor::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
nsresult rv;
|
||||
// 14496-1 '8.3.4 DecoderConfigDescriptor'
|
||||
// 14496-1 '10.2.3 SL Packet Header Configuration'
|
||||
FragmentBuffer* frag = mControl->GetFragment(Audio_Track);
|
||||
rv = frag->GetCSD(DecodeSpecificInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
length = sizeof(ES_ID) + 1;
|
||||
length += DecodeSpecificInfo.Length();
|
||||
|
||||
*aBoxSize = sizeof(tag) + sizeof(length) + length;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ES_Descriptor::ES_Descriptor(ISOControl* aControl)
|
||||
: tag(ESDescrTag)
|
||||
, length(0)
|
||||
, ES_ID(0)
|
||||
, streamDependenceFlag(0)
|
||||
, URL_Flag(0)
|
||||
, reserved(0)
|
||||
, streamPriority(0)
|
||||
, mControl(aControl)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ES_Descriptor);
|
||||
}
|
||||
|
||||
ES_Descriptor::~ES_Descriptor()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ES_Descriptor);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 MP4ESDS_h_
|
||||
#define MP4ESDS_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "MuxerOperation.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ISOControl;
|
||||
|
||||
/**
|
||||
* ESDS tag
|
||||
*/
|
||||
#define ESDescrTag 0x03
|
||||
|
||||
/**
|
||||
* 14496-1 '8.3.3 ES_Descriptor'.
|
||||
* It will get DecoderConfigDescriptor and SLConfigDescriptor from
|
||||
* AAC CSD data.
|
||||
*/
|
||||
class ES_Descriptor : public MuxerOperation {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
uint8_t tag; // ESDescrTag
|
||||
uint8_t length;
|
||||
uint16_t ES_ID;
|
||||
std::bitset<1> streamDependenceFlag;
|
||||
std::bitset<1> URL_Flag;
|
||||
std::bitset<1> reserved;
|
||||
std::bitset<5> streamPriority;
|
||||
|
||||
nsTArray<uint8_t> DecodeSpecificInfo;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
nsresult Find(const nsACString& aType,
|
||||
nsTArray<RefPtr<MuxerOperation>>& aOperations) override;
|
||||
|
||||
// ES_Descriptor methods
|
||||
ES_Descriptor(ISOControl* aControl);
|
||||
~ES_Descriptor();
|
||||
|
||||
protected:
|
||||
ISOControl* mControl;
|
||||
};
|
||||
|
||||
// 14496-14 5.6 'Sample Description Boxes'
|
||||
// Box type: 'esds'
|
||||
class ESDBox : public FullBox {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
RefPtr<ES_Descriptor> es_descriptor;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// ESDBox methods
|
||||
ESDBox(ISOControl* aControl);
|
||||
~ESDBox();
|
||||
};
|
||||
|
||||
// 14496-14 5.6 'Sample Description Boxes'
|
||||
// Box type: 'mp4a'
|
||||
class MP4AudioSampleEntry : public AudioSampleEntry {
|
||||
public:
|
||||
// ISO BMFF members
|
||||
RefPtr<ESDBox> es;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// MP4AudioSampleEntry methods
|
||||
MP4AudioSampleEntry(ISOControl* aControl);
|
||||
~MP4AudioSampleEntry();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MP4ESDS_h_
|
||||
@@ -1,57 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 "nsTArray.h"
|
||||
|
||||
#ifndef MuxerOperation_h_
|
||||
#define MuxerOperation_h_
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* The interface for ISO box. All Boxes inherit from this interface.
|
||||
* Generate() and Write() are needed to be called to produce a complete box.
|
||||
*
|
||||
* Generate() will generate all the data structures and their size.
|
||||
*
|
||||
* Write() will write all data into muxing output stream (ISOControl actually)
|
||||
* and update the data which can't be known at Generate() (for example, the
|
||||
* offset of the video data in mp4 file).
|
||||
*
|
||||
* ISO base media format is composed of several container boxes and the contained
|
||||
* boxes. The container boxes hold a list of MuxerOperation which is implemented
|
||||
* by contained boxes. The contained boxes will be called via the list.
|
||||
* For example:
|
||||
* MovieBox (container) ---> boxes (array of MuxerOperation)
|
||||
* |---> MovieHeaderBox (full box)
|
||||
* |---> TrakBox (container)
|
||||
* |---> MovieExtendsBox (container)
|
||||
*
|
||||
* The complete box structure can be found at 14496-12 E.2 "The‘isom’brand".
|
||||
*/
|
||||
class MuxerOperation {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MuxerOperation)
|
||||
|
||||
// Generate data of this box and its contained box, and calculate box size.
|
||||
virtual nsresult Generate(uint32_t* aBoxSize) = 0;
|
||||
|
||||
// Write data to stream.
|
||||
virtual nsresult Write() = 0;
|
||||
|
||||
// Find the box type via its name (name is the box type defined in 14496-12;
|
||||
// for example, 'moov' is the name of MovieBox).
|
||||
// It can only look child boxes including itself and the box in the boxes
|
||||
// list if exists. It can't look parent boxes.
|
||||
virtual nsresult Find(const nsACString& aType,
|
||||
nsTArray<RefPtr<MuxerOperation>>& aOperations) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~MuxerOperation() {}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,22 +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 += [
|
||||
'ISOMediaWriter.h',
|
||||
'ISOTrackMetadata.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AMRBox.cpp',
|
||||
'AVCBox.cpp',
|
||||
'EVRCBox.cpp',
|
||||
'ISOControl.cpp',
|
||||
'ISOMediaBoxes.cpp',
|
||||
'ISOMediaWriter.cpp',
|
||||
'MP4ESDS.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
@@ -7,9 +7,6 @@
|
||||
with Files('*'):
|
||||
BUG_COMPONENT = ('Core', 'Video/Audio: Recording')
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
DIRS += ['fmp4_muxer']
|
||||
|
||||
EXPORTS += [
|
||||
'ContainerWriter.h',
|
||||
'EncodedFrameContainer.h',
|
||||
@@ -37,16 +34,6 @@ FINAL_LIBRARY = 'xul'
|
||||
# These includes are from Android JB, for use of MediaCodec.
|
||||
LOCAL_INCLUDES += ['/ipc/chromium/src']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] > '15':
|
||||
LOCAL_INCLUDES += [
|
||||
'%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
||||
'frameworks/av/include/media',
|
||||
'frameworks/native/include',
|
||||
'frameworks/native/opengl/include',
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
# Suppress some GCC warnings being treated as errors:
|
||||
|
||||
@@ -1,667 +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 "GonkOmxPlatformLayer.h"
|
||||
|
||||
#include <binder/MemoryDealer.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <media/IOMX.h>
|
||||
#include <media/stagefright/MediaCodecList.h>
|
||||
#include <utils/List.h>
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
#include "mozilla/layers/GrallocTextureClient.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/TextureClientRecycleAllocator.h"
|
||||
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "OmxDataDecoder.h"
|
||||
|
||||
|
||||
#ifdef LOG
|
||||
#undef LOG
|
||||
#endif
|
||||
|
||||
#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("GonkOmxPlatformLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
|
||||
#define CHECK_ERR(err) \
|
||||
if (err != OK) { \
|
||||
LOG("error %d at %s", err, __func__); \
|
||||
return NS_ERROR_FAILURE; \
|
||||
} \
|
||||
|
||||
// Android proprietary value.
|
||||
#define ANDROID_OMX_VIDEO_CodingVP8 (static_cast<OMX_VIDEO_CODINGTYPE>(9))
|
||||
|
||||
using namespace android;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// In Gonk, the software component name has prefix "OMX.google". It needs to
|
||||
// have a way to use hardware codec first.
|
||||
bool IsSoftwareCodec(const char* aComponentName)
|
||||
{
|
||||
nsAutoCString str(aComponentName);
|
||||
return (str.Find(NS_LITERAL_CSTRING("OMX.google.")) == -1 ? false : true);
|
||||
}
|
||||
|
||||
bool IsInEmulator()
|
||||
{
|
||||
char propQemu[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.kernel.qemu", propQemu, "");
|
||||
return !strncmp(propQemu, "1", 1);
|
||||
}
|
||||
|
||||
class GonkOmxObserver : public BnOMXObserver {
|
||||
public:
|
||||
void onMessage(const omx_message& aMsg)
|
||||
{
|
||||
switch (aMsg.type) {
|
||||
case omx_message::EVENT:
|
||||
{
|
||||
sp<GonkOmxObserver> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
|
||||
if (self->mClient && self->mClient->Event(aMsg.u.event_data.event,
|
||||
aMsg.u.event_data.data1,
|
||||
aMsg.u.event_data.data2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
});
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
break;
|
||||
}
|
||||
case omx_message::EMPTY_BUFFER_DONE:
|
||||
{
|
||||
sp<GonkOmxObserver> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
|
||||
if (!self->mPromiseLayer) {
|
||||
return;
|
||||
}
|
||||
BufferData::BufferID id = (BufferData::BufferID)aMsg.u.buffer_data.buffer;
|
||||
self->mPromiseLayer->EmptyFillBufferDone(OMX_DirInput, id);
|
||||
});
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
break;
|
||||
}
|
||||
case omx_message::FILL_BUFFER_DONE:
|
||||
{
|
||||
sp<GonkOmxObserver> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
|
||||
if (!self->mPromiseLayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: these codes look a little ugly, it'd be better to improve them.
|
||||
RefPtr<BufferData> buf;
|
||||
BufferData::BufferID id = (BufferData::BufferID)aMsg.u.extended_buffer_data.buffer;
|
||||
buf = self->mPromiseLayer->FindAndRemoveBufferHolder(OMX_DirOutput, id);
|
||||
MOZ_RELEASE_ASSERT(buf);
|
||||
GonkBufferData* gonkBuffer = static_cast<GonkBufferData*>(buf.get());
|
||||
|
||||
// Copy the critical information to local buffer.
|
||||
if (gonkBuffer->IsLocalBuffer()) {
|
||||
gonkBuffer->mBuffer->nOffset = aMsg.u.extended_buffer_data.range_offset;
|
||||
gonkBuffer->mBuffer->nFilledLen = aMsg.u.extended_buffer_data.range_length;
|
||||
gonkBuffer->mBuffer->nFlags = aMsg.u.extended_buffer_data.flags;
|
||||
gonkBuffer->mBuffer->nTimeStamp = aMsg.u.extended_buffer_data.timestamp;
|
||||
}
|
||||
self->mPromiseLayer->EmptyFillBufferDone(OMX_DirOutput, buf);
|
||||
});
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOG("Unhandle event %d", aMsg.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
mPromiseLayer = nullptr;
|
||||
mClient = nullptr;
|
||||
}
|
||||
|
||||
GonkOmxObserver(TaskQueue* aTaskQueue, OmxPromiseLayer* aPromiseLayer, OmxDataDecoder* aDataDecoder)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mPromiseLayer(aPromiseLayer)
|
||||
, mClient(aDataDecoder)
|
||||
{}
|
||||
|
||||
protected:
|
||||
RefPtr<TaskQueue> mTaskQueue;
|
||||
// TODO:
|
||||
// we should combine both event handlers into one. And we should provide
|
||||
// an unified way for event handling in OmxPlatformLayer class.
|
||||
RefPtr<OmxPromiseLayer> mPromiseLayer;
|
||||
RefPtr<OmxDataDecoder> mClient;
|
||||
};
|
||||
|
||||
// This class allocates Gralloc buffer and manages TextureClient's recycle.
|
||||
class GonkTextureClientRecycleHandler : public layers::ITextureClientRecycleAllocator
|
||||
{
|
||||
typedef MozPromise<layers::TextureClient*, nsresult, /* IsExclusive = */ true> TextureClientRecyclePromise;
|
||||
|
||||
public:
|
||||
GonkTextureClientRecycleHandler(OMX_VIDEO_PORTDEFINITIONTYPE& aDef)
|
||||
: ITextureClientRecycleAllocator()
|
||||
, mMonitor("GonkTextureClientRecycleHandler")
|
||||
{
|
||||
RefPtr<layers::ImageBridgeChild> bridge = layers::ImageBridgeChild::GetSingleton();
|
||||
|
||||
// Allocate Gralloc texture memory.
|
||||
layers::GrallocTextureData* textureData =
|
||||
layers::GrallocTextureData::Create(gfx::IntSize(aDef.nFrameWidth, aDef.nFrameHeight),
|
||||
aDef.eColorFormat,
|
||||
gfx::BackendType::NONE,
|
||||
GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_READ_OFTEN,
|
||||
bridge);
|
||||
|
||||
mGraphBuffer = textureData->GetGraphicBuffer();
|
||||
MOZ_ASSERT(mGraphBuffer.get());
|
||||
|
||||
mTextureClient =
|
||||
layers::TextureClient::CreateWithData(textureData,
|
||||
layers::TextureFlags::DEALLOCATE_CLIENT | layers::TextureFlags::RECYCLE,
|
||||
bridge);
|
||||
MOZ_ASSERT(mTextureClient);
|
||||
|
||||
mPromise.SetMonitor(&mMonitor);
|
||||
}
|
||||
|
||||
RefPtr<TextureClientRecyclePromise> WaitforRecycle()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_ASSERT(!!mGraphBuffer.get());
|
||||
|
||||
mTextureClient->SetRecycleAllocator(this);
|
||||
return mPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
// DO NOT use smart pointer to receive TextureClient; otherwise it will
|
||||
// distrupt the reference count.
|
||||
layers::TextureClient* GetTextureClient()
|
||||
{
|
||||
return mTextureClient;
|
||||
}
|
||||
|
||||
GraphicBuffer* GetGraphicBuffer()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
return mGraphBuffer.get();
|
||||
}
|
||||
|
||||
// This function is called from layers thread.
|
||||
void RecycleTextureClient(layers::TextureClient* aClient) override
|
||||
{
|
||||
MOZ_ASSERT(mTextureClient == aClient);
|
||||
|
||||
// Clearing the recycle allocator drops a reference, so make sure we stay alive
|
||||
// for the duration of this function.
|
||||
RefPtr<GonkTextureClientRecycleHandler> kungFuDeathGrip(this);
|
||||
aClient->SetRecycleAllocator(nullptr);
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
mPromise.ResolveIfExists(mTextureClient, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
mPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
|
||||
|
||||
// DO NOT clear TextureClient here.
|
||||
// The ref count could be 1 and RecycleCallback will be called if we clear
|
||||
// the ref count here. That breaks the whole mechanism. (RecycleCallback
|
||||
// should be called from layers)
|
||||
mGraphBuffer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
// Because TextureClient calls RecycleCallbackl when ref count is 1, so we
|
||||
// should hold only one reference here and use raw pointer when out of this
|
||||
// class.
|
||||
RefPtr<layers::TextureClient> mTextureClient;
|
||||
|
||||
// It is protected by mMonitor.
|
||||
sp<android::GraphicBuffer> mGraphBuffer;
|
||||
|
||||
// It is protected by mMonitor.
|
||||
MozPromiseHolder<TextureClientRecyclePromise> mPromise;
|
||||
|
||||
Monitor mMonitor;
|
||||
};
|
||||
|
||||
GonkBufferData::GonkBufferData(bool aLiveInLocal,
|
||||
GonkOmxPlatformLayer* aGonkPlatformLayer)
|
||||
: BufferData(nullptr)
|
||||
, mId(0)
|
||||
, mGonkPlatformLayer(aGonkPlatformLayer)
|
||||
{
|
||||
if (!aLiveInLocal) {
|
||||
mMirrorBuffer = new OMX_BUFFERHEADERTYPE;
|
||||
PodZero(mMirrorBuffer.get());
|
||||
mBuffer = mMirrorBuffer.get();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkBufferData::ReleaseBuffer()
|
||||
{
|
||||
if (mTextureClientRecycleHandler) {
|
||||
mTextureClientRecycleHandler->Shutdown();
|
||||
mTextureClientRecycleHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkBufferData::InitSharedMemory(android::IMemory* aMemory)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mMirrorBuffer.get());
|
||||
|
||||
// aMemory is a IPC memory, it is safe to use it here.
|
||||
mBuffer->pBuffer = (OMX_U8*)aMemory->pointer();
|
||||
mBuffer->nAllocLen = aMemory->size();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkBufferData::InitLocalBuffer(IOMX::buffer_id aId)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mMirrorBuffer.get());
|
||||
|
||||
mBuffer = (OMX_BUFFERHEADERTYPE*)aId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkBufferData::InitGraphicBuffer(OMX_VIDEO_PORTDEFINITIONTYPE& aDef)
|
||||
{
|
||||
mTextureClientRecycleHandler = new GonkTextureClientRecycleHandler(aDef);
|
||||
|
||||
if (!mTextureClientRecycleHandler->GetGraphicBuffer()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaData>
|
||||
GonkBufferData::GetPlatformMediaData()
|
||||
{
|
||||
if (mGonkPlatformLayer->GetTrackInfo()->GetAsAudioInfo()) {
|
||||
// This is audio decoding.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mTextureClientRecycleHandler) {
|
||||
// There is no GraphicBuffer, it should fallback to normal YUV420 VideoData.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VideoInfo info(*mGonkPlatformLayer->GetTrackInfo()->GetAsVideoInfo());
|
||||
RefPtr<VideoData> data =
|
||||
VideoData::CreateAndCopyIntoTextureClient(info,
|
||||
0,
|
||||
mBuffer->nTimeStamp,
|
||||
1,
|
||||
mTextureClientRecycleHandler->GetTextureClient(),
|
||||
false,
|
||||
0,
|
||||
info.ImageRect());
|
||||
LOG("%p, disp width %d, height %d, pic width %d, height %d, time %ld",
|
||||
this, info.mDisplay.width, info.mDisplay.height,
|
||||
info.mImage.width, info.mImage.height, mBuffer->nTimeStamp);
|
||||
|
||||
// Get TextureClient Promise here to wait for resolved.
|
||||
RefPtr<GonkBufferData> self(this);
|
||||
mTextureClientRecycleHandler->WaitforRecycle()
|
||||
->Then(mGonkPlatformLayer->GetTaskQueue(), __func__,
|
||||
[self] () {
|
||||
self->mPromise.ResolveIfExists(self, __func__);
|
||||
},
|
||||
[self] () {
|
||||
OmxBufferFailureHolder failure(OMX_ErrorUndefined, self);
|
||||
self->mPromise.RejectIfExists(failure, __func__);
|
||||
});
|
||||
|
||||
return data.forget();
|
||||
}
|
||||
|
||||
GonkOmxPlatformLayer::GonkOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
|
||||
OmxPromiseLayer* aPromiseLayer,
|
||||
TaskQueue* aTaskQueue,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mImageContainer(aImageContainer)
|
||||
, mNode(0)
|
||||
{
|
||||
mOmxObserver = new GonkOmxObserver(mTaskQueue, aPromiseLayer, aDataDecoder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkOmxPlatformLayer::AllocateOmxBuffer(OMX_DIRTYPE aType,
|
||||
BUFFERLIST* aBufferList)
|
||||
{
|
||||
MOZ_ASSERT(!mMemoryDealer[aType].get());
|
||||
|
||||
// Get port definition.
|
||||
OMX_PARAM_PORTDEFINITIONTYPE def;
|
||||
nsTArray<uint32_t> portindex;
|
||||
GetPortIndices(portindex);
|
||||
for (auto idx : portindex) {
|
||||
InitOmxParameter(&def);
|
||||
def.nPortIndex = idx;
|
||||
|
||||
OMX_ERRORTYPE err = GetParameter(OMX_IndexParamPortDefinition,
|
||||
&def,
|
||||
sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
|
||||
if (err != OMX_ErrorNone) {
|
||||
return NS_ERROR_FAILURE;
|
||||
} else if (def.eDir == aType) {
|
||||
LOG("Get OMX_IndexParamPortDefinition: port: %d, type: %d", def.nPortIndex, def.eDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t t = 0;
|
||||
|
||||
// Configure video output GraphicBuffer for video decoding acceleration.
|
||||
bool useGralloc = false;
|
||||
if (aType == OMX_DirOutput && mQuirks.test(kRequiresAllocateBufferOnOutputPorts) &&
|
||||
(def.eDomain == OMX_PortDomainVideo)) {
|
||||
if (NS_FAILED(EnableOmxGraphicBufferPort(def))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
LOG("Enable OMX GraphicBuffer port, number %d, width %d, height %d", def.nBufferCountActual,
|
||||
def.format.video.nFrameWidth, def.format.video.nFrameHeight);
|
||||
|
||||
useGralloc = true;
|
||||
|
||||
t = 1024; // MemoryDealer doesn't like 0, it's just for MemoryDealer happy.
|
||||
} else {
|
||||
t = def.nBufferCountActual * def.nBufferSize;
|
||||
LOG("Buffer count %d, buffer size %d", def.nBufferCountActual, def.nBufferSize);
|
||||
}
|
||||
|
||||
bool liveinlocal = mOmx->livesLocally(mNode, getpid());
|
||||
|
||||
// MemoryDealer is a IPC buffer allocator in Gonk because IOMX is actually
|
||||
// lives in mediaserver.
|
||||
mMemoryDealer[aType] = new MemoryDealer(t, "Gecko-OMX");
|
||||
for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
|
||||
RefPtr<GonkBufferData> buffer;
|
||||
IOMX::buffer_id bufferID;
|
||||
status_t st;
|
||||
nsresult rv;
|
||||
|
||||
buffer = new GonkBufferData(liveinlocal, this);
|
||||
if (useGralloc) {
|
||||
// Buffer is lived remotely. Use GraphicBuffer for decoded video frame display.
|
||||
rv = buffer->InitGraphicBuffer(def.format.video);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
st = mOmx->useGraphicBuffer(mNode,
|
||||
def.nPortIndex,
|
||||
buffer->mTextureClientRecycleHandler->GetGraphicBuffer(),
|
||||
&bufferID);
|
||||
CHECK_ERR(st);
|
||||
} else {
|
||||
sp<IMemory> mem = mMemoryDealer[aType]->allocate(def.nBufferSize);
|
||||
MOZ_ASSERT(mem.get());
|
||||
|
||||
if ((mQuirks.test(kRequiresAllocateBufferOnInputPorts) && aType == OMX_DirInput) ||
|
||||
(mQuirks.test(kRequiresAllocateBufferOnOutputPorts) && aType == OMX_DirOutput)) {
|
||||
// Buffer is lived remotely. We allocate a local OMX_BUFFERHEADERTYPE
|
||||
// as the mirror of the remote OMX_BUFFERHEADERTYPE.
|
||||
st = mOmx->allocateBufferWithBackup(mNode, aType, mem, &bufferID);
|
||||
CHECK_ERR(st);
|
||||
rv = buffer->InitSharedMemory(mem.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// Buffer is lived locally, bufferID is the actually OMX_BUFFERHEADERTYPE
|
||||
// pointer.
|
||||
st = mOmx->useBuffer(mNode, aType, mem, &bufferID);
|
||||
CHECK_ERR(st);
|
||||
rv = buffer->InitLocalBuffer(bufferID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
rv = buffer->SetBufferId(bufferID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aBufferList->AppendElement(buffer);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkOmxPlatformLayer::ReleaseOmxBuffer(OMX_DIRTYPE aType,
|
||||
BUFFERLIST* aBufferList)
|
||||
{
|
||||
status_t st;
|
||||
uint32_t len = aBufferList->Length();
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
GonkBufferData* buffer = static_cast<GonkBufferData*>(aBufferList->ElementAt(i).get());
|
||||
IOMX::buffer_id id = (OMX_BUFFERHEADERTYPE*) buffer->ID();
|
||||
st = mOmx->freeBuffer(mNode, aType, id);
|
||||
if (st != OK) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
buffer->ReleaseBuffer();
|
||||
}
|
||||
aBufferList->Clear();
|
||||
mMemoryDealer[aType].clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkOmxPlatformLayer::EnableOmxGraphicBufferPort(OMX_PARAM_PORTDEFINITIONTYPE& aDef)
|
||||
{
|
||||
status_t st;
|
||||
|
||||
st = mOmx->enableGraphicBuffers(mNode, aDef.nPortIndex, OMX_TRUE);
|
||||
CHECK_ERR(st);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::GetState(OMX_STATETYPE* aType)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->getState(mNode, aType);
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::GetParameter(OMX_INDEXTYPE aParamIndex,
|
||||
OMX_PTR aComponentParameterStructure,
|
||||
OMX_U32 aComponentParameterSize)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->getParameter(mNode,
|
||||
aParamIndex,
|
||||
aComponentParameterStructure,
|
||||
aComponentParameterSize);
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::SetParameter(OMX_INDEXTYPE aParamIndex,
|
||||
OMX_PTR aComponentParameterStructure,
|
||||
OMX_U32 aComponentParameterSize)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->setParameter(mNode,
|
||||
aParamIndex,
|
||||
aComponentParameterStructure,
|
||||
aComponentParameterSize);
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkOmxPlatformLayer::Shutdown()
|
||||
{
|
||||
mOmx->freeNode(mNode);
|
||||
mOmxObserver->Shutdown();
|
||||
mOmxObserver = nullptr;
|
||||
mOmxClient.disconnect();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::InitOmxToStateLoaded(const TrackInfo* aInfo)
|
||||
{
|
||||
mInfo = aInfo;
|
||||
status_t err = mOmxClient.connect();
|
||||
if (err != OK) {
|
||||
return OMX_ErrorUndefined;
|
||||
}
|
||||
mOmx = mOmxClient.interface();
|
||||
if (!mOmx.get()) {
|
||||
return OMX_ErrorUndefined;
|
||||
}
|
||||
|
||||
LOG("find componenet for mime type %s", mInfo->mMimeType.Data());
|
||||
|
||||
nsTArray<ComponentInfo> components;
|
||||
if (FindComponents(mInfo->mMimeType, &components)) {
|
||||
for (auto comp : components) {
|
||||
if (LoadComponent(comp)) {
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG("no component is loaded");
|
||||
return OMX_ErrorUndefined;
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::EmptyThisBuffer(BufferData* aData)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->emptyBuffer(mNode,
|
||||
(IOMX::buffer_id)aData->ID(),
|
||||
aData->mBuffer->nOffset,
|
||||
aData->mBuffer->nFilledLen,
|
||||
aData->mBuffer->nFlags,
|
||||
aData->mBuffer->nTimeStamp);
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::FillThisBuffer(BufferData* aData)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->fillBuffer(mNode, (IOMX::buffer_id)aData->ID());
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::SendCommand(OMX_COMMANDTYPE aCmd,
|
||||
OMX_U32 aParam1,
|
||||
OMX_PTR aCmdData)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->sendCommand(mNode, aCmd, aParam1);
|
||||
}
|
||||
|
||||
bool
|
||||
GonkOmxPlatformLayer::LoadComponent(const ComponentInfo& aComponent)
|
||||
{
|
||||
status_t err = mOmx->allocateNode(aComponent.mName, mOmxObserver, &mNode);
|
||||
if (err == OK) {
|
||||
mQuirks = aComponent.mQuirks;
|
||||
LOG("Load OpenMax component %s, alloc input %d, alloc output %d, live locally %d",
|
||||
aComponent.mName, mQuirks.test(kRequiresAllocateBufferOnInputPorts),
|
||||
mQuirks.test(kRequiresAllocateBufferOnOutputPorts),
|
||||
mOmx->livesLocally(mNode, getpid()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
layers::ImageContainer*
|
||||
GonkOmxPlatformLayer::GetImageContainer()
|
||||
{
|
||||
return mImageContainer;
|
||||
}
|
||||
|
||||
const TrackInfo*
|
||||
GonkOmxPlatformLayer::GetTrackInfo()
|
||||
{
|
||||
return mInfo;
|
||||
}
|
||||
|
||||
bool
|
||||
GonkOmxPlatformLayer::FindComponents(const nsACString& aMimeType,
|
||||
nsTArray<ComponentInfo>* aComponents)
|
||||
{
|
||||
static const MediaCodecList* codecs = MediaCodecList::getInstance();
|
||||
|
||||
bool useHardwareCodecOnly = false;
|
||||
|
||||
// H264 and H263 has different profiles, software codec doesn't support high profile.
|
||||
// So we use hardware codec only.
|
||||
if (!IsInEmulator() &&
|
||||
(aMimeType.EqualsLiteral("video/avc") ||
|
||||
aMimeType.EqualsLiteral("video/mp4") ||
|
||||
aMimeType.EqualsLiteral("video/mp4v-es") ||
|
||||
aMimeType.EqualsLiteral("video/3gp"))) {
|
||||
useHardwareCodecOnly = true;
|
||||
}
|
||||
|
||||
const char* mime = aMimeType.Data();
|
||||
// Translate VP8 MIME type to Android format.
|
||||
if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
|
||||
mime = "video/x-vnd.on2.vp8";
|
||||
}
|
||||
|
||||
size_t start = 0;
|
||||
bool found = false;
|
||||
while (true) {
|
||||
ssize_t index = codecs->findCodecByType(mime, false /* encoder */, start);
|
||||
if (index < 0) {
|
||||
break;
|
||||
}
|
||||
start = index + 1;
|
||||
|
||||
const char* name = codecs->getCodecName(index);
|
||||
if (IsSoftwareCodec(name) && useHardwareCodecOnly) {
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
|
||||
if (!aComponents) {
|
||||
continue;
|
||||
}
|
||||
ComponentInfo* comp = aComponents->AppendElement();
|
||||
comp->mName = name;
|
||||
if (codecs->codecHasQuirk(index, "requires-allocate-on-input-ports")) {
|
||||
comp->mQuirks.set(kRequiresAllocateBufferOnInputPorts);
|
||||
}
|
||||
if (codecs->codecHasQuirk(index, "requires-allocate-on-output-ports")) {
|
||||
comp->mQuirks.set(kRequiresAllocateBufferOnOutputPorts);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
OMX_VIDEO_CODINGTYPE
|
||||
GonkOmxPlatformLayer::CompressionFormat()
|
||||
{
|
||||
MOZ_ASSERT(mInfo);
|
||||
|
||||
return mInfo->mMimeType.EqualsLiteral("video/webm; codecs=vp8") ?
|
||||
ANDROID_OMX_VIDEO_CodingVP8 : OmxPlatformLayer::CompressionFormat();
|
||||
}
|
||||
|
||||
} // mozilla
|
||||
@@ -1,205 +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(GonkOmxPlatformLayer_h_)
|
||||
#define GonkOmxPlatformLayer_h_
|
||||
|
||||
#pragma GCC visibility push(default)
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <media/stagefright/OMXClient.h>
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
#include "OMX_Component.h"
|
||||
|
||||
#include "OmxPlatformLayer.h"
|
||||
|
||||
class nsACString;
|
||||
|
||||
namespace android {
|
||||
class IMemory;
|
||||
class MemoryDealer;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class GonkOmxObserver;
|
||||
class GonkOmxPlatformLayer;
|
||||
class GonkTextureClientRecycleHandler;
|
||||
|
||||
/*
|
||||
* Due to Android's omx node could live in local process (client) or remote
|
||||
* process (mediaserver). And there are 3 kinds of buffer in Android OMX.
|
||||
*
|
||||
* 1.
|
||||
* When buffer is in local process, the IOMX::buffer_id is OMX_BUFFERHEADERTYPE
|
||||
* pointer actually, it is safe to use it directly.
|
||||
*
|
||||
* 2.
|
||||
* When buffer is in remote process, the OMX_BUFFERHEADERTYPE pointer is 'IN' the
|
||||
* remote process. It can't be used in local process, so here it allocates a
|
||||
* local OMX_BUFFERHEADERTYPE. The raw/decoded data is in the android shared
|
||||
* memory, IMemory.
|
||||
*
|
||||
* 3.
|
||||
* When buffer is in remote process for the display output port. It uses
|
||||
* GraphicBuffer to accelerate the decoding and display.
|
||||
*
|
||||
*/
|
||||
class GonkBufferData : public OmxPromiseLayer::BufferData {
|
||||
protected:
|
||||
virtual ~GonkBufferData() {}
|
||||
|
||||
public:
|
||||
GonkBufferData(bool aLiveInLocal,
|
||||
GonkOmxPlatformLayer* aLayer);
|
||||
|
||||
BufferID ID() override
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaData> GetPlatformMediaData() override;
|
||||
|
||||
bool IsLocalBuffer()
|
||||
{
|
||||
return !!mMirrorBuffer.get();
|
||||
}
|
||||
|
||||
void ReleaseBuffer();
|
||||
|
||||
nsresult SetBufferId(android::IOMX::buffer_id aId)
|
||||
{
|
||||
mId = aId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The mBuffer is in local process. And aId is actually the OMX_BUFFERHEADERTYPE
|
||||
// pointer. It doesn't need a mirror buffer.
|
||||
nsresult InitLocalBuffer(android::IOMX::buffer_id aId);
|
||||
|
||||
// aMemory is an IPC based memory which will be used as the pBuffer in
|
||||
// mBuffer. And the mBuffer will be the mirror OMX_BUFFERHEADERTYPE
|
||||
// of the one in the remote process.
|
||||
nsresult InitSharedMemory(android::IMemory* aMemory);
|
||||
|
||||
// GraphicBuffer is for video decoding acceleration on output port.
|
||||
// Then mBuffer is the mirror OMX_BUFFERHEADERTYPE of the one in the remote
|
||||
// process.
|
||||
nsresult InitGraphicBuffer(OMX_VIDEO_PORTDEFINITIONTYPE& aDef);
|
||||
|
||||
// Android OMX uses this id to pass the buffer between OMX component and
|
||||
// client.
|
||||
android::IOMX::buffer_id mId;
|
||||
|
||||
// mMirrorBuffer are used only when the omx node is in mediaserver.
|
||||
// Due to IPC problem, the mId is the OMX_BUFFERHEADERTYPE address in mediaserver.
|
||||
// It can't mapping to client process, so we need a local OMX_BUFFERHEADERTYPE
|
||||
// here to mirror the remote OMX_BUFFERHEADERTYPE in mediaserver.
|
||||
nsAutoPtr<OMX_BUFFERHEADERTYPE> mMirrorBuffer;
|
||||
|
||||
// It creates GraphicBuffer and manages TextureClient.
|
||||
RefPtr<GonkTextureClientRecycleHandler> mTextureClientRecycleHandler;
|
||||
|
||||
GonkOmxPlatformLayer* mGonkPlatformLayer;
|
||||
};
|
||||
|
||||
class GonkOmxPlatformLayer : public OmxPlatformLayer {
|
||||
public:
|
||||
enum {
|
||||
kRequiresAllocateBufferOnInputPorts = 0,
|
||||
kRequiresAllocateBufferOnOutputPorts,
|
||||
QUIRKS,
|
||||
};
|
||||
typedef std::bitset<QUIRKS> Quirks;
|
||||
|
||||
struct ComponentInfo {
|
||||
const char* mName;
|
||||
Quirks mQuirks;
|
||||
};
|
||||
|
||||
GonkOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
|
||||
OmxPromiseLayer* aPromiseLayer,
|
||||
TaskQueue* aTaskQueue,
|
||||
layers::ImageContainer* aImageContainer);
|
||||
|
||||
nsresult AllocateOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBufferList) override;
|
||||
|
||||
nsresult ReleaseOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBufferList) override;
|
||||
|
||||
OMX_ERRORTYPE GetState(OMX_STATETYPE* aType) override;
|
||||
|
||||
OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE aParamIndex,
|
||||
OMX_PTR aComponentParameterStructure,
|
||||
OMX_U32 aComponentParameterSize) override;
|
||||
|
||||
OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE nIndex,
|
||||
OMX_PTR aComponentParameterStructure,
|
||||
OMX_U32 aComponentParameterSize) override;
|
||||
|
||||
OMX_ERRORTYPE InitOmxToStateLoaded(const TrackInfo* aInfo) override;
|
||||
|
||||
OMX_ERRORTYPE EmptyThisBuffer(BufferData* aData) override;
|
||||
|
||||
OMX_ERRORTYPE FillThisBuffer(BufferData* aData) override;
|
||||
|
||||
OMX_ERRORTYPE SendCommand(OMX_COMMANDTYPE aCmd,
|
||||
OMX_U32 aParam1,
|
||||
OMX_PTR aCmdData) override;
|
||||
|
||||
nsresult Shutdown() override;
|
||||
|
||||
static bool FindComponents(const nsACString& aMimeType,
|
||||
nsTArray<ComponentInfo>* aComponents = nullptr);
|
||||
|
||||
// Android/QCOM decoder uses its own OMX_VIDEO_CodingVP8 definition in
|
||||
// frameworks/native/media/include/openmax/OMX_Video.h, not the one defined
|
||||
// in OpenMAX v1.1.2 OMX_VideoExt.h
|
||||
OMX_VIDEO_CODINGTYPE CompressionFormat() override;
|
||||
|
||||
protected:
|
||||
friend GonkBufferData;
|
||||
|
||||
layers::ImageContainer* GetImageContainer();
|
||||
|
||||
const TrackInfo* GetTrackInfo();
|
||||
|
||||
TaskQueue* GetTaskQueue()
|
||||
{
|
||||
return mTaskQueue;
|
||||
}
|
||||
|
||||
nsresult EnableOmxGraphicBufferPort(OMX_PARAM_PORTDEFINITIONTYPE& aDef);
|
||||
|
||||
bool LoadComponent(const ComponentInfo& aComponent);
|
||||
|
||||
friend class GonkOmxObserver;
|
||||
|
||||
RefPtr<TaskQueue> mTaskQueue;
|
||||
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
|
||||
// OMX_DirInput is 0, OMX_DirOutput is 1.
|
||||
android::sp<android::MemoryDealer> mMemoryDealer[2];
|
||||
|
||||
android::sp<GonkOmxObserver> mOmxObserver;
|
||||
|
||||
android::sp<android::IOMX> mOmx;
|
||||
|
||||
android::IOMX::node_id mNode;
|
||||
|
||||
android::OMXClient mOmxClient;
|
||||
|
||||
Quirks mQuirks;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC visibility pop
|
||||
|
||||
#endif // GonkOmxPlatformLayer_h_
|
||||
@@ -21,29 +21,6 @@ LOCAL_INCLUDES += [
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and (CONFIG['ANDROID_VERSION'] == '19' or CONFIG['ANDROID_VERSION'] == '20'):
|
||||
# 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'
|
||||
]
|
||||
CXXFLAGS += [
|
||||
'-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
||||
'frameworks/base/include/binder',
|
||||
'frameworks/base/include/utils',
|
||||
]
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'GonkOmxPlatformLayer.cpp',
|
||||
]
|
||||
EXTRA_DSO_LDOPTS += [
|
||||
'-libbinder',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# 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 CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
|
||||
if CONFIG['OS_TARGET'] != 'WINNT':
|
||||
Library('media_standalone')
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
||||
@@ -42,16 +42,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
UNIFIED_SOURCES += ['OSXRunLoopSingleton.cpp']
|
||||
EXPORTS += ['OSXRunLoopSingleton.h']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
if CONFIG['ANDROID_VERSION'] >= '17':
|
||||
LOCAL_INCLUDES += [
|
||||
'%' + '%s/frameworks/wilhelm/include' % CONFIG['ANDROID_SOURCE'],
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'%' + '%s/system/media/wilhelm/include' % CONFIG['ANDROID_SOURCE'],
|
||||
]
|
||||
|
||||
if CONFIG['_MSC_VER']:
|
||||
DEFINES['__PRETTY_FUNCTION__'] = '__FUNCSIG__'
|
||||
|
||||
|
||||
@@ -105,13 +105,6 @@ DIRS += [
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DIRS += ['plugins/ipc/hangui']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
DIRS += [
|
||||
'speakermanager',
|
||||
'tethering',
|
||||
'wifi',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_SECUREELEMENT']:
|
||||
DIRS += ['secureelement']
|
||||
|
||||
|
||||
@@ -1,655 +0,0 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* 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 TOPIC_INTERFACE_STATE_CHANGED = "network-interface-state-changed";
|
||||
|
||||
const ETHERNET_NETWORK_IFACE_PREFIX = "eth";
|
||||
const DEFAULT_ETHERNET_NETWORK_IFACE = "eth0";
|
||||
|
||||
const INTERFACE_IPADDR_NULL = "0.0.0.0";
|
||||
const INTERFACE_GATEWAY_NULL = "0.0.0.0";
|
||||
const INTERFACE_PREFIX_NULL = 0;
|
||||
const INTERFACE_MACADDR_NULL = "00:00:00:00:00:00";
|
||||
|
||||
const NETWORK_INTERFACE_UP = "up";
|
||||
const NETWORK_INTERFACE_DOWN = "down";
|
||||
|
||||
const IP_MODE_DHCP = "dhcp";
|
||||
const IP_MODE_STATIC = "static";
|
||||
|
||||
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
|
||||
"@mozilla.org/network/manager;1",
|
||||
"nsINetworkManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
|
||||
"@mozilla.org/network/service;1",
|
||||
"nsINetworkService");
|
||||
|
||||
let 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("-*- EthernetManager: " + s + "\n");
|
||||
};
|
||||
} else {
|
||||
debug = function(s) {};
|
||||
}
|
||||
}
|
||||
updateDebug();
|
||||
|
||||
// nsINetworkInterface
|
||||
|
||||
function EthernetInterface(attr) {
|
||||
this.info.state = attr.state;
|
||||
this.info.type = attr.type;
|
||||
this.info.name = attr.name;
|
||||
this.info.ipMode = attr.ipMode;
|
||||
this.info.ips = [attr.ip];
|
||||
this.info.prefixLengths = [attr.prefixLength];
|
||||
this.info.gateways = [attr.gateway];
|
||||
this.info.dnses = attr.dnses;
|
||||
this.httpProxyHost = "";
|
||||
this.httpProxyPort = 0;
|
||||
}
|
||||
EthernetInterface.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]),
|
||||
|
||||
updateConfig: function(config) {
|
||||
debug("Interface " + this.info.name + " updateConfig " + JSON.stringify(config));
|
||||
this.info.state = (config.state != undefined) ?
|
||||
config.state : this.info.state;
|
||||
this.info.ips = (config.ip != undefined) ? [config.ip] : this.info.ips;
|
||||
this.info.prefixLengths = (config.prefixLength != undefined) ?
|
||||
[config.prefixLength] : this.info.prefixLengths;
|
||||
this.info.gateways = (config.gateway != undefined) ?
|
||||
[config.gateway] : this.info.gateways;
|
||||
this.info.dnses = (config.dnses != undefined) ? config.dnses : this.info.dnses;
|
||||
this.httpProxyHost = (config.httpProxyHost != undefined) ?
|
||||
config.httpProxyHost : this.httpProxyHost;
|
||||
this.httpProxyPort = (config.httpProxyPort != undefined) ?
|
||||
config.httpProxyPort : this.httpProxyPort;
|
||||
this.info.ipMode = (config.ipMode != undefined) ?
|
||||
config.ipMode : this.info.ipMode;
|
||||
},
|
||||
|
||||
info: {
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// nsIEthernetManager
|
||||
|
||||
/*
|
||||
* Network state transition diagram
|
||||
*
|
||||
* ---------- enable --------- connect ----------- disconnect --------------
|
||||
* | Disabled | -----> | Enabled | -------> | Connected | <----------> | Disconnected |
|
||||
* ---------- --------- ----------- connect --------------
|
||||
* ^ | | |
|
||||
* | disable | | |
|
||||
* -----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
function EthernetManager() {
|
||||
debug("EthernetManager start");
|
||||
|
||||
// Interface list.
|
||||
this.ethernetInterfaces = {};
|
||||
|
||||
// Used to memorize last connection information.
|
||||
this.lastStaticConfig = {};
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
}
|
||||
|
||||
EthernetManager.prototype = {
|
||||
classID: Components.ID("a96441dd-36b3-4f7f-963b-2c032e28a039"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIEthernetManager]),
|
||||
|
||||
ethernetInterfaces: null,
|
||||
lastStaticConfig: null,
|
||||
|
||||
observer: function(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "xpcom-shutdown":
|
||||
debug("xpcom-shutdown");
|
||||
|
||||
this._shutdown();
|
||||
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_shutdown: function() {
|
||||
debug("Shuting down");
|
||||
(function onRemove(ifnameList) {
|
||||
if (!ifnameList.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ifname = ifnameList.shift();
|
||||
this.removeInterface(ifname, { notify: onRemove.bind(this, ifnameList) });
|
||||
}).call(this, Object.keys(this.ethernetInterfaces));
|
||||
},
|
||||
|
||||
get interfaceList() {
|
||||
return Object.keys(this.ethernetInterfaces);
|
||||
},
|
||||
|
||||
scan: function(callback) {
|
||||
debug("Scan");
|
||||
|
||||
gNetworkService.getInterfaces(function(success, list) {
|
||||
let ethList = [];
|
||||
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify(ethList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
debug("Found interface " + list[i]);
|
||||
if (!list[i].startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
ethList.push(list[i]);
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback.notify(ethList);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addInterface: function(ifname, callback) {
|
||||
debug("Add interface " + ifname);
|
||||
|
||||
if (!ifname || !ifname.startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Invalid interface.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ethernetInterfaces[ifname]) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface already exists.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
gNetworkService.getInterfaceConfig(ifname, function(success, result) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Netd error.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Since the operation may still succeed with an invalid interface name,
|
||||
// check the mac address as well.
|
||||
if (result.macAddr == INTERFACE_MACADDR_NULL) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Interface not found.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.ethernetInterfaces[ifname] = new EthernetInterface({
|
||||
state: result.link == NETWORK_INTERFACE_UP ?
|
||||
Ci.nsINetworkInfo.NETWORK_STATE_DISABLED :
|
||||
Ci.nsINetworkInfo.NETWORK_STATE_ENABLED,
|
||||
name: ifname,
|
||||
type: Ci.nsINetworkInfo.NETWORK_TYPE_ETHERNET,
|
||||
ip: result.ip,
|
||||
prefixLength: result.prefix,
|
||||
ipMode: IP_MODE_DHCP
|
||||
});
|
||||
|
||||
// Register the interface to NetworkManager.
|
||||
gNetworkManager.registerNetworkInterface(this.ethernetInterfaces[ifname]);
|
||||
|
||||
debug("Add interface " + ifname + " succeeded with " +
|
||||
JSON.stringify(this.ethernetInterfaces[ifname]));
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
removeInterface: function(ifname, callback) {
|
||||
debug("Remove interface " + ifname);
|
||||
|
||||
if (!ifname || !ifname.startsWith(ETHERNET_NETWORK_IFACE_PREFIX)) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Invalid interface.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.ethernetInterfaces[ifname]) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface does not exist.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure interface is disable before removing.
|
||||
this.disable(ifname, { notify: function(success, message) {
|
||||
// Unregister the interface from NetworkManager and also remove it from
|
||||
// the interface list.
|
||||
gNetworkManager.unregisterNetworkInterface(this.ethernetInterfaces[ifname]);
|
||||
delete this.ethernetInterfaces[ifname];
|
||||
|
||||
debug("Remove interface " + ifname + " succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this)});
|
||||
},
|
||||
|
||||
updateInterfaceConfig: function(ifname, config, callback) {
|
||||
debug("Update interface config with " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
if (!config) {
|
||||
if (callback) {
|
||||
callback.notify(false, "No config to update.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Network state can not be modified externally.
|
||||
if (config.state) {
|
||||
delete config.state;
|
||||
}
|
||||
|
||||
let currentIpMode = iface.info.ipMode;
|
||||
|
||||
// Update config.
|
||||
this.ethernetInterfaces[iface.info.name].updateConfig(config);
|
||||
|
||||
// Do not automatically re-connect if the interface is not in connected
|
||||
// state.
|
||||
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let newIpMode = this.ethernetInterfaces[iface.info.name].info.ipMode;
|
||||
|
||||
if (newIpMode == IP_MODE_STATIC) {
|
||||
this._setStaticIP(iface.info.name, callback);
|
||||
return;
|
||||
}
|
||||
if ((currentIpMode == IP_MODE_STATIC) && (newIpMode == IP_MODE_DHCP)) {
|
||||
gNetworkService.stopDhcp(iface.info.name, function(success) {
|
||||
if (success) {
|
||||
debug("DHCP for " + iface.info.name + " stopped.");
|
||||
}
|
||||
});
|
||||
|
||||
// Clear the current network settings before do dhcp request, otherwise
|
||||
// dhcp settings could fail.
|
||||
this.disconnect(iface.info.name, { notify: function(success, message) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify("Disconnect failed.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._runDhcp(iface.info.name, callback);
|
||||
}.bind(this) });
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
enable: function(ifname, callback) {
|
||||
debug("Enable interface " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
// Interface can be only enabled in the state of disabled.
|
||||
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_DISABLED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface already enabled.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
let config = { ifname: iface.info.name,
|
||||
ip: ips.value[0],
|
||||
prefix: prefixLengths.value[0],
|
||||
link: NETWORK_INTERFACE_UP };
|
||||
gNetworkService.setInterfaceConfig(config, function(success) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Netd Error.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.ethernetInterfaces[iface.info.name].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_ENABLED
|
||||
});
|
||||
|
||||
debug("Enable interface " + iface.info.name + " succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
disable: function(ifname, callback) {
|
||||
debug("Disable interface " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_DISABLED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface already disabled.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
gNetworkService.stopDhcp(iface.info.name, function(success) {
|
||||
if (success) {
|
||||
debug("DHCP for " + iface.info.name + " stopped.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
let config = { ifname: iface.info.name,
|
||||
ip: ips.value[0],
|
||||
prefix: prefixLengths.value[0],
|
||||
link: NETWORK_INTERFACE_DOWN };
|
||||
gNetworkService.setInterfaceConfig(config, function(success) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Netd Error.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.ethernetInterfaces[iface.info.name].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_DISABLED
|
||||
});
|
||||
|
||||
debug("Disable interface " + iface.info.name + " succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
connect: function(ifname, callback) {
|
||||
debug("Connect interface " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
// Interface can only be connected in the state of enabled or
|
||||
// disconnected.
|
||||
if (iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_DISABLED ||
|
||||
iface.info.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface " + ifname + " is not available or "
|
||||
+ " already connected.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (iface.info.ipMode == IP_MODE_DHCP) {
|
||||
this._runDhcp(iface.info.name, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iface.info.ipMode == IP_MODE_STATIC) {
|
||||
if (this._checkConfigNull(iface) && this.lastStaticConfig[iface.info.name]) {
|
||||
debug("Connect with lastStaticConfig " +
|
||||
JSON.stringify(this.lastStaticConfig[iface.info.name]));
|
||||
this.ethernetInterfaces[iface.info.name].updateConfig(
|
||||
this.lastStaticConfig[iface.info.name]);
|
||||
}
|
||||
this._setStaticIP(iface.info.name, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback.notify(false, "IP mode is wrong or not set.");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
disconnect: function(ifname, callback) {
|
||||
debug("Disconnect interface " + ifname);
|
||||
|
||||
this._ensureIfname(ifname, callback, function(iface) {
|
||||
// Interface can be only disconnected in the state of connected.
|
||||
if (iface.info.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface is already disconnected");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let config = { ifname: iface.info.name,
|
||||
ip: INTERFACE_IPADDR_NULL,
|
||||
prefix: INTERFACE_PREFIX_NULL,
|
||||
link: NETWORK_INTERFACE_UP };
|
||||
gNetworkService.setInterfaceConfig(config, function(success) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Netd error.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop dhcp daemon.
|
||||
gNetworkService.stopDhcp(iface.info.name, function(success) {
|
||||
if (success) {
|
||||
debug("DHCP for " + iface.info.name + " stopped.");
|
||||
}
|
||||
});
|
||||
|
||||
this.ethernetInterfaces[iface.info.name].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_DISCONNECTED,
|
||||
ip: INTERFACE_IPADDR_NULL,
|
||||
prefixLength: INTERFACE_PREFIX_NULL,
|
||||
gateway: INTERFACE_GATEWAY_NULL
|
||||
});
|
||||
|
||||
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
||||
|
||||
debug("Disconnect interface " + iface.info.name + " succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_checkConfigNull: function(iface) {
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
let gateways = iface.info.getGateways();
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
|
||||
if (ips.value[0] == INTERFACE_IPADDR_NULL &&
|
||||
prefixLengths.value[0] == INTERFACE_PREFIX_NULL &&
|
||||
gateways[0] == INTERFACE_GATEWAY_NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_ensureIfname: function(ifname, callback, func) {
|
||||
// If no given ifname, use the default one.
|
||||
if (!ifname) {
|
||||
ifname = DEFAULT_ETHERNET_NETWORK_IFACE;
|
||||
}
|
||||
|
||||
let iface = this.ethernetInterfaces[ifname];
|
||||
if (!iface) {
|
||||
if (callback) {
|
||||
callback.notify(true, "Interface " + ifname + " is not available.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
func.call(this, iface);
|
||||
},
|
||||
|
||||
_runDhcp: function(ifname, callback) {
|
||||
debug("runDhcp with " + ifname);
|
||||
|
||||
if (!this.ethernetInterfaces[ifname]) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Invalid interface.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
gNetworkService.dhcpRequest(ifname, function(success, result) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify(false, "DHCP failed.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
debug("DHCP succeeded with " + JSON.stringify(result));
|
||||
|
||||
// Clear last static network information when connecting with dhcp mode.
|
||||
if (this.lastStaticConfig[ifname]) {
|
||||
this.lastStaticConfig[ifname] = null;
|
||||
}
|
||||
|
||||
this.ethernetInterfaces[ifname].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED,
|
||||
ip: result.ipaddr_str,
|
||||
gateway: result.gateway_str,
|
||||
prefixLength: result.prefixLength,
|
||||
dnses: [result.dns1_str, result.dns2_str]
|
||||
});
|
||||
|
||||
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
||||
|
||||
debug("Connect interface " + ifname + " with DHCP succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_setStaticIP: function(ifname, callback) {
|
||||
let iface = this.ethernetInterfaces[ifname];
|
||||
if (!iface) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Invalid interface.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
|
||||
let config = { ifname: iface.info.name,
|
||||
ip: ips.value[0],
|
||||
prefix: prefixLengths.value[0],
|
||||
link: NETWORK_INTERFACE_UP };
|
||||
gNetworkService.setInterfaceConfig(config, function(success) {
|
||||
if (!success) {
|
||||
if (callback) {
|
||||
callback.notify(false, "Netd Error.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep the lastest static network information.
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
let gateways = iface.info.getGateways();
|
||||
iface.info.getAddresses(ips, prefixLengths);
|
||||
|
||||
this.lastStaticConfig[iface.info.name] = {
|
||||
ip: ips.value[0],
|
||||
prefixLength: prefixLengths.value[0],
|
||||
gateway: gateways[0]
|
||||
};
|
||||
|
||||
this.ethernetInterfaces[ifname].updateConfig({
|
||||
state: Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED,
|
||||
});
|
||||
|
||||
gNetworkManager.updateNetworkInterface(this.ethernetInterfaces[ifname]);
|
||||
|
||||
debug("Connect interface " + ifname + " with static ip succeeded.");
|
||||
|
||||
if (callback) {
|
||||
callback.notify(true, "ok");
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([EthernetManager]);
|
||||
@@ -1,2 +0,0 @@
|
||||
component {a96441dd-36b3-4f7f-963b-2c032e28a039} EthernetManager.js
|
||||
contract @mozilla.org/ethernetManager;1 {a96441dd-36b3-4f7f-963b-2c032e28a039}
|
||||
@@ -1,200 +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 "NetUtils.h"
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include "prinit.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "nsDebug.h"
|
||||
#include "SystemProperty.h"
|
||||
|
||||
using mozilla::system::Property;
|
||||
|
||||
static void* sNetUtilsLib;
|
||||
static PRCallOnceType sInitNetUtilsLib;
|
||||
|
||||
static PRStatus
|
||||
InitNetUtilsLib()
|
||||
{
|
||||
sNetUtilsLib = dlopen("/system/lib/libnetutils.so", RTLD_LAZY);
|
||||
// We might fail to open the hardware lib. That's OK.
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
static void*
|
||||
GetNetUtilsLibHandle()
|
||||
{
|
||||
PR_CallOnce(&sInitNetUtilsLib, InitNetUtilsLib);
|
||||
return sNetUtilsLib;
|
||||
}
|
||||
|
||||
// static
|
||||
void*
|
||||
NetUtils::GetSharedLibrary()
|
||||
{
|
||||
void* netLib = GetNetUtilsLibHandle();
|
||||
if (!netLib) {
|
||||
NS_WARNING("No /system/lib/libnetutils.so");
|
||||
}
|
||||
return netLib;
|
||||
}
|
||||
|
||||
// static
|
||||
int32_t
|
||||
NetUtils::SdkVersion()
|
||||
{
|
||||
char propVersion[Property::VALUE_MAX_LENGTH];
|
||||
Property::Get("ro.build.version.sdk", propVersion, "0");
|
||||
int32_t version = strtol(propVersion, nullptr, 10);
|
||||
return version;
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(ifc_enable, int32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_disable, int32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_configure, int32_t, const char*, in_addr_t, uint32_t,
|
||||
in_addr_t, in_addr_t, in_addr_t)
|
||||
DEFINE_DLFUNC(ifc_reset_connections, int32_t, const char*, const int32_t)
|
||||
DEFINE_DLFUNC(ifc_set_default_route, int32_t, const char*, in_addr_t)
|
||||
DEFINE_DLFUNC(ifc_add_route, int32_t, const char*, const char*, uint32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_remove_route, int32_t, const char*, const char*, uint32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_remove_host_routes, int32_t, const char*)
|
||||
DEFINE_DLFUNC(ifc_remove_default_route, int32_t, const char*)
|
||||
DEFINE_DLFUNC(dhcp_stop, int32_t, const char*)
|
||||
|
||||
NetUtils::NetUtils()
|
||||
{
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_enable(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_enable)
|
||||
return ifc_enable(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_disable(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_disable)
|
||||
return ifc_disable(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_configure(const char *ifname,
|
||||
in_addr_t address,
|
||||
uint32_t prefixLength,
|
||||
in_addr_t gateway,
|
||||
in_addr_t dns1,
|
||||
in_addr_t dns2)
|
||||
{
|
||||
USE_DLFUNC(ifc_configure)
|
||||
int32_t ret = ifc_configure(ifname, address, prefixLength, gateway, dns1, dns2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_reset_connections(const char *ifname,
|
||||
const int32_t resetMask)
|
||||
{
|
||||
USE_DLFUNC(ifc_reset_connections)
|
||||
return ifc_reset_connections(ifname, resetMask);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_set_default_route(const char *ifname,
|
||||
in_addr_t gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_set_default_route)
|
||||
return ifc_set_default_route(ifname, gateway);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_add_route(const char *ifname,
|
||||
const char *dst,
|
||||
uint32_t prefixLength,
|
||||
const char *gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_add_route)
|
||||
return ifc_add_route(ifname, dst, prefixLength, gateway);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_route(const char *ifname,
|
||||
const char *dst,
|
||||
uint32_t prefixLength,
|
||||
const char *gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_route)
|
||||
return ifc_remove_route(ifname, dst, prefixLength, gateway);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_host_routes(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_host_routes)
|
||||
return ifc_remove_host_routes(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_default_route(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_default_route)
|
||||
return ifc_remove_default_route(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_dhcp_stop(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(dhcp_stop)
|
||||
return dhcp_stop(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_dhcp_do_request(const char *ifname,
|
||||
char *ipaddr,
|
||||
char *gateway,
|
||||
uint32_t *prefixLength,
|
||||
char *dns1,
|
||||
char *dns2,
|
||||
char *server,
|
||||
uint32_t *lease,
|
||||
char* vendorinfo)
|
||||
{
|
||||
int32_t ret = -1;
|
||||
uint32_t sdkVersion = SdkVersion();
|
||||
|
||||
if (sdkVersion == 15) {
|
||||
// ICS
|
||||
// http://androidxref.com/4.0.4/xref/system/core/libnetutils/dhcp_utils.c#149
|
||||
DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*, uint32_t*, char*, char*, char*, uint32_t*)
|
||||
USE_DLFUNC(dhcp_do_request)
|
||||
vendorinfo[0] = '\0';
|
||||
|
||||
ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns1, dns2,
|
||||
server, lease);
|
||||
} else if (sdkVersion == 16 || sdkVersion == 17) {
|
||||
// JB 4.1 and 4.2
|
||||
// http://androidxref.com/4.1.2/xref/system/core/libnetutils/dhcp_utils.c#175
|
||||
// http://androidxref.com/4.2.2_r1/xref/system/core/include/netutils/dhcp.h#26
|
||||
DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*, uint32_t*, char*, char*, char*, uint32_t*, char*)
|
||||
USE_DLFUNC(dhcp_do_request)
|
||||
ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns1, dns2,
|
||||
server, lease, vendorinfo);
|
||||
} else if (sdkVersion == 18) {
|
||||
// JB 4.3
|
||||
// http://androidxref.com/4.3_r2.1/xref/system/core/libnetutils/dhcp_utils.c#181
|
||||
DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*, uint32_t*, char**, char*, uint32_t*, char*, char*)
|
||||
USE_DLFUNC(dhcp_do_request)
|
||||
char *dns[3] = {dns1, dns2, nullptr};
|
||||
char domains[Property::VALUE_MAX_LENGTH];
|
||||
ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns,
|
||||
server, lease, vendorinfo, domains);
|
||||
} else if (sdkVersion >= 19) {
|
||||
// KitKat 4.4.X
|
||||
// http://androidxref.com/4.4_r1/xref/system/core/libnetutils/dhcp_utils.c#18
|
||||
// Lollipop 5.0
|
||||
//http://androidxref.com/5.0.0_r2/xref/system/core/libnetutils/dhcp_utils.c#186
|
||||
DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*, uint32_t*, char**, char*, uint32_t*, char*, char*, char*)
|
||||
USE_DLFUNC(dhcp_do_request)
|
||||
char *dns[3] = {dns1, dns2, nullptr};
|
||||
char domains[Property::VALUE_MAX_LENGTH];
|
||||
char mtu[Property::VALUE_MAX_LENGTH];
|
||||
ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns, server, lease, vendorinfo, domains, mtu);
|
||||
} else {
|
||||
NS_WARNING("Unable to perform do_dhcp_request: unsupported sdk version!");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1,75 +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/. */
|
||||
|
||||
/**
|
||||
* Abstraction on top of the network support from libnetutils that we
|
||||
* use to set up network connections.
|
||||
*/
|
||||
|
||||
#ifndef NetUtils_h
|
||||
#define NetUtils_h
|
||||
|
||||
#include "arpa/inet.h"
|
||||
|
||||
// Copied from ifc.h
|
||||
#define RESET_IPV4_ADDRESSES 0x01
|
||||
#define RESET_IPV6_ADDRESSES 0x02
|
||||
#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES)
|
||||
|
||||
// Implements netutils functions. No need for an abstract class here since we
|
||||
// only have a one sdk specific method (dhcp_do_request)
|
||||
class NetUtils
|
||||
{
|
||||
public:
|
||||
static void* GetSharedLibrary();
|
||||
|
||||
NetUtils();
|
||||
|
||||
int32_t do_ifc_enable(const char *ifname);
|
||||
int32_t do_ifc_disable(const char *ifname);
|
||||
int32_t do_ifc_configure(const char *ifname,
|
||||
in_addr_t address,
|
||||
uint32_t prefixLength,
|
||||
in_addr_t gateway,
|
||||
in_addr_t dns1,
|
||||
in_addr_t dns2);
|
||||
int32_t do_ifc_reset_connections(const char *ifname, const int32_t resetMask);
|
||||
int32_t do_ifc_set_default_route(const char *ifname, in_addr_t gateway);
|
||||
int32_t do_ifc_add_route(const char *ifname,
|
||||
const char *dst,
|
||||
uint32_t prefixLength,
|
||||
const char *gateway);
|
||||
int32_t do_ifc_remove_route(const char *ifname,
|
||||
const char *dst,
|
||||
uint32_t prefixLength,
|
||||
const char *gateway);
|
||||
int32_t do_ifc_remove_host_routes(const char *ifname);
|
||||
int32_t do_ifc_remove_default_route(const char *ifname);
|
||||
int32_t do_dhcp_stop(const char *ifname);
|
||||
int32_t do_dhcp_do_request(const char *ifname,
|
||||
char *ipaddr,
|
||||
char *gateway,
|
||||
uint32_t *prefixLength,
|
||||
char *dns1,
|
||||
char *dns2,
|
||||
char *server,
|
||||
uint32_t *lease,
|
||||
char* vendorinfo);
|
||||
|
||||
static int32_t SdkVersion();
|
||||
};
|
||||
|
||||
// Defines a function type with the right arguments and return type.
|
||||
#define DEFINE_DLFUNC(name, ret, args...) typedef ret (*FUNC##name)(args);
|
||||
|
||||
// Set up a dlsymed function ready to use.
|
||||
#define USE_DLFUNC(name) \
|
||||
FUNC##name name = (FUNC##name) dlsym(GetSharedLibrary(), #name); \
|
||||
if (!name) { \
|
||||
MOZ_CRASH("Symbol not found in shared library : " #name); \
|
||||
}
|
||||
|
||||
#endif // NetUtils_h
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,388 +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 DEBUG = false;
|
||||
function debug(s) { dump("-*- NetworkStatsManager: " + s + "\n"); }
|
||||
|
||||
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/DOMRequestHelper.jsm");
|
||||
|
||||
// Ensure NetworkStatsService and NetworkStatsDB are loaded in the parent process
|
||||
// to receive messages from the child processes.
|
||||
var appInfo = Cc["@mozilla.org/xre/app-info;1"];
|
||||
var isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime)
|
||||
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||
if (isParentProcess) {
|
||||
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsISyncMessageSender");
|
||||
|
||||
// NetworkStatsData
|
||||
const nsIClassInfo = Ci.nsIClassInfo;
|
||||
const NETWORKSTATSDATA_CID = Components.ID("{3b16fe17-5583-483a-b486-b64a3243221c}");
|
||||
|
||||
function NetworkStatsData(aWindow, aData) {
|
||||
this.rxBytes = aData.rxBytes;
|
||||
this.txBytes = aData.txBytes;
|
||||
this.date = new aWindow.Date(aData.date.getTime());
|
||||
}
|
||||
|
||||
NetworkStatsData.prototype = {
|
||||
classID : NETWORKSTATSDATA_CID,
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([])
|
||||
};
|
||||
|
||||
// NetworkStatsInterface
|
||||
const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
|
||||
const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
|
||||
|
||||
function NetworkStatsInterface() {
|
||||
if (DEBUG) {
|
||||
debug("NetworkStatsInterface Constructor");
|
||||
}
|
||||
}
|
||||
|
||||
NetworkStatsInterface.prototype = {
|
||||
__init: function(aNetwork) {
|
||||
this.type = aNetwork.type;
|
||||
this.id = aNetwork.id;
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSINTERFACE_CID,
|
||||
|
||||
contractID: NETWORKSTATSINTERFACE_CONTRACTID,
|
||||
QueryInterface : XPCOMUtils.generateQI([])
|
||||
}
|
||||
|
||||
// NetworkStats
|
||||
const NETWORKSTATS_CID = Components.ID("{28904f59-8497-4ac0-904f-2af14b7fd3de}");
|
||||
|
||||
function NetworkStats(aWindow, aStats) {
|
||||
if (DEBUG) {
|
||||
debug("NetworkStats Constructor");
|
||||
}
|
||||
this.appManifestURL = aStats.appManifestURL || null;
|
||||
this.browsingTrafficOnly = aStats.browsingTrafficOnly || false;
|
||||
this.serviceType = aStats.serviceType || null;
|
||||
this.network = new aWindow.MozNetworkStatsInterface(aStats.network);
|
||||
this.start = aStats.start ? new aWindow.Date(aStats.start.getTime()) : null;
|
||||
this.end = aStats.end ? new aWindow.Date(aStats.end.getTime()) : null;
|
||||
|
||||
let samples = this.data = new aWindow.Array();
|
||||
for (let i = 0; i < aStats.data.length; i++) {
|
||||
samples.push(aWindow.MozNetworkStatsData._create(
|
||||
aWindow, new NetworkStatsData(aWindow, aStats.data[i])));
|
||||
}
|
||||
}
|
||||
|
||||
NetworkStats.prototype = {
|
||||
classID : NETWORKSTATS_CID,
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI()
|
||||
}
|
||||
|
||||
// NetworkStatsAlarm
|
||||
const NETWORKSTATSALARM_CID = Components.ID("{a93ea13e-409c-4189-9b1e-95fff220be55}");
|
||||
|
||||
function NetworkStatsAlarm(aWindow, aAlarm) {
|
||||
this.alarmId = aAlarm.id;
|
||||
this.network = new aWindow.MozNetworkStatsInterface(aAlarm.network);
|
||||
this.threshold = aAlarm.threshold;
|
||||
this.data = aAlarm.data;
|
||||
}
|
||||
|
||||
NetworkStatsAlarm.prototype = {
|
||||
classID : NETWORKSTATSALARM_CID,
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([])
|
||||
};
|
||||
|
||||
// NetworkStatsManager
|
||||
|
||||
const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
|
||||
const NETWORKSTATSMANAGER_CID = Components.ID("{ceb874cd-cc1a-4e65-b404-cc2d3e42425f}");
|
||||
|
||||
function NetworkStatsManager() {
|
||||
if (DEBUG) {
|
||||
debug("Constructor");
|
||||
}
|
||||
}
|
||||
|
||||
NetworkStatsManager.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
getSamples: function getSamples(aNetwork, aStart, aEnd, aOptions) {
|
||||
if (aStart > aEnd) {
|
||||
throw Components.results.NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// appManifestURL is used to query network statistics by app;
|
||||
// serviceType is used to query network statistics by system service.
|
||||
// It is illegal to specify both of them at the same time.
|
||||
if (aOptions.appManifestURL && aOptions.serviceType) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
// browsingTrafficOnly is meaningful only when querying by app.
|
||||
if (!aOptions.appManifestURL && aOptions.browsingTrafficOnly) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
let appManifestURL = aOptions.appManifestURL;
|
||||
let serviceType = aOptions.serviceType;
|
||||
let browsingTrafficOnly = aOptions.browsingTrafficOnly;
|
||||
|
||||
// TODO Bug 929410 Date object cannot correctly pass through cpmm/ppmm IPC
|
||||
// This is just a work-around by passing timestamp numbers.
|
||||
aStart = aStart.getTime();
|
||||
aEnd = aEnd.getTime();
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:Get",
|
||||
{ network: aNetwork.toJSON(),
|
||||
start: aStart,
|
||||
end: aEnd,
|
||||
appManifestURL: appManifestURL,
|
||||
browsingTrafficOnly: browsingTrafficOnly,
|
||||
serviceType: serviceType,
|
||||
id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
clearStats: function clearStats(aNetwork) {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:Clear",
|
||||
{ network: aNetwork.toJSON(),
|
||||
id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
clearAllStats: function clearAllStats() {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:ClearAll",
|
||||
{id: this.getRequestId(request)});
|
||||
return request;
|
||||
},
|
||||
|
||||
addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:SetAlarm",
|
||||
{id: this.getRequestId(request),
|
||||
data: {network: aNetwork.toJSON(),
|
||||
threshold: aThreshold,
|
||||
startTime: aOptions.startTime,
|
||||
data: aOptions.data,
|
||||
manifestURL: this.manifestURL,
|
||||
pageURL: this.pageURL}});
|
||||
return request;
|
||||
},
|
||||
|
||||
getAllAlarms: function getAllAlarms(aNetwork) {
|
||||
let network = null;
|
||||
if (aNetwork) {
|
||||
network = aNetwork.toJSON();
|
||||
}
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:GetAlarms",
|
||||
{id: this.getRequestId(request),
|
||||
data: {network: network,
|
||||
manifestURL: this.manifestURL}});
|
||||
return request;
|
||||
},
|
||||
|
||||
removeAlarms: function removeAlarms(aAlarmId) {
|
||||
if (aAlarmId == 0) {
|
||||
aAlarmId = -1;
|
||||
}
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms",
|
||||
{id: this.getRequestId(request),
|
||||
data: {alarmId: aAlarmId,
|
||||
manifestURL: this.manifestURL}});
|
||||
|
||||
return request;
|
||||
},
|
||||
|
||||
getAvailableNetworks: function getAvailableNetworks() {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks",
|
||||
{ id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
getAvailableServiceTypes: function getAvailableServiceTypes() {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:GetAvailableServiceTypes",
|
||||
{ id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
get sampleRate() {
|
||||
return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
|
||||
},
|
||||
|
||||
get maxStorageAge() {
|
||||
return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) {
|
||||
debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
|
||||
}
|
||||
|
||||
let msg = aMessage.json;
|
||||
let req = this.takeRequest(msg.id);
|
||||
if (!req) {
|
||||
if (DEBUG) {
|
||||
debug("No request stored with id " + msg.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "NetworkStats:Get:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let result = this._window.MozNetworkStats._create(
|
||||
this._window, new NetworkStats(this._window, msg.result));
|
||||
if (DEBUG) {
|
||||
debug("result: " + JSON.stringify(result));
|
||||
}
|
||||
Services.DOMRequest.fireSuccess(req, result);
|
||||
break;
|
||||
|
||||
case "NetworkStats:GetAvailableNetworks:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let networks = new this._window.Array();
|
||||
for (let i = 0; i < msg.result.length; i++) {
|
||||
let network = new this._window.MozNetworkStatsInterface(msg.result[i]);
|
||||
networks.push(network);
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, networks);
|
||||
break;
|
||||
|
||||
case "NetworkStats:GetAvailableServiceTypes:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let serviceTypes = new this._window.Array();
|
||||
for (let i = 0; i < msg.result.length; i++) {
|
||||
serviceTypes.push(msg.result[i]);
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, serviceTypes);
|
||||
break;
|
||||
|
||||
case "NetworkStats:Clear:Return":
|
||||
case "NetworkStats:ClearAll:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, true);
|
||||
break;
|
||||
|
||||
case "NetworkStats:SetAlarm:Return":
|
||||
case "NetworkStats:RemoveAlarms:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, msg.result);
|
||||
break;
|
||||
|
||||
case "NetworkStats:GetAlarms:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let alarms = new this._window.Array();
|
||||
for (let i = 0; i < msg.result.length; i++) {
|
||||
// The WebIDL type of data is any, so we should manually clone it
|
||||
// into the content window.
|
||||
if ("data" in msg.result[i]) {
|
||||
msg.result[i].data = Cu.cloneInto(msg.result[i].data, this._window);
|
||||
}
|
||||
let alarm = new NetworkStatsAlarm(this._window, msg.result[i]);
|
||||
alarms.push(this._window.MozNetworkStatsAlarm._create(this._window, alarm));
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, alarms);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) {
|
||||
debug("Wrong message: " + aMessage.name);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
init: function(aWindow) {
|
||||
let principal = aWindow.document.nodePrincipal;
|
||||
|
||||
this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
|
||||
"NetworkStats:GetAvailableNetworks:Return",
|
||||
"NetworkStats:GetAvailableServiceTypes:Return",
|
||||
"NetworkStats:Clear:Return",
|
||||
"NetworkStats:ClearAll:Return",
|
||||
"NetworkStats:SetAlarm:Return",
|
||||
"NetworkStats:GetAlarms:Return",
|
||||
"NetworkStats:RemoveAlarms:Return"]);
|
||||
|
||||
// Init app properties.
|
||||
let appsService = Cc["@mozilla.org/AppsService;1"]
|
||||
.getService(Ci.nsIAppsService);
|
||||
|
||||
this.manifestURL = appsService.getManifestURLByLocalId(principal.appId);
|
||||
|
||||
let isApp = !!this.manifestURL.length;
|
||||
if (isApp) {
|
||||
this.pageURL = principal.URI.spec;
|
||||
}
|
||||
|
||||
this.window = aWindow;
|
||||
},
|
||||
|
||||
// Called from DOMRequestIpcHelper
|
||||
uninit: function uninit() {
|
||||
if (DEBUG) {
|
||||
debug("uninit call");
|
||||
}
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSMANAGER_CID,
|
||||
contractID : NETWORKSTATSMANAGER_CONTRACTID,
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver]),
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm,
|
||||
NetworkStatsData,
|
||||
NetworkStatsInterface,
|
||||
NetworkStats,
|
||||
NetworkStatsManager]);
|
||||
@@ -1,14 +0,0 @@
|
||||
component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c}
|
||||
|
||||
component {28904f59-8497-4ac0-904f-2af14b7fd3de} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStats;1 {28904f59-8497-4ac0-904f-2af14b7fd3de}
|
||||
|
||||
component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645}
|
||||
|
||||
component {a93ea13e-409c-4189-9b1e-95fff220be55} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkstatsalarm;1 {a93ea13e-409c-4189-9b1e-95fff220be55}
|
||||
|
||||
component {ceb874cd-cc1a-4e65-b404-cc2d3e42425f} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsManager;1 {ceb874cd-cc1a-4e65-b404-cc2d3e42425f}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,90 +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 DEBUG = false;
|
||||
function debug(s) { dump("-*- NetworkStatsServiceProxy: " + s + "\n"); }
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["NetworkStatsServiceProxy"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
|
||||
|
||||
const NETWORKSTATSSERVICEPROXY_CONTRACTID = "@mozilla.org/networkstatsServiceProxy;1";
|
||||
const NETWORKSTATSSERVICEPROXY_CID = Components.ID("98fd8f69-784e-4626-aa59-56d6436a3c24");
|
||||
const nsINetworkStatsServiceProxy = Ci.nsINetworkStatsServiceProxy;
|
||||
|
||||
function NetworkStatsServiceProxy() {
|
||||
if (DEBUG) {
|
||||
debug("Proxy started");
|
||||
}
|
||||
}
|
||||
|
||||
NetworkStatsServiceProxy.prototype = {
|
||||
/*
|
||||
* Function called in the protocol layer (HTTP, FTP, WebSocket ...etc)
|
||||
* to pass the per-app stats to NetworkStatsService.
|
||||
*/
|
||||
saveAppStats: function saveAppStats(aAppId, aIsInIsolatedMozBrowser, aNetworkInfo, aTimeStamp,
|
||||
aRxBytes, aTxBytes, aIsAccumulative,
|
||||
aCallback) {
|
||||
if (!aNetworkInfo) {
|
||||
if (DEBUG) {
|
||||
debug("|aNetworkInfo| is not specified. Failed to save stats. Returning.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("saveAppStats: " + aAppId + " " + aIsInIsolatedMozBrowser + " " +
|
||||
aNetworkInfo.type + " " + aTimeStamp + " " +
|
||||
aRxBytes + " " + aTxBytes + " " + aIsAccumulative);
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback = aCallback.notify;
|
||||
}
|
||||
|
||||
NetworkStatsService.saveStats(aAppId, aIsInIsolatedMozBrowser, "", aNetworkInfo,
|
||||
aTimeStamp, aRxBytes, aTxBytes,
|
||||
aIsAccumulative, aCallback);
|
||||
},
|
||||
|
||||
/*
|
||||
* Function called in the points of different system services
|
||||
* to pass the per-service stats to NetworkStatsService.
|
||||
*/
|
||||
saveServiceStats: function saveServiceStats(aServiceType, aNetworkInfo,
|
||||
aTimeStamp, aRxBytes, aTxBytes,
|
||||
aIsAccumulative, aCallback) {
|
||||
if (!aNetworkInfo) {
|
||||
if (DEBUG) {
|
||||
debug("|aNetworkInfo| is not specified. Failed to save stats. Returning.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("saveServiceStats: " + aServiceType + " " + aNetworkInfo.type + " " +
|
||||
aTimeStamp + " " + aRxBytes + " " + aTxBytes + " " +
|
||||
aIsAccumulative);
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback = aCallback.notify;
|
||||
}
|
||||
|
||||
NetworkStatsService.saveStats(0, false, aServiceType , aNetworkInfo, aTimeStamp,
|
||||
aRxBytes, aTxBytes, aIsAccumulative,
|
||||
aCallback);
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSSERVICEPROXY_CID,
|
||||
QueryInterface : XPCOMUtils.generateQI([nsINetworkStatsServiceProxy]),
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsServiceProxy]);
|
||||
@@ -1,2 +0,0 @@
|
||||
component {98fd8f69-784e-4626-aa59-56d6436a3c24} NetworkStatsServiceProxy.js
|
||||
contract @mozilla.org/networkstatsServiceProxy;1 {98fd8f69-784e-4626-aa59-56d6436a3c24}
|
||||
@@ -10,10 +10,4 @@ XPIDL_SOURCES += [
|
||||
'nsIUDPSocketChild.idl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
XPIDL_SOURCES += [
|
||||
'nsIEthernetManager.idl',
|
||||
'nsINetworkStatsServiceProxy.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_network'
|
||||
|
||||
@@ -1,137 +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, function, uuid(2a3ad56c-edc0-439f-8aae-900b331ddf49)]
|
||||
interface nsIEthernetManagerCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback function used to report the success of different operations.
|
||||
*
|
||||
* @param success
|
||||
* Boolean value indicates the success of an operation.
|
||||
* @prarm message
|
||||
* Message reported in the end of operation.
|
||||
*/
|
||||
void notify(in boolean success, in DOMString message);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(1746e7dd-92d4-43fa-8ef4-bc13d0b60353)]
|
||||
interface nsIEthernetManagerScanCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback function used to report the result of scan function.
|
||||
*
|
||||
* @param list
|
||||
* List of available ethernet interfaces.
|
||||
*/
|
||||
void notify(in jsval list);
|
||||
};
|
||||
|
||||
/**
|
||||
* An internal idl provides control to ethernet interfaces.
|
||||
*/
|
||||
[scriptable, uuid(81750c87-bb3b-4724-b955-834eafa53fd1)]
|
||||
interface nsIEthernetManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* List of exisiting interface name.
|
||||
*/
|
||||
readonly attribute jsval interfaceList;
|
||||
|
||||
/**
|
||||
* Scan available ethernet interfaces on device.
|
||||
*
|
||||
* @param callback
|
||||
* Callback function.
|
||||
*/
|
||||
void scan(in nsIEthernetManagerScanCallback callback);
|
||||
|
||||
/**
|
||||
* Add a new interface to the interface list.
|
||||
*
|
||||
* @param ifname
|
||||
* Interface name. Should be the form of "eth*".
|
||||
* @param callback
|
||||
* Callback function.
|
||||
*/
|
||||
void addInterface(in DOMString ifname,
|
||||
in nsIEthernetManagerCallback callback);
|
||||
|
||||
/**
|
||||
* Remove an existing interface from the interface list.
|
||||
*
|
||||
* @param ifname
|
||||
* Interface name.
|
||||
* @param Callback
|
||||
* Callback function.
|
||||
*/
|
||||
void removeInterface(in DOMString ifname,
|
||||
in nsIEthernetManagerCallback callback);
|
||||
|
||||
/**
|
||||
* Update a conifg of an existing interface in the interface list.
|
||||
*
|
||||
* @param ifname
|
||||
* Interface name.
|
||||
* @param config
|
||||
* .ip: IP address.
|
||||
* .prefixLength: Mask length.
|
||||
* .gateway: Gateway.
|
||||
* .dnses: DNS addresses.
|
||||
* .httpProxyHost: HTTP proxy host.
|
||||
* .httpProxyPort: HTTP proxy port.
|
||||
* .ipMode: IP mode, can be 'dhcp' or 'static'.
|
||||
* @param callback
|
||||
* Callback function.
|
||||
*/
|
||||
void updateInterfaceConfig(in DOMString ifname,
|
||||
in jsval config,
|
||||
in nsIEthernetManagerCallback callback);
|
||||
|
||||
/**
|
||||
* Enable networking of an existing interface in the interface list.
|
||||
*
|
||||
* @param ifname
|
||||
* Interface name.
|
||||
* @param callback
|
||||
* Callback function.
|
||||
*/
|
||||
void enable(in DOMString ifname,
|
||||
in nsIEthernetManagerCallback callback);
|
||||
|
||||
/**
|
||||
* Disable networking of an existing interface in the interface list.
|
||||
*
|
||||
* @param ifname
|
||||
* Interface name.
|
||||
* @param callback
|
||||
* Callback function.
|
||||
*/
|
||||
void disable(in DOMString ifname,
|
||||
in nsIEthernetManagerCallback callback);
|
||||
|
||||
/**
|
||||
* Make an existing interface connect to network.
|
||||
*
|
||||
* @param ifname
|
||||
* Interface name.
|
||||
* @param callback
|
||||
* Callback function.
|
||||
*/
|
||||
void connect(in DOMString ifname,
|
||||
in nsIEthernetManagerCallback callback);
|
||||
|
||||
/**
|
||||
* Disconnect a connected interface in the interface list.
|
||||
*
|
||||
* @param ifname
|
||||
* Interface name.
|
||||
* @param callback
|
||||
* Callback function.
|
||||
*/
|
||||
void disconnect(in DOMString ifname,
|
||||
in nsIEthernetManagerCallback callback);
|
||||
};
|
||||
@@ -1,64 +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"
|
||||
|
||||
interface nsINetworkInfo;
|
||||
|
||||
[scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
|
||||
interface nsINetworkStatsServiceProxyCallback : nsISupports
|
||||
{
|
||||
/*
|
||||
* @param aResult callback result with boolean value
|
||||
* @param aMessage message
|
||||
*/
|
||||
void notify(in boolean aResult, in jsval aMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(f4f3e901-e102-499d-9d37-dc9951f52df7)]
|
||||
interface nsINetworkStatsServiceProxy : nsISupports
|
||||
{
|
||||
/*
|
||||
* An interface used to record per-app traffic data.
|
||||
* @param aAppId app id
|
||||
* @param aIsInIsolatedMozBrowser
|
||||
* true if the frame is an isolated mozbrowser element. <iframe
|
||||
* mozbrowser mozapp> and <xul:browser> are not considered to be
|
||||
* mozbrowser elements. <iframe mozbrowser noisolation> does not count
|
||||
* as isolated since isolation is disabled. Isolation can only be
|
||||
* disabled if the containing document is chrome.
|
||||
* @param aNetworkInterface network
|
||||
* @param aTimeStamp time stamp
|
||||
* @param aRxBytes received data amount
|
||||
* @param aTxBytes transmitted data amount
|
||||
* @param aIsAccumulative is stats accumulative
|
||||
* @param aCallback an optional callback
|
||||
*/
|
||||
void saveAppStats(in unsigned long aAppId,
|
||||
in boolean aIsInIsolatedMozBrowser,
|
||||
in nsINetworkInfo aNetworkInfo,
|
||||
in unsigned long long aTimeStamp,
|
||||
in unsigned long long aRxBytes,
|
||||
in unsigned long long aTxBytes,
|
||||
in boolean aIsAccumulative,
|
||||
[optional] in nsINetworkStatsServiceProxyCallback aCallback);
|
||||
|
||||
/*
|
||||
* An interface used to record per-system service traffic data.
|
||||
* @param aServiceType system service type
|
||||
* @param aNetworkInterface network
|
||||
* @param aTimeStamp time stamp
|
||||
* @param aRxBytes received data amount
|
||||
* @param aTxBytes transmitted data amount
|
||||
* @param aIsAccumulative is stats accumulative
|
||||
* @param aCallback an optional callback
|
||||
*/
|
||||
void saveServiceStats(in string aServiceType,
|
||||
in nsINetworkInfo aNetworkInfo,
|
||||
in unsigned long long aTimeStamp,
|
||||
in unsigned long long aRxBytes,
|
||||
in unsigned long long aTxBytes,
|
||||
in boolean aIsAccumulative,
|
||||
[optional] in nsINetworkStatsServiceProxyCallback aCallback);
|
||||
};
|
||||
@@ -40,28 +40,6 @@ UNIFIED_SOURCES += [
|
||||
'UDPSocketParent.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
EXTRA_JS_MODULES += [
|
||||
'NetworkStatsDB.jsm',
|
||||
'NetworkStatsService.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
EXTRA_COMPONENTS += [
|
||||
'EthernetManager.js',
|
||||
'EthernetManager.manifest',
|
||||
'NetworkStatsManager.js',
|
||||
'NetworkStatsManager.manifest',
|
||||
'NetworkStatsServiceProxy.js',
|
||||
'NetworkStatsServiceProxy.manifest',
|
||||
]
|
||||
EXPORTS.mozilla.dom.network += [
|
||||
'NetUtils.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'NetUtils.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PTCPServerSocket.ipdl',
|
||||
'PTCPSocket.ipdl',
|
||||
|
||||
@@ -7,7 +7,6 @@ support-files =
|
||||
[test_browser.html]
|
||||
skip-if = true
|
||||
[test_idle.html]
|
||||
# skip-if = (toolkit == 'gonk' && debug) #debug-only failure
|
||||
skip-if = true
|
||||
[test_permissions.html]
|
||||
skip-if = true
|
||||
|
||||
@@ -1,217 +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 "SpeakerManager.h"
|
||||
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
#include "mozilla/dom/Event.h"
|
||||
|
||||
#include "AudioChannelService.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDOMClassInfo.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "SpeakerManagerService.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_QUERY_INTERFACE_INHERITED(SpeakerManager, DOMEventTargetHelper,
|
||||
nsIDOMEventListener)
|
||||
NS_IMPL_ADDREF_INHERITED(SpeakerManager, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(SpeakerManager, DOMEventTargetHelper)
|
||||
|
||||
SpeakerManager::SpeakerManager()
|
||||
: mForcespeaker(false)
|
||||
, mVisible(false)
|
||||
{
|
||||
SpeakerManagerService *service =
|
||||
SpeakerManagerService::GetOrCreateSpeakerManagerService();
|
||||
MOZ_ASSERT(service);
|
||||
service->RegisterSpeakerManager(this);
|
||||
}
|
||||
|
||||
SpeakerManager::~SpeakerManager()
|
||||
{
|
||||
SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService();
|
||||
MOZ_ASSERT(service);
|
||||
|
||||
service->UnRegisterSpeakerManager(this);
|
||||
nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner());
|
||||
NS_ENSURE_TRUE_VOID(target);
|
||||
|
||||
target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this,
|
||||
/* useCapture = */ true);
|
||||
}
|
||||
|
||||
bool
|
||||
SpeakerManager::Speakerforced()
|
||||
{
|
||||
// If a background app calls forcespeaker=true that doesn't change anything.
|
||||
// 'speakerforced' remains false everywhere.
|
||||
if (mForcespeaker && !mVisible) {
|
||||
return false;
|
||||
}
|
||||
SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService();
|
||||
MOZ_ASSERT(service);
|
||||
return service->GetSpeakerStatus();
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
SpeakerManager::Forcespeaker()
|
||||
{
|
||||
return mForcespeaker;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManager::SetForcespeaker(bool aEnable)
|
||||
{
|
||||
SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService();
|
||||
MOZ_ASSERT(service);
|
||||
|
||||
service->ForceSpeaker(aEnable, mVisible);
|
||||
mForcespeaker = aEnable;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManager::DispatchSimpleEvent(const nsAString& aStr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
|
||||
nsresult rv = CheckInnerWindowCorrectness();
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
|
||||
event->InitEvent(aStr, false, false);
|
||||
event->SetTrusted(true);
|
||||
|
||||
rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Failed to dispatch the event!!!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManager::Init(nsPIDOMWindowInner* aWindow)
|
||||
{
|
||||
BindToOwner(aWindow);
|
||||
|
||||
nsCOMPtr<nsIDocShell> docshell = GetOwner()->GetDocShell();
|
||||
NS_ENSURE_TRUE_VOID(docshell);
|
||||
docshell->GetIsActive(&mVisible);
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
|
||||
NS_ENSURE_TRUE_VOID(target);
|
||||
|
||||
target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this,
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
SpeakerManager::GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<SpeakerManager>
|
||||
SpeakerManager::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!sgo) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!ownerWindow) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
|
||||
NS_ENSURE_TRUE(permMgr, nullptr);
|
||||
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
nsresult rv =
|
||||
permMgr->TestPermissionFromWindow(ownerWindow, "speaker-control",
|
||||
&permission);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
if (permission != nsIPermissionManager::ALLOW_ACTION) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<SpeakerManager> object = new SpeakerManager();
|
||||
object->Init(ownerWindow);
|
||||
return object.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
SpeakerManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return MozSpeakerManagerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeakerManager::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
|
||||
if (!type.EqualsLiteral("visibilitychange")) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
|
||||
NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
|
||||
docshell->GetIsActive(&mVisible);
|
||||
|
||||
// If an app that has called forcespeaker=true is switched
|
||||
// from the background to the foreground 'speakerforced'
|
||||
// switches to true in all apps. I.e. the app doesn't have to
|
||||
// call forcespeaker=true again when it comes into foreground.
|
||||
SpeakerManagerService *service =
|
||||
SpeakerManagerService::GetOrCreateSpeakerManagerService();
|
||||
MOZ_ASSERT(service);
|
||||
|
||||
if (mVisible && mForcespeaker) {
|
||||
service->ForceSpeaker(mForcespeaker, mVisible);
|
||||
}
|
||||
// If an application that has called forcespeaker=true, but no audio is
|
||||
// currently playing in the app itself, if application switch to
|
||||
// the background, we switch 'speakerforced' to false.
|
||||
if (!mVisible && mForcespeaker) {
|
||||
RefPtr<AudioChannelService> audioChannelService =
|
||||
AudioChannelService::GetOrCreate();
|
||||
if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) {
|
||||
service->ForceSpeaker(false, mVisible);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManager::SetAudioChannelActive(bool isActive)
|
||||
{
|
||||
if (mForcespeaker) {
|
||||
SpeakerManagerService *service =
|
||||
SpeakerManagerService::GetOrCreateSpeakerManagerService();
|
||||
MOZ_ASSERT(service);
|
||||
service->ForceSpeaker(isActive, mVisible);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -1,66 +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_dom_SpeakerManager_h
|
||||
#define mozilla_dom_SpeakerManager_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/MozSpeakerManagerBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
/* This class is used for UA to control devices's speaker status.
|
||||
* After UA set the speaker status, the UA should handle the
|
||||
* forcespeakerchange event and change the speaker status in UI.
|
||||
* The device's speaker status would set back to normal when UA close the application.
|
||||
*/
|
||||
class SpeakerManager final
|
||||
: public DOMEventTargetHelper
|
||||
, public nsIDOMEventListener
|
||||
{
|
||||
friend class SpeakerManagerService;
|
||||
friend class SpeakerManagerServiceChild;
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
public:
|
||||
void Init(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
/**
|
||||
* WebIDL Interface
|
||||
*/
|
||||
// Get this api's force speaker setting.
|
||||
bool Forcespeaker();
|
||||
// Force acoustic sound go through speaker. Don't force to speaker if application
|
||||
// stay in the background and re-force when application
|
||||
// go to foreground
|
||||
void SetForcespeaker(bool aEnable);
|
||||
// Get the device's speaker forced setting.
|
||||
bool Speakerforced();
|
||||
|
||||
void SetAudioChannelActive(bool aIsActive);
|
||||
IMPL_EVENT_HANDLER(speakerforcedchange)
|
||||
|
||||
static already_AddRefed<SpeakerManager>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
|
||||
protected:
|
||||
SpeakerManager();
|
||||
~SpeakerManager();
|
||||
void DispatchSimpleEvent(const nsAString& aStr);
|
||||
// This api's force speaker setting
|
||||
bool mForcespeaker;
|
||||
bool mVisible;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_SpeakerManager_h
|
||||
@@ -1,224 +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 "SpeakerManagerService.h"
|
||||
#include "SpeakerManagerServiceChild.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "AudioChannelService.h"
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
|
||||
#include "nsIAudioManager.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
StaticRefPtr<SpeakerManagerService> gSpeakerManagerService;
|
||||
|
||||
// static
|
||||
SpeakerManagerService*
|
||||
SpeakerManagerService::GetOrCreateSpeakerManagerService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return SpeakerManagerServiceChild::GetOrCreateSpeakerManagerService();
|
||||
}
|
||||
|
||||
// If we already exist, exit early
|
||||
if (gSpeakerManagerService) {
|
||||
return gSpeakerManagerService;
|
||||
}
|
||||
|
||||
// Create new instance, register, return
|
||||
RefPtr<SpeakerManagerService> service = new SpeakerManagerService();
|
||||
|
||||
gSpeakerManagerService = service;
|
||||
|
||||
return gSpeakerManagerService;
|
||||
}
|
||||
|
||||
SpeakerManagerService*
|
||||
SpeakerManagerService::GetSpeakerManagerService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return SpeakerManagerServiceChild::GetSpeakerManagerService();
|
||||
}
|
||||
|
||||
return gSpeakerManagerService;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::Shutdown()
|
||||
{
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return SpeakerManagerServiceChild::Shutdown();
|
||||
}
|
||||
|
||||
if (gSpeakerManagerService) {
|
||||
gSpeakerManagerService = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(SpeakerManagerService, nsIObserver)
|
||||
|
||||
void
|
||||
SpeakerManagerService::ForceSpeaker(bool aEnable, uint64_t aChildId)
|
||||
{
|
||||
TurnOnSpeaker(aEnable);
|
||||
if (aEnable) {
|
||||
mSpeakerStatusSet.Put(aChildId);
|
||||
}
|
||||
Notify();
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::ForceSpeaker(bool aEnable, bool aVisible)
|
||||
{
|
||||
// b2g main process without oop
|
||||
TurnOnSpeaker(aEnable && aVisible);
|
||||
mVisible = aVisible;
|
||||
mOrgSpeakerStatus = aEnable;
|
||||
Notify();
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::TurnOnSpeaker(bool aOn)
|
||||
{
|
||||
nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(audioManager);
|
||||
int32_t phoneState;
|
||||
audioManager->GetPhoneState(&phoneState);
|
||||
int32_t forceuse = (phoneState == nsIAudioManager::PHONE_STATE_IN_CALL ||
|
||||
phoneState == nsIAudioManager::PHONE_STATE_IN_COMMUNICATION)
|
||||
? nsIAudioManager::USE_COMMUNICATION : nsIAudioManager::USE_MEDIA;
|
||||
if (aOn) {
|
||||
audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_SPEAKER);
|
||||
} else {
|
||||
audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SpeakerManagerService::GetSpeakerStatus()
|
||||
{
|
||||
char propQemu[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.kernel.qemu", propQemu, "");
|
||||
if (!strncmp(propQemu, "1", 1)) {
|
||||
return mOrgSpeakerStatus;
|
||||
}
|
||||
nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(audioManager, false);
|
||||
int32_t usage;
|
||||
audioManager->GetForceForUse(nsIAudioManager::USE_MEDIA, &usage);
|
||||
return usage == nsIAudioManager::FORCE_SPEAKER;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::Notify()
|
||||
{
|
||||
// Parent Notify to all the child processes.
|
||||
nsTArray<ContentParent*> children;
|
||||
ContentParent::GetAll(children);
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
Unused << children[i]->SendSpeakerManagerNotify();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) {
|
||||
mRegisteredSpeakerManagers[i]->
|
||||
DispatchSimpleEvent(NS_LITERAL_STRING("speakerforcedchange"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::SetAudioChannelActive(bool aIsActive)
|
||||
{
|
||||
if (!aIsActive && !mVisible) {
|
||||
ForceSpeaker(!mOrgSpeakerStatus, mVisible);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeakerManagerService::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
if (!strcmp(aTopic, "ipc:content-shutdown")) {
|
||||
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
|
||||
if (!props) {
|
||||
NS_WARNING("ipc:content-shutdown message without property bag as subject");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint64_t childID = 0;
|
||||
nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
|
||||
&childID);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// If the audio has paused by audiochannel,
|
||||
// the enable flag should be false and don't need to handle.
|
||||
if (mSpeakerStatusSet.Contains(childID)) {
|
||||
TurnOnSpeaker(false);
|
||||
mSpeakerStatusSet.Remove(childID);
|
||||
}
|
||||
if (mOrgSpeakerStatus) {
|
||||
TurnOnSpeaker(!mOrgSpeakerStatus);
|
||||
mOrgSpeakerStatus = false;
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("ipc:content-shutdown message without childID property");
|
||||
}
|
||||
} else if (!strcmp(aTopic, "xpcom-will-shutdown")) {
|
||||
// Note that we need to do this before xpcom-shutdown, since the
|
||||
// AudioChannelService cannot be used past that point.
|
||||
RefPtr<AudioChannelService> audioChannelService =
|
||||
AudioChannelService::GetOrCreate();
|
||||
if (audioChannelService) {
|
||||
audioChannelService->UnregisterSpeakerManager(this);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, "ipc:content-shutdown");
|
||||
obs->RemoveObserver(this, "xpcom-will-shutdown");
|
||||
}
|
||||
|
||||
Shutdown();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SpeakerManagerService::SpeakerManagerService()
|
||||
: mOrgSpeakerStatus(false),
|
||||
mVisible(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SpeakerManagerService);
|
||||
if (XRE_IsParentProcess()) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, "ipc:content-shutdown", false);
|
||||
obs->AddObserver(this, "xpcom-will-shutdown", false);
|
||||
}
|
||||
}
|
||||
RefPtr<AudioChannelService> audioChannelService =
|
||||
AudioChannelService::GetOrCreate();
|
||||
if (audioChannelService) {
|
||||
audioChannelService->RegisterSpeakerManager(this);
|
||||
}
|
||||
}
|
||||
|
||||
SpeakerManagerService::~SpeakerManagerService()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SpeakerManagerService);
|
||||
}
|
||||
@@ -1,79 +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_dom_SpeakerManagerService_h__
|
||||
#define mozilla_dom_SpeakerManagerService_h__
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsTArray.h"
|
||||
#include "SpeakerManager.h"
|
||||
#include "nsIAudioManager.h"
|
||||
#include "nsCheapSets.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class SpeakerManagerService : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
/*
|
||||
* Return null or instance which has been created.
|
||||
*/
|
||||
static SpeakerManagerService* GetSpeakerManagerService();
|
||||
/*
|
||||
* Return SpeakerManagerService instance.
|
||||
* If SpeakerManagerService is not exist, create and return new one.
|
||||
*/
|
||||
static SpeakerManagerService* GetOrCreateSpeakerManagerService();
|
||||
virtual void ForceSpeaker(bool aEnable, bool aVisible);
|
||||
virtual bool GetSpeakerStatus();
|
||||
virtual void SetAudioChannelActive(bool aIsActive);
|
||||
// Called by child
|
||||
void ForceSpeaker(bool enable, uint64_t aChildid);
|
||||
// Register the SpeakerManager to service for notify the speakerforcedchange event
|
||||
void RegisterSpeakerManager(SpeakerManager* aSpeakerManager)
|
||||
{
|
||||
mRegisteredSpeakerManagers.AppendElement(aSpeakerManager);
|
||||
}
|
||||
void UnRegisterSpeakerManager(SpeakerManager* aSpeakerManager)
|
||||
{
|
||||
mRegisteredSpeakerManagers.RemoveElement(aSpeakerManager);
|
||||
}
|
||||
|
||||
protected:
|
||||
SpeakerManagerService();
|
||||
|
||||
virtual ~SpeakerManagerService();
|
||||
// Notify to UA if device speaker status changed
|
||||
virtual void Notify();
|
||||
|
||||
void TurnOnSpeaker(bool aEnable);
|
||||
|
||||
/**
|
||||
* Shutdown the singleton.
|
||||
*/
|
||||
static void Shutdown();
|
||||
|
||||
nsTArray<RefPtr<SpeakerManager> > mRegisteredSpeakerManagers;
|
||||
// Set for remember all the child speaker status
|
||||
nsCheapSet<nsUint64HashKey> mSpeakerStatusSet;
|
||||
// The Speaker status assign by UA
|
||||
bool mOrgSpeakerStatus;
|
||||
|
||||
bool mVisible;
|
||||
// This is needed for IPC communication between
|
||||
// SpeakerManagerServiceChild and this class.
|
||||
friend class ContentParent;
|
||||
friend class ContentChild;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -1,121 +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 "SpeakerManagerServiceChild.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "AudioChannelService.h"
|
||||
#include <cutils/properties.h>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
StaticRefPtr<SpeakerManagerServiceChild> gSpeakerManagerServiceChild;
|
||||
|
||||
// static
|
||||
SpeakerManagerService*
|
||||
SpeakerManagerServiceChild::GetOrCreateSpeakerManagerService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// If we already exist, exit early
|
||||
if (gSpeakerManagerServiceChild) {
|
||||
return gSpeakerManagerServiceChild;
|
||||
}
|
||||
|
||||
// Create new instance, register, return
|
||||
RefPtr<SpeakerManagerServiceChild> service = new SpeakerManagerServiceChild();
|
||||
|
||||
gSpeakerManagerServiceChild = service;
|
||||
|
||||
return gSpeakerManagerServiceChild;
|
||||
}
|
||||
|
||||
// static
|
||||
SpeakerManagerService*
|
||||
SpeakerManagerServiceChild::GetSpeakerManagerService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
return gSpeakerManagerServiceChild;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerServiceChild::ForceSpeaker(bool aEnable, bool aVisible)
|
||||
{
|
||||
mVisible = aVisible;
|
||||
mOrgSpeakerStatus = aEnable;
|
||||
ContentChild *cc = ContentChild::GetSingleton();
|
||||
if (cc) {
|
||||
cc->SendSpeakerManagerForceSpeaker(aEnable && aVisible);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SpeakerManagerServiceChild::GetSpeakerStatus()
|
||||
{
|
||||
ContentChild *cc = ContentChild::GetSingleton();
|
||||
bool status = false;
|
||||
if (cc) {
|
||||
cc->SendSpeakerManagerGetSpeakerStatus(&status);
|
||||
}
|
||||
char propQemu[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.kernel.qemu", propQemu, "");
|
||||
if (!strncmp(propQemu, "1", 1)) {
|
||||
return mOrgSpeakerStatus;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerServiceChild::Shutdown()
|
||||
{
|
||||
if (gSpeakerManagerServiceChild) {
|
||||
gSpeakerManagerServiceChild = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerServiceChild::SetAudioChannelActive(bool aIsActive)
|
||||
{
|
||||
// Content process and switch to background with no audio and speaker forced.
|
||||
// Then disable speaker
|
||||
for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) {
|
||||
mRegisteredSpeakerManagers[i]->SetAudioChannelActive(aIsActive);
|
||||
}
|
||||
}
|
||||
|
||||
SpeakerManagerServiceChild::SpeakerManagerServiceChild()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate();
|
||||
if (audioChannelService) {
|
||||
audioChannelService->RegisterSpeakerManager(this);
|
||||
}
|
||||
MOZ_COUNT_CTOR(SpeakerManagerServiceChild);
|
||||
}
|
||||
|
||||
SpeakerManagerServiceChild::~SpeakerManagerServiceChild()
|
||||
{
|
||||
RefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate();
|
||||
if (audioChannelService) {
|
||||
audioChannelService->UnregisterSpeakerManager(this);
|
||||
}
|
||||
MOZ_COUNT_DTOR(SpeakerManagerServiceChild);
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerServiceChild::Notify()
|
||||
{
|
||||
for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) {
|
||||
mRegisteredSpeakerManagers[i]->DispatchSimpleEvent(NS_LITERAL_STRING("speakerforcedchange"));
|
||||
}
|
||||
}
|
||||
@@ -1,44 +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_dom_SpeakerManagerServicechild_h__
|
||||
#define mozilla_dom_SpeakerManagerServicechild_h__
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "SpeakerManagerService.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
/* This class is used to do the IPC to enable/disable speaker status
|
||||
Also handle the application speaker competition problem
|
||||
*/
|
||||
class SpeakerManagerServiceChild : public SpeakerManagerService
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Return null or instance which has been created.
|
||||
*/
|
||||
static SpeakerManagerService* GetSpeakerManagerService();
|
||||
/*
|
||||
* Return SpeakerManagerServiceChild instance.
|
||||
* If SpeakerManagerServiceChild is not exist, create and return new one.
|
||||
*/
|
||||
static SpeakerManagerService* GetOrCreateSpeakerManagerService();
|
||||
static void Shutdown();
|
||||
virtual void ForceSpeaker(bool aEnable, bool aVisible) override;
|
||||
virtual bool GetSpeakerStatus() override;
|
||||
virtual void SetAudioChannelActive(bool aIsActive) override;
|
||||
virtual void Notify() override;
|
||||
protected:
|
||||
SpeakerManagerServiceChild();
|
||||
virtual ~SpeakerManagerServiceChild();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,23 +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/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
||||
EXPORTS += [
|
||||
'SpeakerManager.h',
|
||||
'SpeakerManagerService.h',
|
||||
'SpeakerManagerServiceChild.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'SpeakerManager.cpp',
|
||||
'SpeakerManagerService.cpp',
|
||||
'SpeakerManagerServiceChild.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
@@ -1,3 +0,0 @@
|
||||
[DEFAULT]
|
||||
|
||||
[test_speakermanager.html]
|
||||
@@ -1,53 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test MozSpeakerManager API</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
"use strict";
|
||||
|
||||
function testObject() {
|
||||
var mgr = new MozSpeakerManager();
|
||||
var spkforced = false;
|
||||
mgr.onspeakerforcedchange = function() {
|
||||
if (spkforced) {
|
||||
is(mgr.speakerforced, true, 'speaker should be true');
|
||||
spkforced = false;
|
||||
mgr.forcespeaker = false;
|
||||
} else {
|
||||
is(mgr.speakerforced, false, 'speaker should be false');
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
spkforced = true;
|
||||
mgr.forcespeaker = true;
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
// Currently applicable only on FxOS
|
||||
if (navigator.userAgent.indexOf("Mobile") != -1 &&
|
||||
navigator.appVersion.indexOf("Android") == -1) {
|
||||
testObject();
|
||||
} else {
|
||||
ok(true, "MozSpeakerManager on Firefox OS only.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "speaker-control", "allow": 1, "context": document }],
|
||||
startTests);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,92 +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/DOMRequestHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const TETHERING_TYPE_WIFI = "wifi";
|
||||
const TETHERING_TYPE_BLUETOOTH = "bt";
|
||||
const TETHERING_TYPE_USB = "usb";
|
||||
|
||||
function TetheringManager() {
|
||||
}
|
||||
|
||||
TetheringManager.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
classDescription: "TetheringManager",
|
||||
classID: Components.ID("{bd8a831c-d8ec-4f00-8803-606e50781097}"),
|
||||
contractID: "@mozilla.org/dom/tetheringmanager;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver]),
|
||||
|
||||
init: function(aWindow) {
|
||||
const messages = ["WifiManager:setWifiTethering:Return:OK",
|
||||
"WifiManager:setWifiTethering:Return:NO"];
|
||||
this.initDOMRequestHelper(aWindow, messages);
|
||||
},
|
||||
|
||||
// TODO : aMessage format may be different after supporting bt/usb.
|
||||
// for now, use wifi format first.
|
||||
receiveMessage: function(aMessage) {
|
||||
let data = aMessage.data.data;
|
||||
|
||||
let resolver = this.takePromiseResolver(data.resolverId);
|
||||
if (!resolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "WifiManager:setWifiTethering:Return:OK":
|
||||
resolver.resolve(data);
|
||||
break;
|
||||
case "WifiManager:setWifiTethering:Return:NO":
|
||||
resolver.reject(data.reason);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
setTetheringEnabled: function setTetheringEnabled(aEnabled, aType, aConfig) {
|
||||
let self = this;
|
||||
switch (aType) {
|
||||
case TETHERING_TYPE_WIFI:
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
let data = { resolverId: aResolverId, enabled: aEnabled, config: aConfig };
|
||||
cpmm.sendAsyncMessage("WifiManager:setWifiTethering", { data: data});
|
||||
});
|
||||
case TETHERING_TYPE_BLUETOOTH:
|
||||
case TETHERING_TYPE_USB:
|
||||
default:
|
||||
debug("tethering type(" + aType + ") doesn't support");
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.takePromiseResolver(aResolverId).reject();
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory =
|
||||
XPCOMUtils.generateNSGetFactory([TetheringManager]);
|
||||
|
||||
var debug;
|
||||
if (DEBUG) {
|
||||
debug = function (s) {
|
||||
dump("-*- TetheringManager component: " + s + "\n");
|
||||
};
|
||||
} else {
|
||||
debug = function (s) {};
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#TetheringManager.js
|
||||
component {bd8a831c-d8ec-4f00-8803-606e50781097} TetheringManager.js
|
||||
contract @mozilla.org/tetheringmanager;1 {bd8a831c-d8ec-4f00-8803-606e50781097}
|
||||
|
||||
@@ -1,12 +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/.
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'TetheringManager.js',
|
||||
'TetheringManager.manifest',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
@@ -1,768 +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/. */
|
||||
|
||||
const TYPE_WIFI = "wifi";
|
||||
const TYPE_BLUETOOTH = "bt";
|
||||
const TYPE_USB = "usb";
|
||||
|
||||
/**
|
||||
* General tethering setting.
|
||||
*/
|
||||
const TETHERING_SETTING_IP = "192.168.1.1";
|
||||
const TETHERING_SETTNG_PREFIX = "24";
|
||||
const TETHERING_SETTING_START_IP = "192.168.1.10";
|
||||
const TETHERING_SETTING_END_IP = "192.168.1.30";
|
||||
const TETHERING_SETTING_DNS1 = "8.8.8.8";
|
||||
const TETHERING_SETTING_DNS2 = "8.8.4.4";
|
||||
|
||||
const TETHERING_NETWORK_ADDR = "192.168.1.0/24";
|
||||
|
||||
/**
|
||||
* Wifi tethering setting.
|
||||
*/
|
||||
const TETHERING_SETTING_SSID = "FirefoxHotSpot";
|
||||
const TETHERING_SETTING_SECURITY = "open";
|
||||
const TETHERING_SETTING_KEY = "1234567890";
|
||||
|
||||
const SETTINGS_RIL_DATA_ENABLED = 'ril.data.enabled';
|
||||
const SETTINGS_KEY_DATA_APN_SETTINGS = "ril.data.apnSettings";
|
||||
|
||||
// Emulate Promise.jsm semantics.
|
||||
Promise.defer = function() { return new Deferred(); }
|
||||
function Deferred() {
|
||||
this.promise = new Promise(function(resolve, reject) {
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
}.bind(this));
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
var gTestSuite = (function() {
|
||||
let suite = {};
|
||||
|
||||
let tetheringManager;
|
||||
let pendingEmulatorShellCount = 0;
|
||||
|
||||
/**
|
||||
* A wrapper function of "is".
|
||||
*
|
||||
* Calls the marionette function "is" as well as throws an exception
|
||||
* if the givens values are not equal.
|
||||
*
|
||||
* @param value1
|
||||
* Any type of value to compare.
|
||||
*
|
||||
* @param value2
|
||||
* Any type of value to compare.
|
||||
*
|
||||
* @param message
|
||||
* Debug message for this check.
|
||||
*
|
||||
*/
|
||||
function isOrThrow(value1, value2, message) {
|
||||
is(value1, value2, message);
|
||||
if (value1 !== value2) {
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send emulator shell command with safe guard.
|
||||
*
|
||||
* We should only call |finish()| after all emulator command transactions
|
||||
* end, so here comes with the pending counter. Resolve when the emulator
|
||||
* gives positive response, and reject otherwise.
|
||||
*
|
||||
* Fulfill params:
|
||||
* result -- an array of emulator response lines.
|
||||
* Reject params:
|
||||
* result -- an array of emulator response lines.
|
||||
*
|
||||
* @param aCommand
|
||||
* A string command to be passed to emulator through its telnet console.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function runEmulatorShellSafe(aCommand) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
++pendingEmulatorShellCount;
|
||||
runEmulatorShell(aCommand, function(aResult) {
|
||||
--pendingEmulatorShellCount;
|
||||
|
||||
ok(true, "Emulator shell response: " + JSON.stringify(aResult));
|
||||
if (Array.isArray(aResult)) {
|
||||
deferred.resolve(aResult);
|
||||
} else {
|
||||
deferred.reject(aResult);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for timeout.
|
||||
*
|
||||
* Resolve when the given duration elapsed. Never reject.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
*
|
||||
* @param aTimeoutMs
|
||||
* The duration after which the timeout event should occurs.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForTimeout(aTimeoutMs) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
setTimeout(function() {
|
||||
deferred.resolve();
|
||||
}, aTimeoutMs);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mozSettings value specified by @aKey.
|
||||
*
|
||||
* Resolve if that mozSettings value is retrieved successfully, reject
|
||||
* otherwise.
|
||||
*
|
||||
* Fulfill params:
|
||||
* The corresponding mozSettings value of the key.
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aKey
|
||||
* A string.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function getSettings(aKey) {
|
||||
let request = navigator.mozSettings.createLock().get(aKey);
|
||||
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve(aEvent) {
|
||||
ok(true, "getSettings(" + aKey + ") - success");
|
||||
return aEvent.target.result[aKey];
|
||||
}, function reject(aEvent) {
|
||||
ok(false, "getSettings(" + aKey + ") - error");
|
||||
throw aEvent.target.error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mozSettings values.
|
||||
*
|
||||
* Resolve if that mozSettings value is set successfully, reject otherwise.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aSettings
|
||||
* An object of format |{key1: value1, key2: value2, ...}|.
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setSettings(aSettings) {
|
||||
let lock = window.navigator.mozSettings.createLock();
|
||||
let request = lock.set(aSettings);
|
||||
let deferred = Promise.defer();
|
||||
lock.onsettingstransactionsuccess = function () {
|
||||
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
|
||||
deferred.resolve();
|
||||
};
|
||||
lock.onsettingstransactionfailure = function (aEvent) {
|
||||
ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
|
||||
deferred.reject();
|
||||
throw aEvent.target.error;
|
||||
};
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mozSettings value with only one key.
|
||||
*
|
||||
* Resolve if that mozSettings value is set successfully, reject otherwise.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aKey
|
||||
* A string key.
|
||||
* @param aValue
|
||||
* An object value.
|
||||
* @param aAllowError [optional]
|
||||
* A boolean value. If set to true, an error response won't be treated
|
||||
* as test failure. Default: false.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setSettings1(aKey, aValue, aAllowError) {
|
||||
let settings = {};
|
||||
settings[aKey] = aValue;
|
||||
return setSettings(settings, aAllowError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient MozSettings getter for SETTINGS_KEY_DATA_APN_SETTINGS.
|
||||
*/
|
||||
function getDataApnSettings(aAllowError) {
|
||||
return getSettings(SETTINGS_KEY_DATA_APN_SETTINGS, aAllowError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient MozSettings setter for SETTINGS_KEY_DATA_APN_SETTINGS.
|
||||
*/
|
||||
function setDataApnSettings(aApnSettings, aAllowError) {
|
||||
return setSettings1(SETTINGS_KEY_DATA_APN_SETTINGS, aApnSettings, aAllowError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 'ro.tethering.dun_required' system property to 1. Note that this is a
|
||||
* 'ro' property, it can only be set once.
|
||||
*/
|
||||
function setTetheringDunRequired() {
|
||||
return runEmulatorShellSafe(['setprop', 'ro.tethering.dun_required', '1']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
|
||||
*
|
||||
* Fulfill params: A DOMEvent.
|
||||
* Reject params: A DOMEvent.
|
||||
*
|
||||
* @param aRequest
|
||||
* A DOMRequest instance.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function wrapDomRequestAsPromise(aRequest) {
|
||||
let deffered = Promise.defer();
|
||||
|
||||
ok(aRequest instanceof DOMRequest,
|
||||
"aRequest is instanceof" + aRequest.constructor);
|
||||
|
||||
aRequest.onsuccess = function(aEvent) {
|
||||
deffered.resolve(aEvent);
|
||||
};
|
||||
aRequest.onerror = function(aEvent) {
|
||||
deffered.reject(aEvent);
|
||||
};
|
||||
|
||||
return deffered.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for one named MozMobileConnection event.
|
||||
*
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
*
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
*
|
||||
* @param aEventName
|
||||
* A string event name.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForMobileConnectionEventOnce(aEventName, aServiceId) {
|
||||
aServiceId = aServiceId || 0;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let mobileconnection = navigator.mozMobileConnections[aServiceId];
|
||||
|
||||
mobileconnection.addEventListener(aEventName, function onevent(aEvent) {
|
||||
mobileconnection.removeEventListener(aEventName, onevent);
|
||||
|
||||
ok(true, "Mobile connection event '" + aEventName + "' got.");
|
||||
deferred.resolve(aEvent);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for RIL data being connected.
|
||||
*
|
||||
* This function will check |MozMobileConnection.data.connected| on
|
||||
* every 'datachange' event. Resolve when |MozMobileConnection.data.connected|
|
||||
* becomes the expected state. Never reject.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aConnected
|
||||
* Boolean that indicates the desired data state.
|
||||
*
|
||||
* @param aServiceId [optional]
|
||||
* A numeric DSDS service id. Default: 0.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForRilDataConnected(aConnected, aServiceId) {
|
||||
aServiceId = aServiceId || 0;
|
||||
|
||||
return waitForMobileConnectionEventOnce('datachange', aServiceId)
|
||||
.then(function () {
|
||||
let mobileconnection = navigator.mozMobileConnections[aServiceId];
|
||||
if (mobileconnection.data.connected !== aConnected) {
|
||||
return waitForRilDataConnected(aConnected, aServiceId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify everything about routing when the wifi tethering is either on or off.
|
||||
*
|
||||
* We use two unix commands to verify the routing: 'netcfg' and 'ip route'.
|
||||
* For now the following two things will be checked:
|
||||
* 1) The default route interface should be 'rmnet0'.
|
||||
* 2) wlan0 is up and its ip is set to a private subnet.
|
||||
*
|
||||
* We also verify iptables output as netd's NatController will execute
|
||||
* $ iptables -t nat -A POSTROUTING -o rmnet0 -j MASQUERADE
|
||||
*
|
||||
* For tethering through dun, we use 'ip rule' to find the secondary routing
|
||||
* table and look for default route on that table.
|
||||
*
|
||||
* Resolve when the verification is successful and reject otherwise.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: String that indicates the reason of rejection.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function verifyTetheringRouting(aEnabled, aIsDun) {
|
||||
let netcfgResult = {};
|
||||
let ipRouteResult = {};
|
||||
let ipSecondaryRouteResult = {};
|
||||
|
||||
// Execute 'netcfg' and parse to |netcfgResult|, each key of which is the
|
||||
// interface name and value is { ip(string) }.
|
||||
function exeAndParseNetcfg() {
|
||||
return runEmulatorShellSafe(['netcfg'])
|
||||
.then(function (aLines) {
|
||||
// Sample output:
|
||||
//
|
||||
// lo UP 127.0.0.1/8 0x00000049 00:00:00:00:00:00
|
||||
// eth0 UP 10.0.2.15/24 0x00001043 52:54:00:12:34:56
|
||||
// rmnet1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:58
|
||||
// rmnet2 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:59
|
||||
// rmnet3 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:5a
|
||||
// wlan0 UP 192.168.1.1/24 0x00001043 52:54:00:12:34:5b
|
||||
// sit0 DOWN 0.0.0.0/0 0x00000080 00:00:00:00:00:00
|
||||
// rmnet0 UP 10.0.2.100/24 0x00001043 52:54:00:12:34:57
|
||||
//
|
||||
aLines.forEach(function (aLine) {
|
||||
let tokens = aLine.split(/\s+/);
|
||||
if (tokens.length < 5) {
|
||||
return;
|
||||
}
|
||||
let ifname = tokens[0];
|
||||
let ip = (tokens[2].split('/'))[0];
|
||||
netcfgResult[ifname] = { ip: ip };
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Execute 'ip route' and parse to |ipRouteResult|, each key of which is the
|
||||
// interface name and value is { src(string), default(boolean) }.
|
||||
function exeAndParseIpRoute() {
|
||||
return runEmulatorShellSafe(['ip', 'route'])
|
||||
.then(function (aLines) {
|
||||
// Sample output:
|
||||
//
|
||||
// 10.0.2.4 via 10.0.2.2 dev rmnet0
|
||||
// 10.0.2.3 via 10.0.2.2 dev rmnet0
|
||||
// 192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.1
|
||||
// 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
|
||||
// 10.0.2.0/24 dev rmnet0 proto kernel scope link src 10.0.2.100
|
||||
// default via 10.0.2.2 dev rmnet0
|
||||
// default via 10.0.2.2 dev eth0 metric 2
|
||||
//
|
||||
|
||||
// Parse source ip for each interface.
|
||||
aLines.forEach(function (aLine) {
|
||||
let tokens = aLine.trim().split(/\s+/);
|
||||
let srcIndex = tokens.indexOf('src');
|
||||
if (srcIndex < 0 || srcIndex + 1 >= tokens.length) {
|
||||
return;
|
||||
}
|
||||
let ifname = tokens[2];
|
||||
let src = tokens[srcIndex + 1];
|
||||
ipRouteResult[ifname] = { src: src, default: false };
|
||||
});
|
||||
|
||||
// Parse default interfaces.
|
||||
aLines.forEach(function (aLine) {
|
||||
let tokens = aLine.split(/\s+/);
|
||||
if (tokens.length < 2) {
|
||||
return;
|
||||
}
|
||||
if ('default' === tokens[0]) {
|
||||
let ifnameIndex = tokens.indexOf('dev');
|
||||
if (ifnameIndex < 0 || ifnameIndex + 1 >= tokens.length) {
|
||||
return;
|
||||
}
|
||||
let ifname = tokens[ifnameIndex + 1];
|
||||
if (ipRouteResult[ifname]) {
|
||||
ipRouteResult[ifname].default = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Find MASQUERADE in POSTROUTING section. 'MASQUERADE' should be found
|
||||
// when tethering is enabled. 'MASQUERADE' shouldn't be found when tethering
|
||||
// is disabled.
|
||||
function verifyIptables() {
|
||||
return runEmulatorShellSafe(['iptables', '-t', 'nat', '-L', 'POSTROUTING'])
|
||||
.then(function(aLines) {
|
||||
// $ iptables -t nat -L POSTROUTING
|
||||
//
|
||||
// Sample output (tethering on):
|
||||
//
|
||||
// Chain POSTROUTING (policy ACCEPT)
|
||||
// target prot opt source destination
|
||||
// MASQUERADE all -- anywhere anywhere
|
||||
//
|
||||
let found = (function find_MASQUERADE() {
|
||||
// Skip first two lines.
|
||||
for (let i = 2; i < aLines.length; i++) {
|
||||
if (-1 !== aLines[i].indexOf('MASQUERADE')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
if ((aEnabled && !found) || (!aEnabled && found)) {
|
||||
throw 'MASQUERADE' + (found ? '' : ' not') + ' found while tethering is ' +
|
||||
(aEnabled ? 'enabled' : 'disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Execute 'ip rule show', there must be one rule for tethering network
|
||||
// address to lookup for a secondary routing table, return that table id.
|
||||
function verifyIpRule() {
|
||||
if (!aIsDun) {
|
||||
return;
|
||||
}
|
||||
|
||||
return runEmulatorShellSafe(['ip', 'rule', 'show'])
|
||||
.then(function (aLines) {
|
||||
// Sample output:
|
||||
//
|
||||
// 0: from all lookup local
|
||||
// 32765: from 192.168.1.0/24 lookup 60
|
||||
// 32766: from all lookup main
|
||||
// 32767: from all lookup default
|
||||
//
|
||||
let tableId = (function findTableId() {
|
||||
for (let i = 0; i < aLines.length; i++) {
|
||||
let tokens = aLines[i].split(/\s+/);
|
||||
if (-1 != tokens.indexOf(TETHERING_NETWORK_ADDR)) {
|
||||
let lookupIndex = tokens.indexOf('lookup');
|
||||
if (lookupIndex < 0 || lookupIndex + 1 >= tokens.length) {
|
||||
return;
|
||||
}
|
||||
return tokens[lookupIndex + 1];
|
||||
}
|
||||
}
|
||||
return;
|
||||
})();
|
||||
|
||||
if ((aEnabled && !tableId) || (!aEnabled && tableId)) {
|
||||
throw 'Secondary table' + (tableId ? '' : ' not') + ' found while tethering is ' +
|
||||
(aEnabled ? 'enabled' : 'disabled');
|
||||
}
|
||||
|
||||
return tableId;
|
||||
});
|
||||
}
|
||||
|
||||
// Given the table id, use 'ip rule show table <table id>' to find the
|
||||
// default route on that secondary routing table.
|
||||
function execAndParseSecondaryTable(aTableId) {
|
||||
if (!aIsDun || !aEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
return runEmulatorShellSafe(['ip', 'route', 'show', 'table', aTableId])
|
||||
.then(function (aLines) {
|
||||
// We only look for default route in secondary table.
|
||||
aLines.forEach(function (aLine) {
|
||||
let tokens = aLine.split(/\s+/);
|
||||
if (tokens.length < 2) {
|
||||
return;
|
||||
}
|
||||
if ('default' === tokens[0]) {
|
||||
let ifnameIndex = tokens.indexOf('dev');
|
||||
if (ifnameIndex < 0 || ifnameIndex + 1 >= tokens.length) {
|
||||
return;
|
||||
}
|
||||
let ifname = tokens[ifnameIndex + 1];
|
||||
ipSecondaryRouteResult[ifname] = { default: true };
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function verifyDefaultRouteAndIp(aExpectedWifiTetheringIp) {
|
||||
log(JSON.stringify(ipRouteResult));
|
||||
log(JSON.stringify(ipSecondaryRouteResult));
|
||||
log(JSON.stringify(netcfgResult));
|
||||
|
||||
if (aEnabled) {
|
||||
isOrThrow(ipRouteResult['rmnet0'].src, netcfgResult['rmnet0'].ip, 'rmnet0.ip');
|
||||
isOrThrow(ipRouteResult['rmnet0'].default, true, 'rmnet0.default');
|
||||
|
||||
isOrThrow(ipRouteResult['wlan0'].src, netcfgResult['wlan0'].ip, 'wlan0.ip');
|
||||
isOrThrow(ipRouteResult['wlan0'].src, aExpectedWifiTetheringIp, 'expected ip');
|
||||
isOrThrow(ipRouteResult['wlan0'].default, false, 'wlan0.default');
|
||||
|
||||
if (aIsDun) {
|
||||
isOrThrow(ipRouteResult['rmnet1'].src, netcfgResult['rmnet1'].ip, 'rmnet1.ip');
|
||||
isOrThrow(ipRouteResult['rmnet1'].default, false, 'rmnet1.default');
|
||||
// Dun's network default route is set on secondary routing table.
|
||||
isOrThrow(ipSecondaryRouteResult['rmnet1'].default, true, 'secondary rmnet1.default');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return verifyIptables()
|
||||
.then(verifyIpRule)
|
||||
.then(tableId => execAndParseSecondaryTable(tableId))
|
||||
.then(exeAndParseNetcfg)
|
||||
.then(exeAndParseIpRoute)
|
||||
.then(() => verifyDefaultRouteAndIp(TETHERING_SETTING_IP));
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to enable/disable wifi tethering.
|
||||
*
|
||||
* Enable/disable wifi tethering by using setTetheringEnabled API
|
||||
* Resolve when the routing is verified to set up successfully in 20 seconds. The polling
|
||||
* period is 1 second.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: The error message.
|
||||
*
|
||||
* @param aEnabled
|
||||
* Boolean that indicates to enable or disable wifi tethering.
|
||||
* @param aIsDun
|
||||
* Boolean that indicates whether dun is required.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setWifiTetheringEnabled(aEnabled, aIsDun) {
|
||||
let RETRY_INTERVAL_MS = 1000;
|
||||
let retryCnt = 20;
|
||||
|
||||
let config = {
|
||||
"ip" : TETHERING_SETTING_IP,
|
||||
"prefix" : TETHERING_SETTNG_PREFIX,
|
||||
"startIp" : TETHERING_SETTING_START_IP,
|
||||
"endIp" : TETHERING_SETTING_END_IP,
|
||||
"dns1" : TETHERING_SETTING_DNS1,
|
||||
"dns2" : TETHERING_SETTING_DNS2,
|
||||
"wifiConfig": {
|
||||
"ssid" : TETHERING_SETTING_SSID,
|
||||
"security" : TETHERING_SETTING_SECURITY
|
||||
}
|
||||
};
|
||||
|
||||
return tetheringManager.setTetheringEnabled(aEnabled, TYPE_WIFI, config)
|
||||
.then(function waitForRoutingVerified() {
|
||||
return verifyTetheringRouting(aEnabled, aIsDun)
|
||||
.then(null, function onreject(aReason) {
|
||||
|
||||
log('verifyTetheringRouting rejected due to ' + aReason +
|
||||
' (' + retryCnt + ')');
|
||||
|
||||
if (!retryCnt--) {
|
||||
throw aReason;
|
||||
}
|
||||
|
||||
return waitForTimeout(RETRY_INTERVAL_MS).then(waitForRoutingVerified);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure wifi is enabled/disabled.
|
||||
*
|
||||
* Issue a wifi enable/disable request if wifi is not in the desired state;
|
||||
* return a resolved promise otherwise.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @return a resolved promise or deferred promise.
|
||||
*/
|
||||
function ensureWifiEnabled(aEnabled) {
|
||||
let wifiManager = window.navigator.mozWifiManager;
|
||||
if (wifiManager.enabled === aEnabled) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
let request = wifiManager.setWifiEnabled(aEnabled);
|
||||
return wrapDomRequestAsPromise(request)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure tethering manager exists.
|
||||
*
|
||||
* Check navigator property |mozTetheringManager| to ensure we could access
|
||||
* tethering related function.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function ensureTetheringManager() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
tetheringManager = window.navigator.mozTetheringManager;
|
||||
|
||||
if (tetheringManager instanceof MozTetheringManager) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
log("navigator.mozTetheringManager is unavailable");
|
||||
deferred.reject();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required permissions for tethering. Never reject.
|
||||
*
|
||||
* The permissions required for wifi testing are 'wifi-manage' and 'settings-write'.
|
||||
* Never reject.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function acquirePermission() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let permissions = [{ 'type': 'wifi-manage', 'allow': 1, 'context': window.document },
|
||||
{ 'type': 'settings-write', 'allow': 1, 'context': window.document },
|
||||
{ 'type': 'settings-read', 'allow': 1, 'context': window.document },
|
||||
{ 'type': 'settings-api-write', 'allow': 1, 'context': window.document },
|
||||
{ 'type': 'settings-api-read', 'allow': 1, 'context': window.document },
|
||||
{ 'type': 'mobileconnection', 'allow': 1, 'context': window.document }];
|
||||
|
||||
SpecialPowers.pushPermissions(permissions, function() {
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common test routine.
|
||||
*
|
||||
* Start a test with the given test case chain. The test environment will be
|
||||
* settled down before the test. After the test, all the affected things will
|
||||
* be restored.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aTestCaseChain
|
||||
* The test case entry point, which can be a function or a promise.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
suite.startTest = function(aTestCaseChain) {
|
||||
function setUp() {
|
||||
return ensureTetheringManager()
|
||||
.then(acquirePermission);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
waitFor(finish, function() {
|
||||
return pendingEmulatorShellCount === 0;
|
||||
});
|
||||
}
|
||||
|
||||
return setUp()
|
||||
.then(aTestCaseChain)
|
||||
.then(function onresolve() {
|
||||
tearDown();
|
||||
}, function onreject(aReason) {
|
||||
ok(false, 'Promise rejects during test' + (aReason ? '(' + aReason + ')' : ''));
|
||||
tearDown();
|
||||
});
|
||||
};
|
||||
|
||||
//---------------------------------------------------
|
||||
// Public test suite functions
|
||||
//---------------------------------------------------
|
||||
suite.ensureWifiEnabled = ensureWifiEnabled;
|
||||
suite.setWifiTetheringEnabled = setWifiTetheringEnabled;
|
||||
suite.getDataApnSettings = getDataApnSettings;
|
||||
suite.setDataApnSettings = setDataApnSettings;
|
||||
suite.setTetheringDunRequired = setTetheringDunRequired;
|
||||
|
||||
|
||||
/**
|
||||
* The common test routine for wifi tethering.
|
||||
*
|
||||
* Set 'ril.data.enabled' to true
|
||||
* before testing and restore it afterward. It will also verify 'ril.data.enabled'
|
||||
* and 'tethering.wifi.enabled' to be false in the beginning. Note that this routine
|
||||
* will NOT change the state of 'tethering.wifi.enabled' so the user should enable
|
||||
* than disable on his/her own. This routine will only check if tethering is turned
|
||||
* off after testing.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aTestCaseChain
|
||||
* The test case entry point, which can be a function or a promise.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
suite.startTetheringTest = function(aTestCaseChain) {
|
||||
let oriDataEnabled;
|
||||
function verifyInitialState() {
|
||||
return getSettings(SETTINGS_RIL_DATA_ENABLED)
|
||||
.then(enabled => initTetheringTestEnvironment(enabled));
|
||||
}
|
||||
|
||||
function initTetheringTestEnvironment(aEnabled) {
|
||||
oriDataEnabled = aEnabled;
|
||||
if (aEnabled) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.all([waitForRilDataConnected(true),
|
||||
setSettings1(SETTINGS_RIL_DATA_ENABLED, true)]);
|
||||
}
|
||||
}
|
||||
|
||||
function restoreToInitialState() {
|
||||
return setSettings1(SETTINGS_RIL_DATA_ENABLED, oriDataEnabled);
|
||||
}
|
||||
|
||||
return suite.startTest(function() {
|
||||
return verifyInitialState()
|
||||
.then(aTestCaseChain)
|
||||
.then(restoreToInitialState, function onreject(aReason) {
|
||||
return restoreToInitialState()
|
||||
.then(() => { throw aReason; }); // Re-throw the orignal reject reason.
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return suite;
|
||||
})();
|
||||
@@ -1,7 +0,0 @@
|
||||
[DEFAULT]
|
||||
run-if = buildapp == 'b2g'
|
||||
|
||||
[test_wifi_tethering_enabled.js]
|
||||
; The following test must be the last tethering test ran, as it sets the
|
||||
; 'ro.tethering.dun_required' property.
|
||||
[test_wifi_tethering_dun.js]
|
||||
@@ -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/. */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
gTestSuite.startTest(function() {
|
||||
let origApnSettings;
|
||||
return gTestSuite.getDataApnSettings()
|
||||
.then(value => {
|
||||
origApnSettings = value;
|
||||
})
|
||||
.then(() => {
|
||||
// Set dun apn settings.
|
||||
let apnSettings = [[ { "carrier": "T-Mobile US",
|
||||
"apn": "epc1.tmobile.com",
|
||||
"mmsc": "http://mms.msg.eng.t-mobile.com/mms/wapenc",
|
||||
"types": ["default","supl","mms"] },
|
||||
{ "carrier": "T-Mobile US",
|
||||
"apn": "epc2.tmobile.com",
|
||||
"types": ["dun"] } ]];
|
||||
return gTestSuite.setDataApnSettings(apnSettings);
|
||||
})
|
||||
.then(() => gTestSuite.setTetheringDunRequired())
|
||||
.then(() => gTestSuite.startTetheringTest(function() {
|
||||
return gTestSuite.ensureWifiEnabled(false)
|
||||
.then(() => gTestSuite.setWifiTetheringEnabled(true, true))
|
||||
.then(() => gTestSuite.setWifiTetheringEnabled(false, true));
|
||||
}))
|
||||
// Restore apn settings.
|
||||
.then(() => {
|
||||
if (origApnSettings) {
|
||||
return gTestSuite.setDataApnSettings(origApnSettings);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,12 +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/. */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
gTestSuite.startTetheringTest(function() {
|
||||
return gTestSuite.ensureWifiEnabled(false)
|
||||
.then(() => gTestSuite.setWifiTetheringEnabled(true))
|
||||
.then(() => gTestSuite.setWifiTetheringEnabled(false));
|
||||
});
|
||||
@@ -1,73 +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/. */
|
||||
|
||||
/**
|
||||
* Provide the detailed options for specifying different kinds of data filtering
|
||||
* in getSamples function.
|
||||
*/
|
||||
dictionary NetworkStatsGetOptions
|
||||
{
|
||||
/**
|
||||
* App manifest URL is used to filter network stats by app, while service type
|
||||
* is used to filter stats by system service.
|
||||
* Note that, these two options cannot be specified at the same time for now;
|
||||
* others, an NS_ERROR_NOT_IMPLMENTED exception will be thrown.
|
||||
*/
|
||||
DOMString? appManifestURL = null;
|
||||
DOMString serviceType = "";
|
||||
/**
|
||||
* If it is set as true, only the browsing traffic, which is generated from
|
||||
* the mozbrowser iframe element within an app, is returned in result.
|
||||
* If it is set as false or not set, the total traffic, which is generated
|
||||
* from both the mozapp and mozbrowser iframe elements, is returned.
|
||||
*/
|
||||
boolean browsingTrafficOnly = false;
|
||||
};
|
||||
|
||||
dictionary NetworkStatsAlarmOptions
|
||||
{
|
||||
Date startTime;
|
||||
Date data;
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/networkstats;1",
|
||||
ChromeOnly,
|
||||
Pref="dom.mozNetworkStats.enabled"]
|
||||
interface MozNetworkStats {
|
||||
/**
|
||||
* App manifest URL of an application for specifying the per-app stats of the
|
||||
* specified app.
|
||||
*/
|
||||
readonly attribute DOMString appManifestURL;
|
||||
|
||||
/**
|
||||
* True if this stats is the browsing traffic of an app (the traffic generated
|
||||
* by a mozbrowser iframe element).
|
||||
* Otherwise this stats represents the total traffic of an app.
|
||||
*/
|
||||
readonly attribute boolean browsingTrafficOnly;
|
||||
|
||||
/**
|
||||
* Service type is used to retrieve the corresponding "system-only" stats.
|
||||
* E.g., "Tethering", "OTA", etc.
|
||||
*/
|
||||
readonly attribute DOMString serviceType;
|
||||
|
||||
/**
|
||||
* Network the returned data belongs to.
|
||||
*/
|
||||
readonly attribute MozNetworkStatsInterface network;
|
||||
|
||||
/**
|
||||
* Stats for a network.
|
||||
*/
|
||||
[Cached, Pure]
|
||||
readonly attribute sequence<MozNetworkStatsData> data;
|
||||
|
||||
/**
|
||||
* Dates
|
||||
*/
|
||||
readonly attribute object start;
|
||||
readonly attribute object end;
|
||||
};
|
||||
@@ -1,13 +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/. */
|
||||
|
||||
[JSImplementation="@mozilla.org/networkstatsalarm;1",
|
||||
ChromeOnly,
|
||||
Pref="dom.mozNetworkStats.enabled"]
|
||||
interface MozNetworkStatsAlarm {
|
||||
readonly attribute unsigned long alarmId;
|
||||
readonly attribute MozNetworkStatsInterface network;
|
||||
readonly attribute long long threshold;
|
||||
readonly attribute any data;
|
||||
};
|
||||
@@ -1,12 +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/. */
|
||||
|
||||
[JSImplementation="@mozilla.org/networkStatsdata;1",
|
||||
ChromeOnly,
|
||||
Pref="dom.mozNetworkStats.enabled"]
|
||||
interface MozNetworkStatsData {
|
||||
readonly attribute unsigned long rxBytes; // Received bytes.
|
||||
readonly attribute unsigned long txBytes; // Sent bytes.
|
||||
readonly attribute Date date; // Date.
|
||||
};
|
||||
@@ -1,26 +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/. */
|
||||
|
||||
dictionary NetworkInterface {
|
||||
long type;
|
||||
DOMString id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a data interface for which the manager is recording statistics.
|
||||
*/
|
||||
[Constructor(optional NetworkInterface networkinterface),
|
||||
JSImplementation="@mozilla.org/networkstatsinterface;1",
|
||||
ChromeOnly,
|
||||
Pref="dom.mozNetworkStats.enabled"]
|
||||
interface MozNetworkStatsInterface {
|
||||
readonly attribute long type;
|
||||
|
||||
/**
|
||||
* Id value is '0' for wifi or the iccid for mobile (SIM).
|
||||
*/
|
||||
readonly attribute DOMString id;
|
||||
|
||||
jsonifier;
|
||||
};
|
||||
@@ -1,96 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[NavigatorProperty="mozNetworkStats",
|
||||
JSImplementation="@mozilla.org/networkStatsManager;1",
|
||||
ChromeOnly,
|
||||
Pref="dom.mozNetworkStats.enabled"]
|
||||
interface MozNetworkStatsManager {
|
||||
/**
|
||||
* Constants for known interface types.
|
||||
*/
|
||||
const long WIFI = 0;
|
||||
const long MOBILE = 1;
|
||||
|
||||
/**
|
||||
* Find samples between two dates start and end, both included.
|
||||
*
|
||||
* If options is provided, per-app or per-system service usage will be
|
||||
* retrieved; otherwise the target will be overall system usage.
|
||||
*
|
||||
* If success, the request result will be an MozNetworkStats object.
|
||||
*/
|
||||
DOMRequest getSamples(MozNetworkStatsInterface network,
|
||||
Date start,
|
||||
Date end,
|
||||
optional NetworkStatsGetOptions options);
|
||||
|
||||
/**
|
||||
* Install an alarm on a network. The network must be in the return of
|
||||
* getAvailableNetworks() otherwise an "InvalidNetwork" exception will
|
||||
* be raised.
|
||||
*
|
||||
* When total data usage reaches threshold bytes, a "networkstats-alarm"
|
||||
* system message is sent to the application, where the optional parameter
|
||||
* |data| must be a cloneable object.
|
||||
*
|
||||
* If success, the |result| field of the DOMRequest keeps the alarm Id.
|
||||
*/
|
||||
DOMRequest addAlarm(MozNetworkStatsInterface network,
|
||||
long long threshold,
|
||||
optional NetworkStatsAlarmOptions options);
|
||||
|
||||
/**
|
||||
* Obtain all alarms for those networks returned by getAvailableNetworks().
|
||||
* If a network is provided, only retrieves the alarms for that network.
|
||||
* The network must be one of those returned by getAvailebleNetworks() or an
|
||||
* "InvalidNetwork" exception will be raised.
|
||||
*
|
||||
* Each alarm object has the same fields as that in the system message:
|
||||
* - alarmId
|
||||
* - network
|
||||
* - threshold
|
||||
* - data
|
||||
*/
|
||||
DOMRequest getAllAlarms(optional MozNetworkStatsInterface network);
|
||||
|
||||
/**
|
||||
* Remove all network alarms. If an |alarmId| is provided, then only that
|
||||
* alarm is removed.
|
||||
*/
|
||||
DOMRequest removeAlarms(optional unsigned long alarmId = 0);
|
||||
|
||||
/**
|
||||
* Remove all stats related with the provided network from DB.
|
||||
*/
|
||||
DOMRequest clearStats(MozNetworkStatsInterface network);
|
||||
|
||||
/**
|
||||
* Remove all stats in the database.
|
||||
*/
|
||||
DOMRequest clearAllStats();
|
||||
|
||||
/**
|
||||
* Return available networks that used to be saved in the database.
|
||||
*/
|
||||
DOMRequest getAvailableNetworks(); // array of MozNetworkStatsInterface.
|
||||
|
||||
/**
|
||||
* Return available service types that used to be saved in the database.
|
||||
*/
|
||||
DOMRequest getAvailableServiceTypes(); // array of string.
|
||||
|
||||
/**
|
||||
* Minimum time in milliseconds between samples stored in the database.
|
||||
*/
|
||||
readonly attribute long sampleRate;
|
||||
|
||||
/**
|
||||
* Time in milliseconds recorded by the API until present time. All samples
|
||||
* older than maxStorageAge from now are deleted.
|
||||
*/
|
||||
readonly attribute long long maxStorageAge;
|
||||
};
|
||||
@@ -1,18 +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/.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Allow application can control acoustic sound output through speaker.
|
||||
* Reference https://wiki.mozilla.org/WebAPI/SpeakerManager
|
||||
*/
|
||||
[Constructor()]
|
||||
interface MozSpeakerManager : EventTarget {
|
||||
/* query the speaker status */
|
||||
readonly attribute boolean speakerforced;
|
||||
/* force device device's acoustic sound output through speaker */
|
||||
attribute boolean forcespeaker;
|
||||
/* this event will be fired when device's speaker forced status change */
|
||||
attribute EventHandler onspeakerforcedchange;
|
||||
};
|
||||
@@ -1,46 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The capabilities of Wifi. These are guaranteed not to change over the
|
||||
* lifetime of that particular instance.
|
||||
*/
|
||||
enum WifiSecurityMethod {
|
||||
"OPEN",
|
||||
"WEP",
|
||||
"WPA-PSK",
|
||||
"WPA-EAP"
|
||||
};
|
||||
|
||||
enum WifiWpaMethod {
|
||||
"SIM",
|
||||
"PEAP",
|
||||
"TTLS",
|
||||
"TLS"
|
||||
};
|
||||
|
||||
enum WifiWpaPhase2Method {
|
||||
"MSCHAPV2",
|
||||
"GTC"
|
||||
};
|
||||
|
||||
enum WifiWpaCertificate {
|
||||
"SERVER",
|
||||
"USER"
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/mozwificapabilities;1",
|
||||
Func="Navigator::HasWifiManagerSupport"]
|
||||
interface MozWifiCapabilities {
|
||||
[Constant, Cached] readonly attribute sequence<WifiSecurityMethod> security;
|
||||
[Constant, Cached] readonly attribute sequence<WifiWpaMethod> eapMethod;
|
||||
[Constant, Cached] readonly attribute sequence<WifiWpaPhase2Method> eapPhase2;
|
||||
[Constant, Cached] readonly attribute sequence<WifiWpaCertificate> certificate;
|
||||
|
||||
jsonifier;
|
||||
};
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[Constructor(DOMString type, optional MozWifiConnectionInfoEventInit eventInitDict)]
|
||||
interface MozWifiConnectionInfoEvent : Event
|
||||
{
|
||||
/**
|
||||
* Network object with an SSID field.
|
||||
*/
|
||||
readonly attribute any network;
|
||||
|
||||
/**
|
||||
* Strength of the signal to network, in dBm between -55 and -100 dBm.
|
||||
*/
|
||||
readonly attribute short signalStrength;
|
||||
|
||||
/**
|
||||
* Relative signal strength between 0 and 100.
|
||||
*/
|
||||
readonly attribute short relSignalStrength;
|
||||
|
||||
/**
|
||||
* Link speed in Mb/s.
|
||||
*/
|
||||
readonly attribute long linkSpeed;
|
||||
|
||||
/**
|
||||
* IP address in the dotted quad format.
|
||||
*/
|
||||
readonly attribute DOMString? ipAddress;
|
||||
};
|
||||
|
||||
dictionary MozWifiConnectionInfoEventInit : EventInit
|
||||
{
|
||||
any network = null;
|
||||
short signalStrength = 0;
|
||||
short relSignalStrength = 0;
|
||||
long linkSpeed = 0;
|
||||
DOMString ipAddress = "";
|
||||
};
|
||||
@@ -1,347 +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/. */
|
||||
|
||||
enum WifiWPSMethod {
|
||||
"pbc",
|
||||
"pin",
|
||||
"cancel"
|
||||
};
|
||||
|
||||
enum ConnectionStatus {
|
||||
"connecting",
|
||||
"authenticating",
|
||||
"associated",
|
||||
"connected",
|
||||
"disconnected",
|
||||
"wps-timedout",
|
||||
"wps-failed",
|
||||
"wps-overlapped",
|
||||
"connectingfailed"
|
||||
};
|
||||
|
||||
dictionary WifiWPSInfo {
|
||||
WifiWPSMethod method;
|
||||
DOMString? pin;
|
||||
DOMString? bssid;
|
||||
};
|
||||
|
||||
dictionary NetworkProperties {
|
||||
DOMString ssid;
|
||||
long mode;
|
||||
long frequency;
|
||||
sequence<DOMString>? security;
|
||||
sequence<DOMString>? capabilities;
|
||||
boolean known;
|
||||
boolean connected;
|
||||
boolean hidden;
|
||||
DOMString bssid;
|
||||
DOMString signalStrength;
|
||||
long relSignalStrength;
|
||||
DOMString psk;
|
||||
DOMString wep;
|
||||
DOMString wep_key0;
|
||||
DOMString wep_key1;
|
||||
DOMString wep_key2;
|
||||
DOMString wep_key3;
|
||||
long wep_tx_keyidx;
|
||||
long priority;
|
||||
long scan_ssid;
|
||||
DOMString keyManagement;
|
||||
DOMString identity;
|
||||
DOMString password;
|
||||
DOMString auth_alg;
|
||||
DOMString phase1;
|
||||
DOMString phase2;
|
||||
DOMString eap;
|
||||
DOMString pin;
|
||||
boolean dontConnect;
|
||||
DOMString serverCertificate;
|
||||
DOMString subjectMatch;
|
||||
DOMString userCertificate;
|
||||
};
|
||||
|
||||
[Constructor(optional NetworkProperties properties),
|
||||
JSImplementation="@mozilla.org/mozwifinetwork;1",
|
||||
Func="Navigator::HasWifiManagerSupport"]
|
||||
interface MozWifiNetwork {
|
||||
readonly attribute DOMString ssid;
|
||||
readonly attribute long mode;
|
||||
readonly attribute long frequency;
|
||||
[Constant, Cached] readonly attribute sequence<DOMString>? security;
|
||||
[Constant, Cached] readonly attribute sequence<DOMString>? capabilities;
|
||||
readonly attribute boolean known;
|
||||
readonly attribute boolean connected;
|
||||
readonly attribute boolean hidden;
|
||||
|
||||
attribute DOMString? bssid;
|
||||
attribute DOMString? signalStrength;
|
||||
attribute long? relSignalStrength;
|
||||
attribute DOMString? psk;
|
||||
attribute DOMString? wep;
|
||||
attribute DOMString? wep_key0;
|
||||
attribute DOMString? wep_key1;
|
||||
attribute DOMString? wep_key2;
|
||||
attribute DOMString? wep_key3;
|
||||
attribute long? wep_tx_keyidx;
|
||||
attribute long? priority;
|
||||
attribute long? scan_ssid;
|
||||
attribute DOMString? keyManagement;
|
||||
attribute DOMString? identity;
|
||||
attribute DOMString? password;
|
||||
attribute DOMString? auth_alg;
|
||||
attribute DOMString? phase1;
|
||||
attribute DOMString? phase2;
|
||||
attribute DOMString? eap;
|
||||
attribute DOMString? pin;
|
||||
attribute boolean? dontConnect;
|
||||
attribute DOMString? serverCertificate;
|
||||
attribute DOMString? subjectMatch;
|
||||
attribute DOMString? userCertificate;
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/mozwificonnection;1",
|
||||
ChromeOnly]
|
||||
interface MozWifiConnection {
|
||||
readonly attribute ConnectionStatus status;
|
||||
readonly attribute MozWifiNetwork? network;
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/mozwificonnectioninfo;1",
|
||||
ChromeOnly]
|
||||
interface MozWifiConnectionInfo {
|
||||
readonly attribute short signalStrength;
|
||||
readonly attribute short relSignalStrength;
|
||||
readonly attribute long linkSpeed;
|
||||
readonly attribute DOMString? ipAddress;
|
||||
};
|
||||
|
||||
dictionary IPConfiguration {
|
||||
boolean enabled;
|
||||
DOMString ipaddr;
|
||||
DOMString proxy;
|
||||
short maskLength;
|
||||
DOMString gateway;
|
||||
DOMString dns1;
|
||||
DOMString dns2;
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/wifimanager;1",
|
||||
NavigatorProperty="mozWifiManager",
|
||||
Func="Navigator::HasWifiManagerSupport",
|
||||
UnsafeInPrerendering]
|
||||
interface MozWifiManager : EventTarget {
|
||||
/**
|
||||
* Turn on/off wifi functionality.
|
||||
* @param enable true for enable, false for disable.
|
||||
* onsuccess: Wifi enable/disable successfully, including no status change.
|
||||
* onerror: Wifi enable/disable failed or prohibited.
|
||||
*/
|
||||
DOMRequest setWifiEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Returns the list of currently available networks.
|
||||
* onsuccess: We have obtained the current list of networks. request.value
|
||||
* is an object whose property names are SSIDs and values are
|
||||
* network objects.
|
||||
* onerror: We were unable to obtain a list of property names.
|
||||
*/
|
||||
DOMRequest getNetworks();
|
||||
|
||||
/**
|
||||
* Returns the list of networks known to the system that will be
|
||||
* automatically connected to if they're in range.
|
||||
* onsuccess: request.value is an object whose property names are
|
||||
* SSIDs and values are network objects.
|
||||
* onerror: We were unable to obtain a list of known networks.
|
||||
*/
|
||||
DOMRequest getKnownNetworks();
|
||||
|
||||
/**
|
||||
* Takes one of the networks returned from getNetworks and tries to
|
||||
* connect to it.
|
||||
* @param network A network object with information about the network,
|
||||
* such as the SSID, key management desired, etc.
|
||||
* onsuccess: We have started attempting to associate with the network.
|
||||
* request.value is true.
|
||||
* onerror: We were unable to select the network. This most likely means a
|
||||
* configuration error.
|
||||
*/
|
||||
DOMRequest associate(MozWifiNetwork network);
|
||||
|
||||
/**
|
||||
* Given a network, removes it from the list of networks that we'll
|
||||
* automatically connect to. In order to re-connect to the network, it is
|
||||
* necessary to call associate on it.
|
||||
* @param network A network object with the SSID of the network to remove.
|
||||
* onsuccess: We have removed this network. If we were previously
|
||||
* connected to it, we have started reconnecting to the next
|
||||
* network in the list.
|
||||
* onerror: We were unable to remove the network.
|
||||
*/
|
||||
DOMRequest forget(MozWifiNetwork network);
|
||||
|
||||
/**
|
||||
* Wi-Fi Protected Setup functionality.
|
||||
* @param detail WPS detail which has 'method' and 'pin' field.
|
||||
* The possible method field values are:
|
||||
* - pbc: The Push Button Configuration.
|
||||
* - pin: The PIN configuration.
|
||||
* - cancel: Request to cancel WPS in progress.
|
||||
* If method field is 'pin', 'pin' field can exist and has
|
||||
* a PIN number.
|
||||
* If method field is 'pin', 'bssid' field can exist and has
|
||||
* a opposite BSSID.
|
||||
* onsuccess: We have successfully started/canceled wps.
|
||||
* onerror: We have failed to start/cancel wps.
|
||||
*/
|
||||
DOMRequest wps(optional WifiWPSInfo detail);
|
||||
|
||||
/**
|
||||
* Turn on/off wifi power saving mode.
|
||||
* @param enabled true or false.
|
||||
* onsuccess: We have successfully turn on/off wifi power saving mode.
|
||||
* onerror: We have failed to turn on/off wifi power saving mode.
|
||||
*/
|
||||
DOMRequest setPowerSavingMode(boolean enabled);
|
||||
|
||||
/**
|
||||
* Given a network, configure using static IP instead of running DHCP
|
||||
* @param network A network object with the SSID of the network to set static ip.
|
||||
* @param info info should have following field:
|
||||
* - enabled True to enable static IP, false to use DHCP
|
||||
* - ipaddr configured static IP address
|
||||
* - proxy configured proxy server address
|
||||
* - maskLength configured mask length
|
||||
* - gateway configured gateway address
|
||||
* - dns1 configured first DNS server address
|
||||
* - dns2 configured seconf DNS server address
|
||||
* onsuccess: We have successfully configure the static ip mode.
|
||||
* onerror: We have failed to configure the static ip mode.
|
||||
*/
|
||||
DOMRequest setStaticIpMode(MozWifiNetwork network, optional IPConfiguration info);
|
||||
|
||||
/**
|
||||
* Given a network, configure http proxy when using wifi.
|
||||
* @param network A network object with the SSID of the network to set http proxy.
|
||||
* @param info info should have following field:
|
||||
* - httpProxyHost ip address of http proxy.
|
||||
* - httpProxyPort port of http proxy, set 0 to use default port 8080.
|
||||
* set info to null to clear http proxy.
|
||||
* onsuccess: We have successfully configure http proxy.
|
||||
* onerror: We have failed to configure http proxy.
|
||||
*/
|
||||
DOMRequest setHttpProxy(MozWifiNetwork network, any info);
|
||||
|
||||
/**
|
||||
* Import a certificate file, only support CA certificate now.
|
||||
* @param certBlob A Blob object containing raw data of certificate to be imported.
|
||||
* Supported format: binary/base64 encoded DER certificates.
|
||||
* (.der/.crt/.cer/.pem)
|
||||
* Cause error if importing certificates already imported.
|
||||
* @param certPassword Password of certificate.
|
||||
* @param certNickname User assigned nickname for imported certificate.
|
||||
* Nickname must be unique, it causes error on redundant nickname.
|
||||
* onsuccess: We have successfully imported certificate. request.result is an
|
||||
* object, containing information of imported CA:
|
||||
* {
|
||||
* nickname: Nickname of imported CA, String.
|
||||
* usage: Supported usage of imported CA, Array of String,
|
||||
* includes: "ServerCert".
|
||||
* }
|
||||
* onerror: We have failed to import certificate.
|
||||
*/
|
||||
DOMRequest importCert(Blob certBlob,
|
||||
DOMString certPassword,
|
||||
DOMString certNickname);
|
||||
|
||||
/**
|
||||
* Get list of imported WIFI certificates.
|
||||
* onsuccess: We have successfully gotten imported certificate list.
|
||||
* request.result is an object using nickname as key, array of usage
|
||||
* string as value.
|
||||
* request.result[USAGE] = [CA_NICKNAME1, CA_NICKNAME2, ...]
|
||||
* USAGE string includes: "ServerCert".
|
||||
* onerror: We have failed to list certificate.
|
||||
*/
|
||||
DOMRequest getImportedCerts();
|
||||
|
||||
/**
|
||||
* Delete an imported certificate.
|
||||
* @param certNickname Nickname of imported to be deleted.
|
||||
* onsuccess: We have successfully deleted certificate.
|
||||
* onerror: We have failed to delete certificate.
|
||||
*/
|
||||
DOMRequest deleteCert(DOMString certNickname);
|
||||
|
||||
/**
|
||||
* Returns whether or not wifi is currently enabled.
|
||||
*/
|
||||
readonly attribute boolean enabled;
|
||||
|
||||
/**
|
||||
* Returns the MAC address of the wifi adapter.
|
||||
*/
|
||||
readonly attribute DOMString macAddress;
|
||||
|
||||
/**
|
||||
* An non-null object containing the following information:
|
||||
* - status ("disconnected", "connecting", "associated", "connected")
|
||||
* - network
|
||||
*
|
||||
* Note that the object returned is read only. Any changes required must
|
||||
* be done by calling other APIs.
|
||||
*/
|
||||
readonly attribute MozWifiConnection connection;
|
||||
|
||||
/**
|
||||
* A connectionInformation object with the same information found in an
|
||||
* MozWifiConnectionInfoEvent (but without the network).
|
||||
* If we are not currently connected to a network, this will be null.
|
||||
*/
|
||||
readonly attribute MozWifiConnectionInfo? connectionInformation;
|
||||
|
||||
/**
|
||||
* Capabilities of Wifi.
|
||||
*/
|
||||
readonly attribute MozWifiCapabilities? capabilities;
|
||||
|
||||
/**
|
||||
* State notification listeners. These all take an
|
||||
* MozWifiStatusChangeEvent with the new status and a network (which may be
|
||||
* null).
|
||||
*
|
||||
* The possible statuses are:
|
||||
* - connecting: Fires when we start the process of connecting to a
|
||||
* network.
|
||||
* - associated: Fires when we have connected to an access point but do
|
||||
* not yet have an IP address.
|
||||
* - connected: Fires once we are fully connected to an access point and
|
||||
* can access the internet.
|
||||
* - disconnected: Fires when we either fail to connect to an access
|
||||
* point (transition: associated -> disconnected) or
|
||||
* when we were connected to a network but have
|
||||
* disconnected for any reason (transition: connected ->
|
||||
* disconnected).
|
||||
*/
|
||||
attribute EventHandler onstatuschange;
|
||||
|
||||
/**
|
||||
* An event listener that is called with information about the signal
|
||||
* strength and link speed every 5 seconds.
|
||||
*/
|
||||
attribute EventHandler onconnectioninfoupdate;
|
||||
|
||||
/**
|
||||
* These two events fire when the wifi system is brought online or taken
|
||||
* offline.
|
||||
*/
|
||||
attribute EventHandler onenabled;
|
||||
attribute EventHandler ondisabled;
|
||||
|
||||
/**
|
||||
* An event listener that is called with information about the number
|
||||
* of wifi stations connected to wifi hotspot every 5 seconds.
|
||||
*/
|
||||
attribute EventHandler onstationinfoupdate;
|
||||
};
|
||||
@@ -1,147 +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/. */
|
||||
|
||||
enum WPSMethod {
|
||||
"pbc",
|
||||
"keypad",
|
||||
"display"
|
||||
};
|
||||
|
||||
dictionary WPSInfo {
|
||||
WPSMethod method;
|
||||
DOMString pin;
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/wifip2pgroupowner;1",
|
||||
Func="Navigator::HasWifiManagerSupport"]
|
||||
interface MozWifiP2pGroupOwner {
|
||||
readonly attribute DOMString groupName;
|
||||
readonly attribute DOMString macAddress;
|
||||
readonly attribute DOMString ipAddress;
|
||||
readonly attribute DOMString passphrase;
|
||||
readonly attribute DOMString ssid;
|
||||
readonly attribute any wpsCapabilities;
|
||||
readonly attribute unsigned long freq;
|
||||
readonly attribute boolean isLocal;
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/wifip2pmanager;1",
|
||||
NavigatorProperty="mozWifiP2pManager",
|
||||
Func="Navigator::HasWifiManagerSupport"]
|
||||
interface MozWifiP2pManager : EventTarget
|
||||
{
|
||||
/**
|
||||
* Enable/Disable wifi direct scan.
|
||||
*
|
||||
* onsuccess: Succeeded in starting/stopping wifi direct scan.
|
||||
* onerror: Failed to start/stop wifi direct scan.
|
||||
*
|
||||
*/
|
||||
DOMRequest setScanEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Connect to a peer with given configuration.
|
||||
*
|
||||
* @param address The peer MAC address we are going to connect.
|
||||
* @param wpsMethod The WPS method we want to use.
|
||||
* @param goIntent Number from 0 ~ 15 to indicate how much we want to be
|
||||
* the group owner.
|
||||
*
|
||||
* onsuccess: Succeeded in issueing a 'connect' request. It doesn't mean we
|
||||
* have connected to the peer.
|
||||
*
|
||||
* onerror: Failed to issue a 'connect' request, probably due to an
|
||||
* invalid peer address, unsupported wps method or any
|
||||
* preliminary error.
|
||||
*
|
||||
**/
|
||||
DOMRequest connect(DOMString address, WPSMethod wpsMethod, optional byte goIntent);
|
||||
|
||||
/**
|
||||
* Disconnect with a peer.
|
||||
*
|
||||
* @param address The mac address of the peer.
|
||||
*
|
||||
* onsuccess: Succeeded to issue a 'disconnect' request. It doesn't mean we
|
||||
* have disconnected with the peer.
|
||||
*
|
||||
* onerror: Failed to issue a 'disconnect' request, probably due to the
|
||||
* invalid peer address or any preliminary error.
|
||||
*
|
||||
*/
|
||||
DOMRequest disconnect(DOMString address);
|
||||
|
||||
/**
|
||||
* Get peer list
|
||||
*
|
||||
* onsuccess: Command success, req.result contains an array of peer objects.
|
||||
* onerror: Command failed.
|
||||
*
|
||||
* Peer object format:
|
||||
* .address MAC address of the peer (string)
|
||||
* .name the peer's device name (string)
|
||||
* .isGroupOwner if the peer is the group owner (boolean)
|
||||
* .wpsCapabilities array of the supported |WPSMethod|
|
||||
* .connectionStatus one of { "disconnected", "connecting", "connected", "disconnecting" }
|
||||
*
|
||||
*/
|
||||
DOMRequest getPeerList();
|
||||
|
||||
/**
|
||||
* Set pairing confirmation result.
|
||||
*
|
||||
* @param accepted Boolean to indicate whether we accepted the request or not.
|
||||
* @param pin The user input pin number if the wps method is keypad.
|
||||
*
|
||||
* onsuccess: Command succeeded.
|
||||
* onerror: Command failed.
|
||||
*
|
||||
*/
|
||||
DOMRequest setPairingConfirmation(boolean accepted, optional DOMString pin);
|
||||
|
||||
/**
|
||||
* Set device name.
|
||||
*
|
||||
* @param devieName The new device name we are going to set.
|
||||
*
|
||||
* onsuccess: Command succeeded.
|
||||
* onerror: Command failed.
|
||||
*
|
||||
*/
|
||||
DOMRequest setDeviceName(DOMString deviceName);
|
||||
|
||||
/**
|
||||
* Returns if Wifi Direct is enabled.
|
||||
*
|
||||
*/
|
||||
readonly attribute boolean enabled;
|
||||
|
||||
/**
|
||||
* The current group owner, null if none.
|
||||
*/
|
||||
readonly attribute MozWifiP2pGroupOwner? groupOwner;
|
||||
|
||||
/**
|
||||
* An event listener that is called whenever the Wifi Direct peer list is
|
||||
* updated. Use getPeerList() to get the up-to-date peer list.
|
||||
*/
|
||||
attribute EventHandler onpeerinfoupdate;
|
||||
|
||||
/**
|
||||
* An event listener that is called whenever Wifi Direct status changed.
|
||||
* The address of the changed peer will be stored in event.peerList.
|
||||
* See MozWifiP2pStatusChangeEvent.webidl.
|
||||
*/
|
||||
attribute EventHandler onstatuschange;
|
||||
|
||||
/**
|
||||
* An event listener that is called whenever Wifi Direct is enabled.
|
||||
*/
|
||||
attribute EventHandler onenabled;
|
||||
|
||||
/**
|
||||
* An event listener that is called whenever Wifi Direct is disabled.
|
||||
*/
|
||||
attribute EventHandler ondisabled;
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[Constructor(DOMString type, optional MozWifiP2pStatusChangeEventInit eventInitDict),
|
||||
Func="Navigator::HasWifiManagerSupport"]
|
||||
interface MozWifiP2pStatusChangeEvent : Event
|
||||
{
|
||||
/**
|
||||
* The mac address of the peer whose status has just changed.
|
||||
*/
|
||||
readonly attribute DOMString peerAddress;
|
||||
};
|
||||
|
||||
dictionary MozWifiP2pStatusChangeEventInit : EventInit
|
||||
{
|
||||
DOMString peerAddress = "";
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[Constructor(DOMString type, optional MozWifiStationInfoEventInit eventInitDict)]
|
||||
interface MozWifiStationInfoEvent : Event
|
||||
{
|
||||
/**
|
||||
* The number of wifi stations connected to wifi hotspot.
|
||||
*/
|
||||
readonly attribute short station;
|
||||
};
|
||||
|
||||
dictionary MozWifiStationInfoEventInit : EventInit
|
||||
{
|
||||
short station = 0;
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[Constructor(DOMString type, optional MozWifiStatusChangeEventInit eventInitDict)]
|
||||
interface MozWifiStatusChangeEvent : Event
|
||||
{
|
||||
/**
|
||||
* Network object with a SSID field describing the network affected by
|
||||
* this change. This might be null.
|
||||
*/
|
||||
readonly attribute any network;
|
||||
|
||||
/**
|
||||
* String describing the current status of the wifi manager. See above for
|
||||
* the possible values.
|
||||
*/
|
||||
readonly attribute DOMString? status;
|
||||
};
|
||||
|
||||
dictionary MozWifiStatusChangeEventInit : EventInit
|
||||
{
|
||||
any network = null;
|
||||
DOMString status = "";
|
||||
};
|
||||
+3
-24
@@ -677,22 +677,9 @@ if CONFIG['MOZ_SECUREELEMENT']:
|
||||
'SecureElementManager.webidl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
WEBIDL_FILES += [
|
||||
'MozNetworkStats.webidl',
|
||||
'MozNetworkStatsAlarm.webidl',
|
||||
'MozNetworkStatsData.webidl',
|
||||
'MozNetworkStatsInterface.webidl',
|
||||
'MozNetworkStatsManager.webidl',
|
||||
'MozSpeakerManager.webidl',
|
||||
'MozWifiCapabilities.webidl',
|
||||
'MozWifiManager.webidl',
|
||||
'MozWifiP2pManager.webidl',
|
||||
]
|
||||
else:
|
||||
WEBIDL_FILES += [
|
||||
'InstallTrigger.webidl',
|
||||
]
|
||||
WEBIDL_FILES += [
|
||||
'InstallTrigger.webidl',
|
||||
]
|
||||
|
||||
GENERATED_EVENTS_WEBIDL_FILES = [
|
||||
'AnimationPlaybackEvent.webidl',
|
||||
@@ -761,14 +748,6 @@ if CONFIG['MOZ_GAMEPAD']:
|
||||
'GamepadEvent.webidl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
GENERATED_EVENTS_WEBIDL_FILES += [
|
||||
'MozWifiConnectionInfoEvent.webidl',
|
||||
'MozWifiP2pStatusChangeEvent.webidl',
|
||||
'MozWifiStationInfoEvent.webidl',
|
||||
'MozWifiStatusChangeEvent.webidl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_BUILD_APP'] in ['xulrunner'] or CONFIG['MOZ_PHOENIX'] or CONFIG['MOZ_SUITE']:
|
||||
WEBIDL_FILES += [
|
||||
'BrowserFeedWriter.webidl',
|
||||
|
||||
@@ -1,543 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* 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/DOMRequestHelper.jsm");
|
||||
|
||||
const DEBUG = false; // set to false to suppress debug messages
|
||||
|
||||
const DOMWIFIMANAGER_CONTRACTID = "@mozilla.org/wifimanager;1";
|
||||
const DOMWIFIMANAGER_CID = Components.ID("{c9b5f09e-25d2-40ca-aef4-c4d13d93c706}");
|
||||
|
||||
function MozWifiNetwork() {
|
||||
}
|
||||
|
||||
MozWifiNetwork.prototype = {
|
||||
|
||||
init: function(aWindow) {
|
||||
this._window = aWindow;
|
||||
},
|
||||
|
||||
__init: function(obj) {
|
||||
for (let key in obj) {
|
||||
this[key] = obj[key];
|
||||
}
|
||||
},
|
||||
|
||||
classID: Components.ID("{c01fd751-43c0-460a-8b64-abf652ec7220}"),
|
||||
contractID: "@mozilla.org/mozwifinetwork;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer])
|
||||
};
|
||||
|
||||
function MozWifiConnection(obj) {
|
||||
this.status = obj.status;
|
||||
this.network = obj.network;
|
||||
}
|
||||
|
||||
MozWifiConnection.prototype = {
|
||||
classID: Components.ID("{23579da4-201b-4319-bd42-9b7f337343ac}"),
|
||||
contractID: "@mozilla.org/mozwificonnection;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
|
||||
};
|
||||
|
||||
function MozWifiConnectionInfo(obj) {
|
||||
this.signalStrength = obj.signalStrength;
|
||||
this.relSignalStrength = obj.relSignalStrength;
|
||||
this.linkSpeed = obj.linkSpeed;
|
||||
this.ipAddress = obj.ipAddress;
|
||||
}
|
||||
|
||||
MozWifiConnectionInfo.prototype = {
|
||||
classID: Components.ID("{83670352-6ed4-4c35-8de9-402296a1959c}"),
|
||||
contractID: "@mozilla.org/mozwificonnectioninfo;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
|
||||
}
|
||||
|
||||
function MozWifiCapabilities(obj) {
|
||||
this.security = obj.security;
|
||||
this.eapMethod = obj.eapMethod;
|
||||
this.eapPhase2 = obj.eapPhase2;
|
||||
this.certificate = obj.certificate;
|
||||
}
|
||||
|
||||
MozWifiCapabilities.prototype = {
|
||||
classID: Components.ID("08c88ece-8092-481b-863b-5515a52e411a"),
|
||||
contractID: "@mozilla.org/mozwificapabilities;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
|
||||
}
|
||||
|
||||
function DOMWifiManager() {
|
||||
this.defineEventHandlerGetterSetter("onstatuschange");
|
||||
this.defineEventHandlerGetterSetter("onconnectioninfoupdate");
|
||||
this.defineEventHandlerGetterSetter("onenabled");
|
||||
this.defineEventHandlerGetterSetter("ondisabled");
|
||||
this.defineEventHandlerGetterSetter("onstationinfoupdate");
|
||||
}
|
||||
|
||||
DOMWifiManager.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
classDescription: "DOMWifiManager",
|
||||
classID: DOMWIFIMANAGER_CID,
|
||||
contractID: DOMWIFIMANAGER_CONTRACTID,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver]),
|
||||
|
||||
// nsIDOMGlobalPropertyInitializer implementation
|
||||
init: function(aWindow) {
|
||||
// Maintain this state for synchronous APIs.
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "disconnected";
|
||||
this._enabled = false;
|
||||
this._lastConnectionInfo = null;
|
||||
this._capabilities = null;
|
||||
this._stationNumber = 0;
|
||||
|
||||
const messages = ["WifiManager:getNetworks:Return:OK", "WifiManager:getNetworks:Return:NO",
|
||||
"WifiManager:getKnownNetworks:Return:OK", "WifiManager:getKnownNetworks:Return:NO",
|
||||
"WifiManager:associate:Return:OK", "WifiManager:associate:Return:NO",
|
||||
"WifiManager:forget:Return:OK", "WifiManager:forget:Return:NO",
|
||||
"WifiManager:wps:Return:OK", "WifiManager:wps:Return:NO",
|
||||
"WifiManager:setPowerSavingMode:Return:OK", "WifiManager:setPowerSavingMode:Return:NO",
|
||||
"WifiManager:setHttpProxy:Return:OK", "WifiManager:setHttpProxy:Return:NO",
|
||||
"WifiManager:setStaticIpMode:Return:OK", "WifiManager:setStaticIpMode:Return:NO",
|
||||
"WifiManager:importCert:Return:OK", "WifiManager:importCert:Return:NO",
|
||||
"WifiManager:getImportedCerts:Return:OK", "WifiManager:getImportedCerts:Return:NO",
|
||||
"WifiManager:deleteCert:Return:OK", "WifiManager:deleteCert:Return:NO",
|
||||
"WifiManager:setWifiEnabled:Return:OK", "WifiManager:setWifiEnabled:Return:NO",
|
||||
"WifiManager:wifiDown", "WifiManager:wifiUp",
|
||||
"WifiManager:onconnecting", "WifiManager:onassociate",
|
||||
"WifiManager:onconnect", "WifiManager:ondisconnect",
|
||||
"WifiManager:onwpstimeout", "WifiManager:onwpsfail",
|
||||
"WifiManager:onwpsoverlap", "WifiManager:connectioninfoupdate",
|
||||
"WifiManager:onauthenticating", "WifiManager:onconnectingfailed",
|
||||
"WifiManager:stationinfoupdate"];
|
||||
this.initDOMRequestHelper(aWindow, messages);
|
||||
this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
|
||||
|
||||
var state = this._mm.sendSyncMessage("WifiManager:getState")[0];
|
||||
if (state) {
|
||||
this._currentNetwork = this._convertWifiNetwork(state.network);
|
||||
this._lastConnectionInfo = this._convertConnectionInfo(state.connectionInfo);
|
||||
this._enabled = state.enabled;
|
||||
this._connectionStatus = state.status;
|
||||
this._macAddress = state.macAddress;
|
||||
this._capabilities = this._convertWifiCapabilities(state.capabilities);
|
||||
} else {
|
||||
this._currentNetwork = null;
|
||||
this._lastConnectionInfo = null;
|
||||
this._enabled = false;
|
||||
this._connectionStatus = "disconnected";
|
||||
this._macAddress = "";
|
||||
}
|
||||
},
|
||||
|
||||
_convertWifiNetworkToJSON: function(aNetwork) {
|
||||
let json = {};
|
||||
|
||||
for (let key in aNetwork) {
|
||||
// In WifiWorker.js there are lots of check using "key in network".
|
||||
// So if the value of any property of WifiNetwork is undefined, do not clone it.
|
||||
if (aNetwork[key] != undefined) {
|
||||
json[key] = aNetwork[key];
|
||||
}
|
||||
}
|
||||
return json;
|
||||
},
|
||||
|
||||
_convertWifiNetwork: function(aNetwork) {
|
||||
let network = aNetwork ? new this._window.MozWifiNetwork(aNetwork) : null;
|
||||
return network;
|
||||
},
|
||||
|
||||
_convertWifiNetworks: function(aNetworks) {
|
||||
let networks = new this._window.Array();
|
||||
for (let i in aNetworks) {
|
||||
networks.push(this._convertWifiNetwork(aNetworks[i]));
|
||||
}
|
||||
return networks;
|
||||
},
|
||||
|
||||
_convertConnection: function(aConn) {
|
||||
let conn = aConn ? new MozWifiConnection(aConn) : null;
|
||||
return conn;
|
||||
},
|
||||
|
||||
_convertConnectionInfo: function(aInfo) {
|
||||
let info = aInfo ? new MozWifiConnectionInfo(aInfo) : null;
|
||||
return info;
|
||||
},
|
||||
|
||||
_convertWifiCapabilities: function(aCapabilities) {
|
||||
let capabilities = aCapabilities ?
|
||||
new MozWifiCapabilities(aCapabilities) : null;
|
||||
return capabilities;
|
||||
},
|
||||
|
||||
_sendMessageForRequest: function(name, data, request) {
|
||||
let id = this.getRequestId(request);
|
||||
this._mm.sendAsyncMessage(name, { data: data, rid: id, mid: this._id });
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let msg = aMessage.json;
|
||||
if (msg.mid && msg.mid != this._id)
|
||||
return;
|
||||
|
||||
let request;
|
||||
if (msg.rid) {
|
||||
request = this.takeRequest(msg.rid);
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "WifiManager:setWifiEnabled:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:setWifiEnabled:Return:NO":
|
||||
Services.DOMRequest.fireError(request, "Unable to enable/disable Wifi");
|
||||
break;
|
||||
|
||||
case "WifiManager:getNetworks:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, this._convertWifiNetworks(msg.data));
|
||||
break;
|
||||
|
||||
case "WifiManager:getNetworks:Return:NO":
|
||||
Services.DOMRequest.fireError(request, "Unable to scan for networks");
|
||||
break;
|
||||
|
||||
case "WifiManager:getKnownNetworks:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, this._convertWifiNetworks(msg.data));
|
||||
break;
|
||||
|
||||
case "WifiManager:getKnownNetworks:Return:NO":
|
||||
Services.DOMRequest.fireError(request, "Unable to get known networks");
|
||||
break;
|
||||
|
||||
case "WifiManager:associate:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, true);
|
||||
break;
|
||||
|
||||
case "WifiManager:associate:Return:NO":
|
||||
Services.DOMRequest.fireError(request, "Unable to add the network");
|
||||
break;
|
||||
|
||||
case "WifiManager:forget:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, true);
|
||||
break;
|
||||
|
||||
case "WifiManager:forget:Return:NO":
|
||||
Services.DOMRequest.fireError(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:wps:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:wps:Return:NO":
|
||||
Services.DOMRequest.fireError(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:setPowerSavingMode:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:setPowerSavingMode:Return:NO":
|
||||
Services.DOMRequest.fireError(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:setHttpProxy:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:setHttpProxy:Return:NO":
|
||||
Services.DOMRequest.fireError(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:setStaticIpMode:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:setStaticIpMode:Return:NO":
|
||||
Services.DOMRequest.fireError(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:importCert:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, Cu.cloneInto(msg.data, this._window));
|
||||
break;
|
||||
|
||||
case "WifiManager:importCert:Return:NO":
|
||||
Services.DOMRequest.fireError(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:getImportedCerts:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, Cu.cloneInto(msg.data, this._window));
|
||||
break;
|
||||
|
||||
case "WifiManager:getImportedCerts:Return:NO":
|
||||
Services.DOMRequest.fireError(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:deleteCert:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:deleteCert:Return:NO":
|
||||
Services.DOMRequest.fireError(request, msg.data);
|
||||
break;
|
||||
|
||||
case "WifiManager:wifiDown":
|
||||
this._enabled = false;
|
||||
this._currentNetwork = null;
|
||||
this._fireEnabledOrDisabled(false);
|
||||
break;
|
||||
|
||||
case "WifiManager:wifiUp":
|
||||
this._enabled = true;
|
||||
this._macAddress = msg.macAddress;
|
||||
this._fireEnabledOrDisabled(true);
|
||||
break;
|
||||
|
||||
case "WifiManager:onconnecting":
|
||||
this._currentNetwork = this._convertWifiNetwork(msg.network);
|
||||
this._connectionStatus = "connecting";
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onassociate":
|
||||
this._currentNetwork = this._convertWifiNetwork(msg.network);
|
||||
this._connectionStatus = "associated";
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onconnect":
|
||||
this._currentNetwork = this._convertWifiNetwork(msg.network);
|
||||
this._connectionStatus = "connected";
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:ondisconnect":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "disconnected";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onwpstimeout":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "wps-timedout";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onwpsfail":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "wps-failed";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onwpsoverlap":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "wps-overlapped";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:connectioninfoupdate":
|
||||
this._lastConnectionInfo = this._convertConnectionInfo(msg);
|
||||
this._fireConnectionInfoUpdate(msg);
|
||||
break;
|
||||
case "WifiManager:onconnectingfailed":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "connectingfailed";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
case "WifiManager:onauthenticating":
|
||||
this._currentNetwork = this._convertWifiNetwork(msg.network);
|
||||
this._connectionStatus = "authenticating";
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
case "WifiManager:stationinfoupdate":
|
||||
this._stationNumber = msg.station;
|
||||
this._fireStationInfoUpdate(msg);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_fireStatusChangeEvent: function StatusChangeEvent(aNetwork) {
|
||||
var event = new this._window.MozWifiStatusChangeEvent("statuschange",
|
||||
{ network: this._convertWifiNetwork(aNetwork),
|
||||
status: this._connectionStatus
|
||||
});
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
},
|
||||
|
||||
_fireConnectionInfoUpdate: function onConnectionInfoUpdate(info) {
|
||||
var evt = new this._window.MozWifiConnectionInfoEvent("connectioninfoupdate",
|
||||
{ network: this._currentNetwork,
|
||||
signalStrength: info.signalStrength,
|
||||
relSignalStrength: info.relSignalStrength,
|
||||
linkSpeed: info.linkSpeed,
|
||||
ipAddress: info.ipAddress,
|
||||
});
|
||||
this.__DOM_IMPL__.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
_fireEnabledOrDisabled: function enabledDisabled(enabled) {
|
||||
var evt = new this._window.Event(enabled ? "enabled" : "disabled");
|
||||
this.__DOM_IMPL__.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
_fireStationInfoUpdate: function onStationInfoUpdate(info) {
|
||||
var evt = new this._window.MozWifiStationInfoEvent("stationinfoupdate",
|
||||
{ station: this._stationNumber}
|
||||
);
|
||||
this.__DOM_IMPL__.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
setWifiEnabled: function setWifiEnabled(enabled) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:setWifiEnabled", enabled, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
getNetworks: function getNetworks() {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:getNetworks", null, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
getKnownNetworks: function getKnownNetworks() {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:getKnownNetworks", null, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
associate: function associate(network) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:associate",
|
||||
this._convertWifiNetworkToJSON(network), request);
|
||||
return request;
|
||||
},
|
||||
|
||||
forget: function forget(network) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:forget",
|
||||
this._convertWifiNetworkToJSON(network), request);
|
||||
return request;
|
||||
},
|
||||
|
||||
wps: function wps(detail) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:wps", detail, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
setPowerSavingMode: function setPowerSavingMode(enabled) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:setPowerSavingMode", enabled, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
setHttpProxy: function setHttpProxy(network, info) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:setHttpProxy",
|
||||
{ network: this._convertWifiNetworkToJSON(network), info:info}, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
setStaticIpMode: function setStaticIpMode(network, info) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:setStaticIpMode",
|
||||
{ network: this._convertWifiNetworkToJSON(network), info: info}, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
importCert: function nsIDOMWifiManager_importCert(certBlob, certPassword, certNickname) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:importCert",
|
||||
{
|
||||
certBlob: certBlob,
|
||||
certPassword: certPassword,
|
||||
certNickname: certNickname
|
||||
}, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
getImportedCerts: function nsIDOMWifiManager_getImportedCerts() {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:getImportedCerts", null, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
deleteCert: function nsIDOMWifiManager_deleteCert(certNickname) {
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:deleteCert",
|
||||
{
|
||||
certNickname: certNickname
|
||||
}, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
},
|
||||
|
||||
get macAddress() {
|
||||
return this._macAddress;
|
||||
},
|
||||
|
||||
get connection() {
|
||||
let _connection = this._convertConnection({ status: this._connectionStatus,
|
||||
network: this._currentNetwork,
|
||||
});
|
||||
return _connection;
|
||||
},
|
||||
|
||||
get connectionInformation() {
|
||||
return this._lastConnectionInfo;
|
||||
},
|
||||
|
||||
get capabilities() {
|
||||
return this._capabilities;
|
||||
},
|
||||
|
||||
defineEventHandlerGetterSetter: function(name) {
|
||||
Object.defineProperty(this, name, {
|
||||
get: function() {
|
||||
return this.__DOM_IMPL__.getEventHandler(name);
|
||||
},
|
||||
set: function(handler) {
|
||||
this.__DOM_IMPL__.setEventHandler(name, handler);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
|
||||
DOMWifiManager, MozWifiNetwork, MozWifiConnection, MozWifiCapabilities,
|
||||
MozWifiConnectionInfo
|
||||
]);
|
||||
|
||||
var debug;
|
||||
if (DEBUG) {
|
||||
debug = function (s) {
|
||||
dump("-*- DOMWifiManager component: " + s + "\n");
|
||||
};
|
||||
} else {
|
||||
debug = function (s) {};
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
# DOMWifiManager.js
|
||||
component {c9b5f09e-25d2-40ca-aef4-c4d13d93c706} DOMWifiManager.js
|
||||
contract @mozilla.org/wifimanager;1 {c9b5f09e-25d2-40ca-aef4-c4d13d93c706}
|
||||
|
||||
component {c01fd751-43c0-460a-8b64-abf652ec7220} DOMWifiManager.js
|
||||
contract @mozilla.org/mozwifinetwork;1 {c01fd751-43c0-460a-8b64-abf652ec7220}
|
||||
|
||||
component {23579da4-201b-4319-bd42-9b7f337343ac} DOMWifiManager.js
|
||||
contract @mozilla.org/mozwificonnection;1 {23579da4-201b-4319-bd42-9b7f337343ac}
|
||||
|
||||
component {83670352-6ed4-4c35-8de9-402296a1959c} DOMWifiManager.js
|
||||
contract @mozilla.org/mozwificonnectioninfo;1 {83670352-6ed4-4c35-8de9-402296a1959c}
|
||||
|
||||
component {ad5c5295-85fb-4460-8e0c-e130d3f029ab} DOMWifiManager.js
|
||||
contract @mozilla.org/mozwificertificateinfo;1 {ad5c5295-85fb-4460-8e0c-e130d3f029ab}
|
||||
|
||||
component {08c88ece-8092-481b-863b-5515a52e411a} DOMWifiManager.js
|
||||
contract @mozilla.org/mozwificapabilities;1 {08c88ece-8092-481b-863b-5515a52e411a}
|
||||
@@ -1,328 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* 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/DOMRequestHelper.jsm");
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
// interface MozWifiP2pGroupOwner implementation.
|
||||
|
||||
function MozWifiP2pGroupOwner(aGo) {
|
||||
this.groupName = aGo.groupName;
|
||||
this.macAddress = aGo.macAddress;
|
||||
this.ipAddress = aGo.ipAddress;
|
||||
this.passphrase = aGo.passphrase;
|
||||
this.ssid = aGo.ssid;
|
||||
this.wpsCapabilities = aGo.wpsCapabilities;
|
||||
this.freq = aGo.freq;
|
||||
this.isLocal = aGo.isLocal;
|
||||
}
|
||||
|
||||
MozWifiP2pGroupOwner.prototype = {
|
||||
classID: Components.ID("{a9b81450-349d-11e3-aa6e-0800200c9a66}"),
|
||||
contractID: "@mozilla.org/wifip2pgroupowner;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
|
||||
};
|
||||
|
||||
// interface MozWifiP2pManager implementation.
|
||||
|
||||
const MOZ_WIFIP2PMANAGER_CONTRACTID = "@mozilla.org/wifip2pmanager;1";
|
||||
const MOZ_WIFIP2PMANAGER_CID = Components.ID("{8d9125a0-3498-11e3-aa6e-0800200c9a66}");
|
||||
|
||||
function MozWifiP2pManager() {
|
||||
this.defineEventHandlerGetterSetter("onstatuschange");
|
||||
this.defineEventHandlerGetterSetter("onpeerinfoupdate");
|
||||
this.defineEventHandlerGetterSetter("onenabled");
|
||||
this.defineEventHandlerGetterSetter("ondisabled");
|
||||
|
||||
this.currentPeer = null;
|
||||
this.enabled = false;
|
||||
this.groupOwner = null;
|
||||
}
|
||||
|
||||
// For smaller, read-only APIs, we expose any property that doesn't begin with
|
||||
// an underscore.
|
||||
function exposeReadOnly(obj) {
|
||||
let exposedProps = {};
|
||||
for (let i in obj) {
|
||||
if (i[0] === "_") {
|
||||
continue;
|
||||
}
|
||||
exposedProps[i] = "r";
|
||||
}
|
||||
|
||||
obj.__exposedProps__ = exposedProps;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function debug(msg) {
|
||||
if (DEBUG) {
|
||||
dump('-------------- MozWifiP2pManager: ' + msg);
|
||||
}
|
||||
}
|
||||
|
||||
MozWifiP2pManager.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
classID: MOZ_WIFIP2PMANAGER_CID,
|
||||
contractID: MOZ_WIFIP2PMANAGER_CONTRACTID,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupports]),
|
||||
|
||||
//
|
||||
// nsIDOMGlobalPropertyInitializer implementation.
|
||||
//
|
||||
|
||||
init: function(aWindow) {
|
||||
const messages = ["WifiP2pManager:setScanEnabled:Return:OK",
|
||||
"WifiP2pManager:setScanEnabled:Return:NO",
|
||||
"WifiP2pManager:getPeerList:Return:OK",
|
||||
"WifiP2pManager:getPeerList:Return:NO",
|
||||
"WifiP2pManager:connect:Return:OK",
|
||||
"WifiP2pManager:connect:Return:NO",
|
||||
"WifiP2pManager:disconnect:Return:OK",
|
||||
"WifiP2pManager:disconnect:Return:NO",
|
||||
"WifiP2pManager:setPairingConfirmation:Return",
|
||||
"WifiP2pManager:setDeviceName:Return:OK",
|
||||
"WifiP2pManager:setDeviceName:Return:NO",
|
||||
|
||||
"WifiP2pManager:p2pDown",
|
||||
"WifiP2pManager:p2pUp",
|
||||
"WifiP2pManager:onconnecting",
|
||||
"WifiP2pManager:onconnected",
|
||||
"WifiP2pManager:ondisconnected",
|
||||
"WifiP2pManager:ongroupnstop",
|
||||
"WifiP2pManager:onconnectingfailed",
|
||||
"WifiP2pManager:onwpstimeout",
|
||||
"WifiP2pManager:onwpsfail",
|
||||
"WifiP2pManager:onpeerinfoupdate",
|
||||
];
|
||||
|
||||
this.initDOMRequestHelper(aWindow, messages);
|
||||
this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
|
||||
|
||||
// Notify the internal a new DOM mananger is created.
|
||||
let state = this._mm.sendSyncMessage("WifiP2pManager:getState")[0];
|
||||
if (state) {
|
||||
debug('State: ' + JSON.stringify(state));
|
||||
this.enabled = state.enabled;
|
||||
this.currentPeer = state.currentPeer;
|
||||
if (state.groupOwner) {
|
||||
this.groupOwner = new MozWifiP2pGroupOwner(state.groupOwner);
|
||||
}
|
||||
} else {
|
||||
debug('Failed to get state');
|
||||
}
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
},
|
||||
|
||||
_sendMessageForRequest: function(name, data, request) {
|
||||
let id = this.getRequestId(request);
|
||||
this._mm.sendAsyncMessage(name, { data: data, rid: id, mid: this._id });
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let msg = aMessage.json;
|
||||
if (msg.mid && msg.mid !== this._id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let request;
|
||||
switch (aMessage.name) {
|
||||
case "WifiP2pManager:setScanEnabled:Return:OK":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:setScanEnabled:Return:NO":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireError(request, "Unable to enable/disable Wifi P2P peer discovery.");
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:getPeerList:Return:OK":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireSuccess(request, Cu.cloneInto(msg.data, this._window));
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:getPeerList:Return:NO":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireError(request, "Unable to disable Wifi P2P peer discovery.");
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:connect:Return:OK":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:connect:Return:NO":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireError(request, "Unable to connect to Wifi P2P peer.");
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:disconnect:Return:OK":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:disconnect:Return:NO":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireError(request, "Unable to disconnect to Wifi P2P peer.");
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:setDeviceName:Return:OK":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:setDeviceName:Return:NO":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireError(request, "Unable to set device name.");
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:p2pDown":
|
||||
this.enabled = false;
|
||||
this.currentPeer = null;
|
||||
this._fireEnabledOrDisabled(false);
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:p2pUp":
|
||||
this.enabled = true;
|
||||
this._fireEnabledOrDisabled(true);
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:onconnecting":
|
||||
debug('onconnecting with peer: ' + JSON.stringify(msg.peer));
|
||||
this.currentPeer = msg.peer;
|
||||
this._fireStatusChangeEvent(msg.peer.address);
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:onconnected":
|
||||
debug('onconnected with peer: ' + JSON.stringify(msg.peer));
|
||||
this.currentPeer = msg.peer;
|
||||
this.groupOwner = new MozWifiP2pGroupOwner(msg.groupOwner);
|
||||
this._fireStatusChangeEvent(msg.peer.address);
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:ondisconnected":
|
||||
debug('ondisconnected with peer: ' + JSON.stringify(msg.peer));
|
||||
this.currentPeer = null;
|
||||
this.groupOwner = null;
|
||||
this._fireStatusChangeEvent(msg.peer.address);
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:onconnectingfailed":
|
||||
this._fireStatusChangeEvent(null);
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:onwpstimeout":
|
||||
this._fireStatusChangeEvent(null);
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:onwpsfail":
|
||||
this._fireStatusChangeEvent(null);
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:onpeerinfoupdate":
|
||||
this._firePeerInfoUpdateEvent();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_firePeerInfoUpdateEvent: function PeerInfoUpdate() {
|
||||
let evt = new this._window.Event("peerinfoupdate");
|
||||
this.__DOM_IMPL__.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
_fireStatusChangeEvent: function WifiP2pStatusChange(peerAddress) {
|
||||
let evt = new this._window.MozWifiP2pStatusChangeEvent("statuschange",
|
||||
{ peerAddress: peerAddress });
|
||||
this.__DOM_IMPL__.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
_fireEnabledOrDisabled: function enabledDisabled(enabled) {
|
||||
let evt = new this._window.Event(enabled ? "enabled" : "disabled");
|
||||
this.__DOM_IMPL__.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
//
|
||||
// WifiP2pManager.webidl implementation.
|
||||
//
|
||||
|
||||
enableScan: function () {
|
||||
let request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiP2pManager:enableScan", null, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
disableScan: function () {
|
||||
let request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiP2pManager:disableScan", null, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
setScanEnabled: function(enabled) {
|
||||
let request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiP2pManager:setScanEnabled", enabled, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
connect: function (address, wpsMethod, goIntent) {
|
||||
let request = this.createRequest();
|
||||
let connectionInfo = { address: address, wpsMethod: wpsMethod, goIntent: goIntent };
|
||||
this._sendMessageForRequest("WifiP2pManager:connect", connectionInfo, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
disconnect: function (address) {
|
||||
let request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiP2pManager:disconnect", address, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
getPeerList: function () {
|
||||
let request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiP2pManager:getPeerList", null, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
setPairingConfirmation: function (accepted, pin) {
|
||||
let request = this.createRequest();
|
||||
let result = { accepted: accepted, pin: pin };
|
||||
this._sendMessageForRequest("WifiP2pManager:setPairingConfirmation", result, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
setDeviceName: function(newDeviceName) {
|
||||
let request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiP2pManager:setDeviceName", newDeviceName, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
// Helpers.
|
||||
defineEventHandlerGetterSetter: function(event) {
|
||||
Object.defineProperty(this, event, {
|
||||
get: function() {
|
||||
return this.__DOM_IMPL__.getEventHandler(event);
|
||||
},
|
||||
|
||||
set: function(handler) {
|
||||
this.__DOM_IMPL__.setEventHandler(event, handler);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozWifiP2pManager, MozWifiP2pGroupOwner]);
|
||||
@@ -1,6 +0,0 @@
|
||||
# DOMWifiP2pManager.js
|
||||
component {8d9125a0-3498-11e3-aa6e-0800200c9a66} DOMWifiP2pManager.js
|
||||
contract @mozilla.org/wifip2pmanager;1 {8d9125a0-3498-11e3-aa6e-0800200c9a66}
|
||||
|
||||
component {a9b81450-349d-11e3-aa6e-0800200c9a66} DOMWifiP2pManager.js
|
||||
contract @mozilla.org/wifip2pgroupowner;1 {a9b81450-349d-11e3-aa6e-0800200c9a66}
|
||||
@@ -1,205 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* 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");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["StateMachine"];
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
this.StateMachine = function(aDebugTag) {
|
||||
function debug(aMsg) {
|
||||
dump('-------------- StateMachine:' + aDebugTag + ': ' + aMsg);
|
||||
}
|
||||
|
||||
var sm = {};
|
||||
|
||||
var _initialState;
|
||||
var _curState;
|
||||
var _prevState;
|
||||
var _paused;
|
||||
var _eventQueue = [];
|
||||
var _deferredEventQueue = [];
|
||||
var _defaultEventHandler;
|
||||
|
||||
// Public interfaces.
|
||||
|
||||
sm.setDefaultEventHandler = function(aDefaultEventHandler) {
|
||||
_defaultEventHandler = aDefaultEventHandler;
|
||||
};
|
||||
|
||||
sm.start = function(aInitialState) {
|
||||
_initialState = aInitialState;
|
||||
sm.gotoState(_initialState);
|
||||
};
|
||||
|
||||
sm.sendEvent = function (aEvent) {
|
||||
if (!_initialState) {
|
||||
if (DEBUG) {
|
||||
debug('StateMachine is not running. Call StateMachine.start() first.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
_eventQueue.push(aEvent);
|
||||
asyncCall(handleFirstEvent);
|
||||
};
|
||||
|
||||
sm.getPreviousState = function() {
|
||||
return _prevState;
|
||||
};
|
||||
|
||||
sm.getCurrentState = function() {
|
||||
return _curState;
|
||||
};
|
||||
|
||||
// State object maker.
|
||||
// @param aName string for this state's name.
|
||||
// @param aDelegate object:
|
||||
// .handleEvent: required.
|
||||
// .enter: called before entering this state (optional).
|
||||
// .exit: called before exiting this state (optional).
|
||||
sm.makeState = function (aName, aDelegate) {
|
||||
if (!aDelegate.handleEvent) {
|
||||
throw "handleEvent is a required delegate function.";
|
||||
}
|
||||
var nop = function() {};
|
||||
return {
|
||||
name: aName,
|
||||
enter: (aDelegate.enter || nop),
|
||||
exit: (aDelegate.exit || nop),
|
||||
handleEvent: aDelegate.handleEvent
|
||||
};
|
||||
};
|
||||
|
||||
sm.deferEvent = function (aEvent) {
|
||||
// The definition of a 'deferred event' is:
|
||||
// We are not able to handle this event now but after receiving
|
||||
// certain event or entering a new state, we might be able to handle
|
||||
// it. For example, we couldn't handle CONNECT_EVENT in the
|
||||
// diconnecting state. But once we finish doing "disconnecting", we
|
||||
// could then handle CONNECT_EVENT!
|
||||
//
|
||||
// So, the deferred event may be handled in the following cases:
|
||||
// 1. Once we entered a new state.
|
||||
// 2. Once we handled a regular event.
|
||||
if (DEBUG) {
|
||||
debug('Deferring event: ' + JSON.stringify(aEvent));
|
||||
}
|
||||
_deferredEventQueue.push(aEvent);
|
||||
};
|
||||
|
||||
// Goto the new state. If the current state is null, the exit
|
||||
// function won't be called.
|
||||
sm.gotoState = function (aNewState) {
|
||||
if (_curState) {
|
||||
if (DEBUG) {
|
||||
debug("exiting state: " + _curState.name);
|
||||
}
|
||||
_curState.exit();
|
||||
}
|
||||
|
||||
_prevState = _curState;
|
||||
_curState = aNewState;
|
||||
|
||||
if (DEBUG) {
|
||||
debug("entering state: " + _curState.name);
|
||||
}
|
||||
_curState.enter();
|
||||
|
||||
// We are in the new state now. We got a chance to handle the
|
||||
// deferred events.
|
||||
handleDeferredEvents();
|
||||
|
||||
sm.resume();
|
||||
};
|
||||
|
||||
// No incoming event will be handled after you call pause().
|
||||
// (But they will be queued.)
|
||||
sm.pause = function() {
|
||||
_paused = true;
|
||||
};
|
||||
|
||||
// Continue to handle incoming events.
|
||||
sm.resume = function() {
|
||||
_paused = false;
|
||||
asyncCall(handleFirstEvent);
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Private stuff
|
||||
//----------------------------------------------------------
|
||||
|
||||
function asyncCall(f) {
|
||||
Services.tm.currentThread.dispatch(f, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
function handleFirstEvent() {
|
||||
var hadDeferredEvents;
|
||||
|
||||
if (0 === _eventQueue.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_paused) {
|
||||
return; // The state machine is paused now.
|
||||
}
|
||||
|
||||
hadDeferredEvents = _deferredEventQueue.length > 0;
|
||||
|
||||
handleOneEvent(_eventQueue.shift()); // The handler may defer this event.
|
||||
|
||||
// We've handled one event. If we had deferred events before, now is
|
||||
// a good chance to handle them.
|
||||
if (hadDeferredEvents) {
|
||||
handleDeferredEvents();
|
||||
}
|
||||
|
||||
// Continue to handle the next regular event.
|
||||
handleFirstEvent();
|
||||
}
|
||||
|
||||
function handleDeferredEvents() {
|
||||
if (_deferredEventQueue.length && DEBUG) {
|
||||
debug('Handle deferred events: ' + _deferredEventQueue.length);
|
||||
}
|
||||
for (let i = 0; i < _deferredEventQueue.length; i++) {
|
||||
handleOneEvent(_deferredEventQueue.shift());
|
||||
}
|
||||
}
|
||||
|
||||
function handleOneEvent(aEvent)
|
||||
{
|
||||
if (DEBUG) {
|
||||
debug('Handling event: ' + JSON.stringify(aEvent));
|
||||
}
|
||||
|
||||
var handled = _curState.handleEvent(aEvent);
|
||||
|
||||
if (undefined === handled) {
|
||||
throw "handleEvent returns undefined: " + _curState.name;
|
||||
}
|
||||
if (!handled) {
|
||||
// Event is not handled in the current state. Try handleEventCommon().
|
||||
handled = (_defaultEventHandler ? _defaultEventHandler(aEvent) : handled);
|
||||
}
|
||||
if (undefined === handled) {
|
||||
throw "handleEventCommon returns undefined: " + _curState.name;
|
||||
}
|
||||
if (!handled) {
|
||||
if (DEBUG) {
|
||||
debug('!!!!!!!!! FIXME !!!!!!!!! Event not handled: ' + JSON.stringify(aEvent));
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
return sm;
|
||||
};
|
||||
@@ -1,536 +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 "WifiCertService.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "cert.h"
|
||||
#include "certdb.h"
|
||||
#include "CryptoTask.h"
|
||||
#include "nsIDOMBlob.h"
|
||||
#include "nsIWifiService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
#define NS_WIFICERTSERVICE_CID \
|
||||
{ 0x83585afd, 0x0e11, 0x43aa, {0x83, 0x46, 0xf3, 0x4d, 0x97, 0x5e, 0x46, 0x77} }
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// The singleton Wifi Cert service, to be used on the main thread.
|
||||
StaticRefPtr<WifiCertService> gWifiCertService;
|
||||
|
||||
class ImportCertTask final: public CryptoTask
|
||||
{
|
||||
public:
|
||||
ImportCertTask(int32_t aId, Blob* aCertBlob,
|
||||
const nsAString& aCertPassword,
|
||||
const nsAString& aCertNickname)
|
||||
: mBlob(aCertBlob)
|
||||
, mPassword(aCertPassword)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mResult.mId = aId;
|
||||
mResult.mStatus = 0;
|
||||
mResult.mUsageFlag = 0;
|
||||
mResult.mNickname = aCertNickname;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void ReleaseNSSResources() {}
|
||||
|
||||
virtual nsresult CalculateResult() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
// read data from blob.
|
||||
nsCString blobBuf;
|
||||
nsresult rv = ReadBlob(blobBuf);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
char* buf;
|
||||
uint32_t size = blobBuf.GetMutableData(&buf);
|
||||
if (size == 0) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Try import as DER format first.
|
||||
rv = ImportDERBlob(buf, size);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Try import as PKCS#12 format.
|
||||
return ImportPKCS12Blob(buf, size, mPassword);
|
||||
}
|
||||
|
||||
virtual void CallCallback(nsresult rv)
|
||||
{
|
||||
if (NS_FAILED(rv)) {
|
||||
mResult.mStatus = -1;
|
||||
}
|
||||
gWifiCertService->DispatchResult(mResult);
|
||||
}
|
||||
|
||||
nsresult ImportDERBlob(char* buf, uint32_t size)
|
||||
{
|
||||
// Create certificate object.
|
||||
ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(buf, size));
|
||||
if (!cert) {
|
||||
return MapSECStatus(SECFailure);
|
||||
}
|
||||
|
||||
// Import certificate.
|
||||
return ImportCert(cert);
|
||||
}
|
||||
|
||||
static SECItem*
|
||||
HandleNicknameCollision(SECItem* aOldNickname, PRBool* aCancel, void* aWincx)
|
||||
{
|
||||
const char* dummyName = "Imported User Cert";
|
||||
const size_t dummyNameLen = strlen(dummyName);
|
||||
SECItem* newNick = ::SECITEM_AllocItem(nullptr, nullptr, dummyNameLen + 1);
|
||||
if (!newNick) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
newNick->type = siAsciiString;
|
||||
// Dummy name, will be renamed later.
|
||||
memcpy(newNick->data, dummyName, dummyNameLen + 1);
|
||||
newNick->len = dummyNameLen;
|
||||
|
||||
return newNick;
|
||||
}
|
||||
|
||||
static SECStatus
|
||||
HandleNicknameUpdate(const CERTCertificate *aCert,
|
||||
const SECItem *default_nickname,
|
||||
SECItem **new_nickname,
|
||||
void *arg)
|
||||
{
|
||||
WifiCertServiceResultOptions *result = (WifiCertServiceResultOptions *)arg;
|
||||
|
||||
nsCString userNickname;
|
||||
CopyUTF16toUTF8(result->mNickname, userNickname);
|
||||
|
||||
nsCString fullNickname;
|
||||
if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) {
|
||||
// Accept self-signed SSL CA as server certificate.
|
||||
fullNickname.AssignLiteral("WIFI_SERVERCERT_");
|
||||
fullNickname += userNickname;
|
||||
result->mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER;
|
||||
} else if (aCert->nsCertType & NS_CERT_TYPE_SSL_CLIENT) {
|
||||
// User Certificate
|
||||
fullNickname.AssignLiteral("WIFI_USERCERT_");
|
||||
fullNickname += userNickname;
|
||||
result->mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER;
|
||||
}
|
||||
char* nickname;
|
||||
uint32_t length = fullNickname.GetMutableData(&nickname);
|
||||
|
||||
SECItem* newNick = ::SECITEM_AllocItem(nullptr, nullptr, length + 1);
|
||||
if (!newNick) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
newNick->type = siAsciiString;
|
||||
memcpy(newNick->data, nickname, length + 1);
|
||||
newNick->len = length;
|
||||
|
||||
*new_nickname = newNick;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
nsresult ImportPKCS12Blob(char* buf, uint32_t size, const nsAString& aPassword)
|
||||
{
|
||||
nsString password(aPassword);
|
||||
|
||||
// password is null-terminated wide-char string.
|
||||
// passwordItem is required to be big-endian form of password, stored in char
|
||||
// array, including the null-termination.
|
||||
uint32_t length = password.Length() + 1;
|
||||
ScopedSECItem passwordItem(
|
||||
::SECITEM_AllocItem(nullptr, nullptr, length * sizeof(nsString::char_type)));
|
||||
|
||||
if (!passwordItem) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mozilla::NativeEndian::copyAndSwapToBigEndian(passwordItem->data,
|
||||
password.BeginReading(),
|
||||
length);
|
||||
// Create a decoder.
|
||||
ScopedSEC_PKCS12DecoderContext p12dcx(SEC_PKCS12DecoderStart(
|
||||
passwordItem, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr));
|
||||
|
||||
if (!p12dcx) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Assign data to decorder.
|
||||
SECStatus srv = SEC_PKCS12DecoderUpdate(p12dcx,
|
||||
reinterpret_cast<unsigned char*>(buf),
|
||||
size);
|
||||
if (srv != SECSuccess) {
|
||||
return MapSECStatus(srv);
|
||||
}
|
||||
|
||||
// Verify certificates.
|
||||
srv = SEC_PKCS12DecoderVerify(p12dcx);
|
||||
if (srv != SECSuccess) {
|
||||
return MapSECStatus(srv);
|
||||
}
|
||||
|
||||
// Set certificate nickname and usage flag.
|
||||
srv = SEC_PKCS12DecoderRenameCertNicknames(p12dcx, HandleNicknameUpdate,
|
||||
&mResult);
|
||||
|
||||
// Validate certificates.
|
||||
srv = SEC_PKCS12DecoderValidateBags(p12dcx, HandleNicknameCollision);
|
||||
if (srv != SECSuccess) {
|
||||
return MapSECStatus(srv);
|
||||
}
|
||||
|
||||
// Initialize slot.
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) {
|
||||
srv = PK11_InitPin(slot, "", "");
|
||||
if (srv != SECSuccess) {
|
||||
return MapSECStatus(srv);
|
||||
}
|
||||
}
|
||||
|
||||
// Import cert and key.
|
||||
srv = SEC_PKCS12DecoderImportBags(p12dcx);
|
||||
if (srv != SECSuccess) {
|
||||
return MapSECStatus(srv);
|
||||
}
|
||||
|
||||
// User certificate must be imported from PKCS#12.
|
||||
return (mResult.mUsageFlag & nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER)
|
||||
? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult ReadBlob(/*out*/ nsCString& aBuf)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(mBlob);
|
||||
|
||||
static const uint64_t MAX_FILE_SIZE = 16384;
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t size = mBlob->GetSize(rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
if (size > MAX_FILE_SIZE) {
|
||||
return NS_ERROR_FILE_TOO_BIG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
mBlob->GetInternalStream(getter_AddRefs(inputStream), rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
rv = NS_ReadInputStreamToString(inputStream, aBuf, (uint32_t)size);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ImportCert(CERTCertificate* aCert)
|
||||
{
|
||||
nsCString userNickname, fullNickname;
|
||||
|
||||
CopyUTF16toUTF8(mResult.mNickname, userNickname);
|
||||
// Determine certificate nickname by adding prefix according to its type.
|
||||
if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) {
|
||||
// Accept self-signed SSL CA as server certificate.
|
||||
fullNickname.AssignLiteral("WIFI_SERVERCERT_");
|
||||
fullNickname += userNickname;
|
||||
mResult.mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER;
|
||||
} else if (aCert->nsCertType & NS_CERT_TYPE_SSL_CLIENT) {
|
||||
// User Certificate
|
||||
fullNickname.AssignLiteral("WIFI_USERCERT_");
|
||||
fullNickname += userNickname;
|
||||
mResult.mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER;
|
||||
} else {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
char* nickname;
|
||||
uint32_t length;
|
||||
length = fullNickname.GetMutableData(&nickname);
|
||||
if (length == 0) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Import certificate, duplicated nickname will cause error.
|
||||
SECStatus srv = CERT_AddTempCertToPerm(aCert, nickname, nullptr);
|
||||
if (srv != SECSuccess) {
|
||||
return MapSECStatus(srv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<Blob> mBlob;
|
||||
nsString mPassword;
|
||||
WifiCertServiceResultOptions mResult;
|
||||
};
|
||||
|
||||
class DeleteCertTask final: public CryptoTask
|
||||
{
|
||||
public:
|
||||
DeleteCertTask(int32_t aId, const nsAString& aCertNickname)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mResult.mId = aId;
|
||||
mResult.mStatus = 0;
|
||||
mResult.mUsageFlag = 0;
|
||||
mResult.mNickname = aCertNickname;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void ReleaseNSSResources() {}
|
||||
|
||||
virtual nsresult CalculateResult() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
nsCString userNickname;
|
||||
CopyUTF16toUTF8(mResult.mNickname, userNickname);
|
||||
|
||||
// Delete server certificate.
|
||||
nsCString serverCertName("WIFI_SERVERCERT_", 16);
|
||||
serverCertName += userNickname;
|
||||
nsresult rv = deleteCert(serverCertName);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Delete user certificate and private key.
|
||||
nsCString userCertName("WIFI_USERCERT_", 14);
|
||||
userCertName += userNickname;
|
||||
rv = deleteCert(userCertName);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult deleteCert(const nsCString &aCertNickname)
|
||||
{
|
||||
ScopedCERTCertificate cert(
|
||||
CERT_FindCertByNickname(CERT_GetDefaultCertDB(), aCertNickname.get())
|
||||
);
|
||||
// Because we delete certificates in blind, so it's acceptable to delete
|
||||
// a non-exist certificate.
|
||||
if (!cert) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ScopedPK11SlotInfo slot(
|
||||
PK11_KeyForCertExists(cert, nullptr, nullptr)
|
||||
);
|
||||
|
||||
SECStatus srv;
|
||||
if (slot) {
|
||||
// Delete private key along with certificate.
|
||||
srv = PK11_DeleteTokenCertAndKey(cert, nullptr);
|
||||
} else {
|
||||
srv = SEC_DeletePermCertificate(cert);
|
||||
}
|
||||
|
||||
if (srv != SECSuccess) {
|
||||
return MapSECStatus(srv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual void CallCallback(nsresult rv)
|
||||
{
|
||||
if (NS_FAILED(rv)) {
|
||||
mResult.mStatus = -1;
|
||||
}
|
||||
gWifiCertService->DispatchResult(mResult);
|
||||
}
|
||||
|
||||
WifiCertServiceResultOptions mResult;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(WifiCertService, nsIWifiCertService)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiCertService::Start(nsIWifiEventListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(aListener);
|
||||
|
||||
mListener = aListener;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiCertService::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mListener = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WifiCertService::DispatchResult(const WifiCertServiceResultOptions& aOptions)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mozilla::AutoSafeJSContext cx;
|
||||
JS::RootedValue val(cx);
|
||||
nsCString dummyInterface;
|
||||
|
||||
if (!ToJSValue(cx, aOptions, &val)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Certll the listener with a JS value.
|
||||
mListener->OnCommand(val, dummyInterface);
|
||||
}
|
||||
|
||||
WifiCertService::WifiCertService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!gWifiCertService);
|
||||
}
|
||||
|
||||
WifiCertService::~WifiCertService()
|
||||
{
|
||||
MOZ_ASSERT(!gWifiCertService);
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
already_AddRefed<WifiCertService>
|
||||
WifiCertService::FactoryCreate()
|
||||
{
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!gWifiCertService) {
|
||||
gWifiCertService = new WifiCertService();
|
||||
ClearOnShutdown(&gWifiCertService);
|
||||
}
|
||||
|
||||
RefPtr<WifiCertService> service = gWifiCertService.get();
|
||||
return service.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiCertService::ImportCert(int32_t aId, nsIDOMBlob* aCertBlob,
|
||||
const nsAString& aCertPassword,
|
||||
const nsAString& aCertNickname)
|
||||
{
|
||||
RefPtr<Blob> blob = static_cast<Blob*>(aCertBlob);
|
||||
RefPtr<CryptoTask> task = new ImportCertTask(aId, blob, aCertPassword,
|
||||
aCertNickname);
|
||||
return task->Dispatch("WifiImportCert");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiCertService::DeleteCert(int32_t aId, const nsAString& aCertNickname)
|
||||
{
|
||||
RefPtr<CryptoTask> task = new DeleteCertTask(aId, aCertNickname);
|
||||
return task->Dispatch("WifiDeleteCert");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiCertService::HasPrivateKey(const nsAString& aCertNickname, bool *aHasKey)
|
||||
{
|
||||
*aHasKey = false;
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCString certNickname;
|
||||
CopyUTF16toUTF8(aCertNickname, certNickname);
|
||||
|
||||
ScopedCERTCertificate cert(
|
||||
CERT_FindCertByNickname(CERT_GetDefaultCertDB(), certNickname.get())
|
||||
);
|
||||
if (!cert) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ScopedPK11SlotInfo slot(
|
||||
PK11_KeyForCertExists(cert, nullptr, nullptr)
|
||||
);
|
||||
if (slot) {
|
||||
*aHasKey = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiCertService,
|
||||
WifiCertService::FactoryCreate)
|
||||
|
||||
NS_DEFINE_NAMED_CID(NS_WIFICERTSERVICE_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kWifiCertServiceCIDs[] = {
|
||||
{ &kNS_WIFICERTSERVICE_CID, false, nullptr, WifiCertServiceConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kWifiCertServiceContracts[] = {
|
||||
{ "@mozilla.org/wifi/certservice;1", &kNS_WIFICERTSERVICE_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module kWifiCertServiceModule = {
|
||||
mozilla::Module::kVersion,
|
||||
kWifiCertServiceCIDs,
|
||||
kWifiCertServiceContracts,
|
||||
nullptr
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
NSMODULE_DEFN(WifiCertServiceModule) = &kWifiCertServiceModule;
|
||||
@@ -1,40 +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 WifiCertService_h
|
||||
#define WifiCertService_h
|
||||
|
||||
#include "nsIWifiCertService.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "nsThread.h"
|
||||
#include "mozilla/dom/WifiOptionsBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WifiCertService final : public nsIWifiCertService,
|
||||
public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIWIFICERTSERVICE
|
||||
|
||||
static already_AddRefed<WifiCertService>
|
||||
FactoryCreate();
|
||||
void DispatchResult(const mozilla::dom::WifiCertServiceResultOptions& aOptions);
|
||||
|
||||
private:
|
||||
WifiCertService();
|
||||
~WifiCertService();
|
||||
virtual void virtualDestroyNSSReference() {};
|
||||
nsCOMPtr<nsIWifiEventListener> mListener;
|
||||
};
|
||||
|
||||
} // namespce dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // WifiCertService_h
|
||||
@@ -1,594 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["WifiCommand"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/systemlibs.js");
|
||||
|
||||
const SUPP_PROP = "init.svc.wpa_supplicant";
|
||||
const WPA_SUPPLICANT = "wpa_supplicant";
|
||||
const DEBUG = false;
|
||||
|
||||
this.WifiCommand = function(aControlMessage, aInterface, aSdkVersion) {
|
||||
function debug(msg) {
|
||||
if (DEBUG) {
|
||||
dump('-------------- WifiCommand: ' + msg);
|
||||
}
|
||||
}
|
||||
|
||||
var command = {};
|
||||
|
||||
//-------------------------------------------------
|
||||
// Utilities.
|
||||
//-------------------------------------------------
|
||||
command.getSdkVersion = function() {
|
||||
return aSdkVersion;
|
||||
};
|
||||
|
||||
//-------------------------------------------------
|
||||
// General commands.
|
||||
//-------------------------------------------------
|
||||
|
||||
command.loadDriver = function (callback) {
|
||||
voidControlMessage("load_driver", function(status) {
|
||||
callback(status);
|
||||
});
|
||||
};
|
||||
|
||||
command.unloadDriver = function (callback) {
|
||||
voidControlMessage("unload_driver", function(status) {
|
||||
callback(status);
|
||||
});
|
||||
};
|
||||
|
||||
command.startSupplicant = function (callback) {
|
||||
voidControlMessage("start_supplicant", callback);
|
||||
};
|
||||
|
||||
command.killSupplicant = function (callback) {
|
||||
// It is interesting to note that this function does exactly what
|
||||
// wifi_stop_supplicant does. Unforunately, on the Galaxy S2, Samsung
|
||||
// changed that function in a way that means that it doesn't recognize
|
||||
// wpa_supplicant as already running. Therefore, we have to roll our own
|
||||
// version here.
|
||||
stopProcess(SUPP_PROP, WPA_SUPPLICANT, callback);
|
||||
};
|
||||
|
||||
command.terminateSupplicant = function (callback) {
|
||||
doBooleanCommand("TERMINATE", "OK", callback);
|
||||
};
|
||||
|
||||
command.stopSupplicant = function (callback) {
|
||||
voidControlMessage("stop_supplicant", callback);
|
||||
};
|
||||
|
||||
command.listNetworks = function (callback) {
|
||||
doStringCommand("LIST_NETWORKS", callback);
|
||||
};
|
||||
|
||||
command.addNetwork = function (callback) {
|
||||
doIntCommand("ADD_NETWORK", callback);
|
||||
};
|
||||
|
||||
command.setNetworkVariable = function (netId, name, value, callback) {
|
||||
doBooleanCommand("SET_NETWORK " + netId + " " + name + " " +
|
||||
value, "OK", callback);
|
||||
};
|
||||
|
||||
command.getNetworkVariable = function (netId, name, callback) {
|
||||
doStringCommand("GET_NETWORK " + netId + " " + name, callback);
|
||||
};
|
||||
|
||||
command.removeNetwork = function (netId, callback) {
|
||||
doBooleanCommand("REMOVE_NETWORK " + netId, "OK", callback);
|
||||
};
|
||||
|
||||
command.enableNetwork = function (netId, disableOthers, callback) {
|
||||
doBooleanCommand((disableOthers ? "SELECT_NETWORK " : "ENABLE_NETWORK ") +
|
||||
netId, "OK", callback);
|
||||
};
|
||||
|
||||
command.disableNetwork = function (netId, callback) {
|
||||
doBooleanCommand("DISABLE_NETWORK " + netId, "OK", callback);
|
||||
};
|
||||
|
||||
command.status = function (callback) {
|
||||
doStringCommand("STATUS", callback);
|
||||
};
|
||||
|
||||
command.ping = function (callback) {
|
||||
doBooleanCommand("PING", "PONG", callback);
|
||||
};
|
||||
|
||||
command.scanResults = function (callback) {
|
||||
doStringCommand("SCAN_RESULTS", callback);
|
||||
};
|
||||
|
||||
command.disconnect = function (callback) {
|
||||
doBooleanCommand("DISCONNECT", "OK", callback);
|
||||
};
|
||||
|
||||
command.reconnect = function (callback) {
|
||||
doBooleanCommand("RECONNECT", "OK", callback);
|
||||
};
|
||||
|
||||
command.reassociate = function (callback) {
|
||||
doBooleanCommand("REASSOCIATE", "OK", callback);
|
||||
};
|
||||
|
||||
command.setBackgroundScan = function (enable, callback) {
|
||||
doBooleanCommand("SET pno " + (enable ? "1" : "0"),
|
||||
"OK",
|
||||
function(ok) {
|
||||
callback(true, ok);
|
||||
});
|
||||
};
|
||||
|
||||
command.doSetScanMode = function (setActive, callback) {
|
||||
doBooleanCommand(setActive ?
|
||||
"DRIVER SCAN-ACTIVE" :
|
||||
"DRIVER SCAN-PASSIVE", "OK", callback);
|
||||
};
|
||||
|
||||
command.scan = function (callback) {
|
||||
doBooleanCommand("SCAN", "OK", callback);
|
||||
};
|
||||
|
||||
command.setLogLevel = function (level, callback) {
|
||||
doBooleanCommand("LOG_LEVEL " + level, "OK", callback);
|
||||
};
|
||||
|
||||
command.getLogLevel = function (callback) {
|
||||
doStringCommand("LOG_LEVEL", callback);
|
||||
};
|
||||
|
||||
command.wpsPbc = function (callback, iface) {
|
||||
let cmd = 'WPS_PBC';
|
||||
|
||||
// If the network interface is specified and we are based on JB,
|
||||
// append the argument 'interface=[iface]' to the supplicant command.
|
||||
//
|
||||
// Note: The argument "interface" is only required for wifi p2p on JB.
|
||||
// For other cases, the argument is useless and even leads error.
|
||||
// Check the evil work here:
|
||||
// http://androidxref.com/4.2.2_r1/xref/external/wpa_supplicant_8/wpa_supplicant/ctrl_iface_unix.c#172
|
||||
//
|
||||
if (iface && isJellybean()) {
|
||||
cmd += (' inferface=' + iface);
|
||||
}
|
||||
|
||||
doBooleanCommand(cmd, "OK", callback);
|
||||
};
|
||||
|
||||
command.wpsPin = function (detail, callback) {
|
||||
let cmd = 'WPS_PIN ';
|
||||
|
||||
// See the comment above in wpsPbc().
|
||||
if (detail.iface && isJellybean()) {
|
||||
cmd += ('inferface=' + iface + ' ');
|
||||
}
|
||||
|
||||
cmd += (detail.bssid === undefined ? "any" : detail.bssid);
|
||||
cmd += (detail.pin === undefined ? "" : (" " + detail.pin));
|
||||
|
||||
doStringCommand(cmd, callback);
|
||||
};
|
||||
|
||||
command.wpsCancel = function (callback) {
|
||||
doBooleanCommand("WPS_CANCEL", "OK", callback);
|
||||
};
|
||||
|
||||
command.startDriver = function (callback) {
|
||||
doBooleanCommand("DRIVER START", "OK");
|
||||
};
|
||||
|
||||
command.stopDriver = function (callback) {
|
||||
doBooleanCommand("DRIVER STOP", "OK");
|
||||
};
|
||||
|
||||
command.startPacketFiltering = function (callback) {
|
||||
var commandChain = ["DRIVER RXFILTER-ADD 0",
|
||||
"DRIVER RXFILTER-ADD 1",
|
||||
"DRIVER RXFILTER-ADD 3",
|
||||
"DRIVER RXFILTER-START"];
|
||||
|
||||
doBooleanCommandChain(commandChain, callback);
|
||||
};
|
||||
|
||||
command.stopPacketFiltering = function (callback) {
|
||||
var commandChain = ["DRIVER RXFILTER-STOP",
|
||||
"DRIVER RXFILTER-REMOVE 3",
|
||||
"DRIVER RXFILTER-REMOVE 1",
|
||||
"DRIVER RXFILTER-REMOVE 0"];
|
||||
|
||||
doBooleanCommandChain(commandChain, callback);
|
||||
};
|
||||
|
||||
command.doGetRssi = function (cmd, callback) {
|
||||
doCommand(cmd, function(data) {
|
||||
var rssi = -200;
|
||||
|
||||
if (!data.status) {
|
||||
// If we are associating, the reply is "OK".
|
||||
var reply = data.reply;
|
||||
if (reply !== "OK") {
|
||||
// Format is: <SSID> rssi XX". SSID can contain spaces.
|
||||
var offset = reply.lastIndexOf("rssi ");
|
||||
if (offset !== -1) {
|
||||
rssi = reply.substr(offset + 5) | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(rssi);
|
||||
});
|
||||
};
|
||||
|
||||
command.getRssi = function (callback) {
|
||||
command.doGetRssi("DRIVER RSSI", callback);
|
||||
};
|
||||
|
||||
command.getRssiApprox = function (callback) {
|
||||
command.doGetRssi("DRIVER RSSI-APPROX", callback);
|
||||
};
|
||||
|
||||
command.getLinkSpeed = function (callback) {
|
||||
doStringCommand("DRIVER LINKSPEED", function(reply) {
|
||||
if (reply) {
|
||||
reply = reply.split(" ")[1] | 0; // Format: LinkSpeed XX
|
||||
}
|
||||
callback(reply);
|
||||
});
|
||||
};
|
||||
|
||||
let infoKeys = [{regexp: /RSSI=/i, prop: 'rssi'},
|
||||
{regexp: /LINKSPEED=/i, prop: 'linkspeed'}];
|
||||
|
||||
command.getConnectionInfoICS = function (callback) {
|
||||
doStringCommand("SIGNAL_POLL", function(reply) {
|
||||
if (!reply) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find any values matching |infoKeys|. This gets executed frequently
|
||||
// enough that we want to avoid creating intermediate strings as much as
|
||||
// possible.
|
||||
let rval = {};
|
||||
for (let i = 0; i < infoKeys.length; i++) {
|
||||
let re = infoKeys[i].regexp;
|
||||
let iKeyStart = reply.search(re);
|
||||
if (iKeyStart !== -1) {
|
||||
let prop = infoKeys[i].prop;
|
||||
let iValueStart = reply.indexOf('=', iKeyStart) + 1;
|
||||
let iNewlineAfterValue = reply.indexOf('\n', iValueStart);
|
||||
let iValueEnd = iNewlineAfterValue !== -1
|
||||
? iNewlineAfterValue
|
||||
: reply.length;
|
||||
rval[prop] = reply.substring(iValueStart, iValueEnd) | 0;
|
||||
}
|
||||
}
|
||||
|
||||
callback(rval);
|
||||
});
|
||||
};
|
||||
|
||||
command.getMacAddress = function (callback) {
|
||||
doStringCommand("DRIVER MACADDR", function(reply) {
|
||||
if (reply) {
|
||||
reply = reply.split(" ")[2]; // Format: Macaddr = XX.XX.XX.XX.XX.XX
|
||||
}
|
||||
callback(reply);
|
||||
});
|
||||
};
|
||||
|
||||
command.connectToHostapd = function(callback) {
|
||||
voidControlMessage("connect_to_hostapd", callback);
|
||||
};
|
||||
|
||||
command.closeHostapdConnection = function(callback) {
|
||||
voidControlMessage("close_hostapd_connection", callback);
|
||||
};
|
||||
|
||||
command.hostapdCommand = function (callback, request) {
|
||||
var msg = { cmd: "hostapd_command",
|
||||
request: request,
|
||||
iface: aInterface };
|
||||
|
||||
aControlMessage(msg, function(data) {
|
||||
callback(data.status ? null : data.reply);
|
||||
});
|
||||
};
|
||||
|
||||
command.hostapdGetStations = function (callback) {
|
||||
var msg = { cmd: "hostapd_get_stations",
|
||||
iface: aInterface };
|
||||
|
||||
aControlMessage(msg, function(data) {
|
||||
callback(data.status);
|
||||
});
|
||||
};
|
||||
|
||||
command.setPowerModeICS = function (mode, callback) {
|
||||
doBooleanCommand("DRIVER POWERMODE " + (mode === "AUTO" ? 0 : 1), "OK", callback);
|
||||
};
|
||||
|
||||
command.setPowerModeJB = function (mode, callback) {
|
||||
doBooleanCommand("SET ps " + (mode === "AUTO" ? 1 : 0), "OK", callback);
|
||||
};
|
||||
|
||||
command.getPowerMode = function (callback) {
|
||||
doStringCommand("DRIVER GETPOWER", function(reply) {
|
||||
if (reply) {
|
||||
reply = (reply.split()[2]|0); // Format: powermode = XX
|
||||
}
|
||||
callback(reply);
|
||||
});
|
||||
};
|
||||
|
||||
command.setNumAllowedChannels = function (numChannels, callback) {
|
||||
doBooleanCommand("DRIVER SCAN-CHANNELS " + numChannels, "OK", callback);
|
||||
};
|
||||
|
||||
command.getNumAllowedChannels = function (callback) {
|
||||
doStringCommand("DRIVER SCAN-CHANNELS", function(reply) {
|
||||
if (reply) {
|
||||
reply = (reply.split()[2]|0); // Format: Scan-Channels = X
|
||||
}
|
||||
callback(reply);
|
||||
});
|
||||
};
|
||||
|
||||
command.setBluetoothCoexistenceMode = function (mode, callback) {
|
||||
doBooleanCommand("DRIVER BTCOEXMODE " + mode, "OK", callback);
|
||||
};
|
||||
|
||||
command.setBluetoothCoexistenceScanMode = function (mode, callback) {
|
||||
doBooleanCommand("DRIVER BTCOEXSCAN-" + (mode ? "START" : "STOP"),
|
||||
"OK", callback);
|
||||
};
|
||||
|
||||
command.saveConfig = function (callback) {
|
||||
// Make sure we never write out a value for AP_SCAN other than 1.
|
||||
doBooleanCommand("AP_SCAN 1", "OK", function(ok) {
|
||||
doBooleanCommand("SAVE_CONFIG", "OK", callback);
|
||||
});
|
||||
};
|
||||
|
||||
command.reloadConfig = function (callback) {
|
||||
doBooleanCommand("RECONFIGURE", "OK", callback);
|
||||
};
|
||||
|
||||
command.setScanResultHandling = function (mode, callback) {
|
||||
doBooleanCommand("AP_SCAN " + mode, "OK", callback);
|
||||
};
|
||||
|
||||
command.addToBlacklist = function (bssid, callback) {
|
||||
doBooleanCommand("BLACKLIST " + bssid, "OK", callback);
|
||||
};
|
||||
|
||||
command.clearBlacklist = function (callback) {
|
||||
doBooleanCommand("BLACKLIST clear", "OK", callback);
|
||||
};
|
||||
|
||||
command.setSuspendOptimizationsICS = function (enabled, callback) {
|
||||
doBooleanCommand("DRIVER SETSUSPENDOPT " + (enabled ? 0 : 1),
|
||||
"OK", callback);
|
||||
};
|
||||
|
||||
command.setSuspendOptimizationsJB = function (enabled, callback) {
|
||||
doBooleanCommand("DRIVER SETSUSPENDMODE " + (enabled ? 1 : 0),
|
||||
"OK", callback);
|
||||
};
|
||||
|
||||
command.connectToSupplicant = function(callback) {
|
||||
voidControlMessage("connect_to_supplicant", callback);
|
||||
};
|
||||
|
||||
command.closeSupplicantConnection = function(callback) {
|
||||
voidControlMessage("close_supplicant_connection", callback);
|
||||
};
|
||||
|
||||
command.getMacAddress = function(callback) {
|
||||
doStringCommand("DRIVER MACADDR", function(reply) {
|
||||
if (reply) {
|
||||
reply = reply.split(" ")[2]; // Format: Macaddr = XX.XX.XX.XX.XX.XX
|
||||
}
|
||||
callback(reply);
|
||||
});
|
||||
};
|
||||
|
||||
command.setDeviceName = function(deviceName, callback) {
|
||||
doBooleanCommand("SET device_name " + deviceName, "OK", callback);
|
||||
};
|
||||
|
||||
//-------------------------------------------------
|
||||
// P2P commands.
|
||||
//-------------------------------------------------
|
||||
|
||||
command.p2pProvDiscovery = function(address, wpsMethod, callback) {
|
||||
var command = "P2P_PROV_DISC " + address + " " + wpsMethod;
|
||||
doBooleanCommand(command, "OK", callback);
|
||||
};
|
||||
|
||||
command.p2pConnect = function(config, callback) {
|
||||
var command = "P2P_CONNECT " + config.address + " " + config.wpsMethodWithPin + " ";
|
||||
if (config.joinExistingGroup) {
|
||||
command += "join";
|
||||
} else {
|
||||
command += "go_intent=" + config.goIntent;
|
||||
}
|
||||
|
||||
debug('P2P connect command: ' + command);
|
||||
doBooleanCommand(command, "OK", callback);
|
||||
};
|
||||
|
||||
command.p2pGroupRemove = function(iface, callback) {
|
||||
debug("groupRemove()");
|
||||
doBooleanCommand("P2P_GROUP_REMOVE " + iface, "OK", callback);
|
||||
};
|
||||
|
||||
command.p2pEnable = function(detail, callback) {
|
||||
var commandChain = ["SET device_name " + detail.deviceName,
|
||||
"SET device_type " + detail.deviceType,
|
||||
"SET config_methods " + detail.wpsMethods,
|
||||
"P2P_SET conc_pref sta",
|
||||
"P2P_FLUSH"];
|
||||
|
||||
doBooleanCommandChain(commandChain, callback);
|
||||
};
|
||||
|
||||
command.p2pDisable = function(callback) {
|
||||
doBooleanCommand("P2P_SET disabled 1", "OK", callback);
|
||||
};
|
||||
|
||||
command.p2pEnableScan = function(timeout, callback) {
|
||||
doBooleanCommand("P2P_FIND " + timeout, "OK", callback);
|
||||
};
|
||||
|
||||
command.p2pDisableScan = function(callback) {
|
||||
doBooleanCommand("P2P_STOP_FIND", "OK", callback);
|
||||
};
|
||||
|
||||
command.p2pGetGroupCapab = function(address, callback) {
|
||||
command.p2pPeer(address, function(reply) {
|
||||
debug('p2p_peer reply: ' + reply);
|
||||
if (!reply) {
|
||||
callback(0);
|
||||
return;
|
||||
}
|
||||
var capab = /group_capab=0x([0-9a-fA-F]+)/.exec(reply)[1];
|
||||
if (!capab) {
|
||||
callback(0);
|
||||
} else {
|
||||
callback(parseInt(capab, 16));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
command.p2pPeer = function(address, callback) {
|
||||
doStringCommand("P2P_PEER " + address, callback);
|
||||
};
|
||||
|
||||
command.p2pGroupAdd = function(netId, callback) {
|
||||
doBooleanCommand("P2P_GROUP_ADD persistent=" + netId, callback);
|
||||
};
|
||||
|
||||
command.p2pReinvoke = function(netId, address, callback) {
|
||||
doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + address, "OK", callback);
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Private stuff.
|
||||
//----------------------------------------------------------
|
||||
|
||||
function voidControlMessage(cmd, callback) {
|
||||
aControlMessage({ cmd: cmd, iface: aInterface }, function (data) {
|
||||
callback(data.status);
|
||||
});
|
||||
}
|
||||
|
||||
function doCommand(request, callback) {
|
||||
var msg = { cmd: "command",
|
||||
request: request,
|
||||
iface: aInterface };
|
||||
|
||||
aControlMessage(msg, callback);
|
||||
}
|
||||
|
||||
function doIntCommand(request, callback) {
|
||||
doCommand(request, function(data) {
|
||||
callback(data.status ? -1 : (data.reply|0));
|
||||
});
|
||||
}
|
||||
|
||||
function doBooleanCommand(request, expected, callback) {
|
||||
doCommand(request, function(data) {
|
||||
callback(data.status ? false : (data.reply === expected));
|
||||
});
|
||||
}
|
||||
|
||||
function doStringCommand(request, callback) {
|
||||
doCommand(request, function(data) {
|
||||
callback(data.status ? null : data.reply);
|
||||
});
|
||||
}
|
||||
|
||||
function doBooleanCommandChain(commandChain, callback, i) {
|
||||
if (undefined === i) {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
doBooleanCommand(commandChain[i], "OK", function(ok) {
|
||||
if (!ok) {
|
||||
return callback(false);
|
||||
}
|
||||
i++;
|
||||
if (i === commandChain.length || !commandChain[i]) {
|
||||
// Reach the end or empty command.
|
||||
return callback(true);
|
||||
}
|
||||
doBooleanCommandChain(commandChain, callback, i);
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// Helper functions.
|
||||
//--------------------------------------------------
|
||||
|
||||
function stopProcess(service, process, callback) {
|
||||
var count = 0;
|
||||
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
function tick() {
|
||||
let result = libcutils.property_get(service);
|
||||
if (result === null) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (result === "stopped" || ++count >= 5) {
|
||||
// Either we succeeded or ran out of time.
|
||||
timer = null;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Else it's still running, continue waiting.
|
||||
timer.initWithCallback(tick, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
setProperty("ctl.stop", process, tick);
|
||||
}
|
||||
|
||||
// Wrapper around libcutils.property_set that returns true if setting the
|
||||
// value was successful.
|
||||
// Note that the callback is not called asynchronously.
|
||||
function setProperty(key, value, callback) {
|
||||
let ok = true;
|
||||
try {
|
||||
libcutils.property_set(key, value);
|
||||
} catch(e) {
|
||||
ok = false;
|
||||
}
|
||||
callback(ok);
|
||||
}
|
||||
|
||||
function isJellybean() {
|
||||
// According to http://developer.android.com/guide/topics/manifest/uses-sdk-element.html
|
||||
// ----------------------------------------------------
|
||||
// | Platform Version | API Level | VERSION_CODE |
|
||||
// ----------------------------------------------------
|
||||
// | Android 4.1, 4.1.1 | 16 | JELLY_BEAN_MR2 |
|
||||
// | Android 4.2, 4.2.2 | 17 | JELLY_BEAN_MR1 |
|
||||
// | Android 4.3 | 18 | JELLY_BEAN |
|
||||
// ----------------------------------------------------
|
||||
return aSdkVersion === 16 || aSdkVersion === 17 || aSdkVersion === 18;
|
||||
}
|
||||
|
||||
return command;
|
||||
};
|
||||
@@ -1,188 +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 "WifiHotspotUtils.h"
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include "prinit.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
static void* sWifiHotspotUtilsLib;
|
||||
static PRCallOnceType sInitWifiHotspotUtilsLib;
|
||||
// Socket pair used to exit from a blocking read.
|
||||
static struct wpa_ctrl* ctrl_conn;
|
||||
static const char *ctrl_iface_dir = "/data/misc/wifi/hostapd";
|
||||
static char *ctrl_ifname = nullptr;
|
||||
|
||||
DEFINE_DLFUNC(wpa_ctrl_open, struct wpa_ctrl*, const char*)
|
||||
DEFINE_DLFUNC(wpa_ctrl_close, void, struct wpa_ctrl*)
|
||||
DEFINE_DLFUNC(wpa_ctrl_attach, int32_t, struct wpa_ctrl*)
|
||||
DEFINE_DLFUNC(wpa_ctrl_detach, int32_t, struct wpa_ctrl*)
|
||||
DEFINE_DLFUNC(wpa_ctrl_request, int32_t, struct wpa_ctrl*,
|
||||
const char*, size_t cmd_len, char *reply,
|
||||
size_t *reply_len, void (*msg_cb)(char *msg, size_t len))
|
||||
|
||||
|
||||
static PRStatus
|
||||
InitWifiHotspotUtilsLib()
|
||||
{
|
||||
sWifiHotspotUtilsLib = dlopen("/system/lib/libwpa_client.so", RTLD_LAZY);
|
||||
// We might fail to open the hardware lib. That's OK.
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
static void*
|
||||
GetWifiHotspotLibHandle()
|
||||
{
|
||||
PR_CallOnce(&sInitWifiHotspotUtilsLib, InitWifiHotspotUtilsLib);
|
||||
return sWifiHotspotUtilsLib;
|
||||
}
|
||||
|
||||
struct wpa_ctrl *
|
||||
WifiHotspotUtils::openConnection(const char *ifname)
|
||||
{
|
||||
if (!ifname) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
USE_DLFUNC(wpa_ctrl_open)
|
||||
ctrl_conn = wpa_ctrl_open(nsPrintfCString("%s/%s", ctrl_iface_dir, ifname).get());
|
||||
return ctrl_conn;
|
||||
}
|
||||
|
||||
int32_t
|
||||
WifiHotspotUtils::sendCommand(struct wpa_ctrl *ctrl, const char *cmd,
|
||||
char *reply, size_t *reply_len)
|
||||
{
|
||||
int32_t ret;
|
||||
|
||||
if (!ctrl_conn) {
|
||||
NS_WARNING(nsPrintfCString("Not connected to hostapd - \"%s\" command dropped.\n", cmd).get());
|
||||
return -1;
|
||||
}
|
||||
|
||||
USE_DLFUNC(wpa_ctrl_request)
|
||||
ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), reply, reply_len, nullptr);
|
||||
if (ret == -2) {
|
||||
NS_WARNING(nsPrintfCString("'%s' command timed out.\n", cmd).get());
|
||||
return -2;
|
||||
} else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Make the reply printable.
|
||||
reply[*reply_len] = '\0';
|
||||
if (strncmp(cmd, "STA-FIRST", 9) == 0 ||
|
||||
strncmp(cmd, "STA-NEXT", 8) == 0) {
|
||||
char *pos = reply;
|
||||
|
||||
while (*pos && *pos != '\n')
|
||||
pos++;
|
||||
*pos = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void*
|
||||
WifiHotspotUtils::GetSharedLibrary()
|
||||
{
|
||||
void* wpaClientLib = GetWifiHotspotLibHandle();
|
||||
if (!wpaClientLib) {
|
||||
NS_WARNING("No /system/lib/libwpa_client.so");
|
||||
}
|
||||
return wpaClientLib;
|
||||
}
|
||||
|
||||
int32_t WifiHotspotUtils::do_wifi_connect_to_hostapd()
|
||||
{
|
||||
struct dirent *dent;
|
||||
|
||||
DIR *dir = opendir(ctrl_iface_dir);
|
||||
if (dir) {
|
||||
while ((dent = readdir(dir))) {
|
||||
if (strcmp(dent->d_name, ".") == 0 ||
|
||||
strcmp(dent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
ctrl_ifname = strdup(dent->d_name);
|
||||
break;
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
ctrl_conn = openConnection(ctrl_ifname);
|
||||
if (!ctrl_conn) {
|
||||
NS_WARNING(nsPrintfCString("Unable to open connection to hostapd on \"%s\": %s",
|
||||
ctrl_ifname, strerror(errno)).get());
|
||||
return -1;
|
||||
}
|
||||
|
||||
USE_DLFUNC(wpa_ctrl_attach)
|
||||
if (wpa_ctrl_attach(ctrl_conn) != 0) {
|
||||
USE_DLFUNC(wpa_ctrl_close)
|
||||
wpa_ctrl_close(ctrl_conn);
|
||||
ctrl_conn = nullptr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t WifiHotspotUtils::do_wifi_close_hostapd_connection()
|
||||
{
|
||||
if (!ctrl_conn) {
|
||||
NS_WARNING("Invalid ctrl_conn.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
USE_DLFUNC(wpa_ctrl_detach)
|
||||
if (wpa_ctrl_detach(ctrl_conn) < 0) {
|
||||
NS_WARNING("Failed to detach wpa_ctrl.");
|
||||
}
|
||||
|
||||
USE_DLFUNC(wpa_ctrl_close)
|
||||
wpa_ctrl_close(ctrl_conn);
|
||||
ctrl_conn = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t WifiHotspotUtils::do_wifi_hostapd_command(const char *command,
|
||||
char *reply,
|
||||
size_t *reply_len)
|
||||
{
|
||||
return sendCommand(ctrl_conn, command, reply, reply_len);
|
||||
}
|
||||
|
||||
int32_t WifiHotspotUtils::do_wifi_hostapd_get_stations()
|
||||
{
|
||||
char addr[32], cmd[64];
|
||||
int stations = 0;
|
||||
size_t addrLen = sizeof(addr);
|
||||
|
||||
if (sendCommand(ctrl_conn, "STA-FIRST", addr, &addrLen)) {
|
||||
return 0;
|
||||
}
|
||||
stations++;
|
||||
|
||||
SprintfLiteral(cmd, "STA-NEXT %s", addr);
|
||||
while (sendCommand(ctrl_conn, cmd, addr, &addrLen) == 0) {
|
||||
stations++;
|
||||
SprintfLiteral(cmd, "STA-NEXT %s", addr);
|
||||
}
|
||||
|
||||
return stations;
|
||||
}
|
||||
@@ -1,46 +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/. */
|
||||
|
||||
/**
|
||||
* Abstraction on top of the network support from libnetutils that we
|
||||
* use to set up network connections.
|
||||
*/
|
||||
|
||||
#ifndef WifiHotspotUtils_h
|
||||
#define WifiHotspotUtils_h
|
||||
|
||||
// Forward declaration.
|
||||
struct wpa_ctrl;
|
||||
|
||||
class WifiHotspotUtils
|
||||
{
|
||||
public:
|
||||
static void* GetSharedLibrary();
|
||||
|
||||
int32_t do_wifi_connect_to_hostapd();
|
||||
int32_t do_wifi_close_hostapd_connection();
|
||||
int32_t do_wifi_hostapd_command(const char *command,
|
||||
char *reply,
|
||||
size_t *reply_len);
|
||||
int32_t do_wifi_hostapd_get_stations();
|
||||
|
||||
private:
|
||||
struct wpa_ctrl * openConnection(const char *ifname);
|
||||
int32_t sendCommand(struct wpa_ctrl *ctrl, const char *cmd,
|
||||
char *reply, size_t *reply_len);
|
||||
};
|
||||
|
||||
// Defines a function type with the right arguments and return type.
|
||||
#define DEFINE_DLFUNC(name, ret, args...) typedef ret (*FUNC##name)(args);
|
||||
|
||||
// Set up a dlsymed function ready to use.
|
||||
#define USE_DLFUNC(name) \
|
||||
FUNC##name name = (FUNC##name) dlsym(GetSharedLibrary(), #name); \
|
||||
if (!name) { \
|
||||
MOZ_CRASH("Symbol not found in shared library : " #name); \
|
||||
}
|
||||
|
||||
#endif // WifiHotspotUtils_h
|
||||
@@ -1,154 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* 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/systemlibs.js");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
|
||||
"@mozilla.org/network/service;1",
|
||||
"nsINetworkService");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["WifiNetUtil"];
|
||||
|
||||
const DHCP_PROP = "init.svc.dhcpcd";
|
||||
const DHCP = "dhcpcd";
|
||||
const DEBUG = false;
|
||||
|
||||
this.WifiNetUtil = function(controlMessage) {
|
||||
function debug(msg) {
|
||||
if (DEBUG) {
|
||||
dump('-------------- NetUtil: ' + msg);
|
||||
}
|
||||
}
|
||||
|
||||
var util = {};
|
||||
|
||||
util.runDhcp = function (ifname, gen, callback) {
|
||||
util.stopDhcp(ifname, function() {
|
||||
gNetworkService.dhcpRequest(ifname, function(success, dhcpInfo) {
|
||||
util.runIpConfig(ifname, dhcpInfo, function(data) {
|
||||
callback(data, gen);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
util.stopDhcp = function (ifname, callback) {
|
||||
// This function does exactly what dhcp_stop does. Unforunately, if we call
|
||||
// this function twice before the previous callback is returned. We may block
|
||||
// our self waiting for the callback. It slows down the wifi startup procedure.
|
||||
// Therefore, we have to roll our own version here.
|
||||
let dhcpService = DHCP_PROP + "_" + ifname;
|
||||
let suffix = (ifname.substr(0, 3) === "p2p") ? "p2p" : ifname;
|
||||
let processName = DHCP + "_" + suffix;
|
||||
|
||||
// The implementation of |dhcp_do_request| would wait until the
|
||||
// |result_prop_name| (e.g. dhcp.wlan0.result) to be non-null
|
||||
// or 30 second timeout. So we manually change the result property
|
||||
// to 'ko' to avoid timeout.
|
||||
//
|
||||
// http://androidxref.com/4.4.4_r1/xref/system/core/libnetutils/dhcp_utils.c#234
|
||||
setProperty('dhcp.' + suffix + '.result', 'ko', function() {
|
||||
stopProcess(dhcpService, processName, callback);
|
||||
});
|
||||
};
|
||||
|
||||
util.startDhcpServer = function (config, callback) {
|
||||
gNetworkService.setDhcpServer(true, config, function (error) {
|
||||
callback(!error);
|
||||
});
|
||||
};
|
||||
|
||||
util.stopDhcpServer = function (callback) {
|
||||
gNetworkService.setDhcpServer(false, null, function (error) {
|
||||
callback(!error);
|
||||
});
|
||||
};
|
||||
|
||||
util.runIpConfig = function (name, data, callback) {
|
||||
if (!data) {
|
||||
debug("IP config failed to run");
|
||||
callback({ info: data });
|
||||
return;
|
||||
}
|
||||
|
||||
setProperty("net." + name + ".dns1", ipToString(data.dns1),
|
||||
function(ok) {
|
||||
if (!ok) {
|
||||
debug("Unable to set net.<ifname>.dns1");
|
||||
return;
|
||||
}
|
||||
setProperty("net." + name + ".dns2", ipToString(data.dns2),
|
||||
function(ok) {
|
||||
if (!ok) {
|
||||
debug("Unable to set net.<ifname>.dns2");
|
||||
return;
|
||||
}
|
||||
setProperty("net." + name + ".gw", ipToString(data.gateway),
|
||||
function(ok) {
|
||||
if (!ok) {
|
||||
debug("Unable to set net.<ifname>.gw");
|
||||
return;
|
||||
}
|
||||
callback({ info: data });
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//--------------------------------------------------
|
||||
// Helper functions.
|
||||
//--------------------------------------------------
|
||||
|
||||
function stopProcess(service, process, callback) {
|
||||
var count = 0;
|
||||
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
function tick() {
|
||||
let result = libcutils.property_get(service);
|
||||
if (result === null) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (result === "stopped" || ++count >= 5) {
|
||||
// Either we succeeded or ran out of time.
|
||||
timer = null;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Else it's still running, continue waiting.
|
||||
timer.initWithCallback(tick, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
setProperty("ctl.stop", process, tick);
|
||||
}
|
||||
|
||||
// Wrapper around libcutils.property_set that returns true if setting the
|
||||
// value was successful.
|
||||
// Note that the callback is not called asynchronously.
|
||||
function setProperty(key, value, callback) {
|
||||
let ok = true;
|
||||
try {
|
||||
libcutils.property_set(key, value);
|
||||
} catch(e) {
|
||||
ok = false;
|
||||
}
|
||||
callback(ok);
|
||||
}
|
||||
|
||||
function ipToString(n) {
|
||||
return String((n >> 0) & 0xFF) + "." +
|
||||
((n >> 8) & 0xFF) + "." +
|
||||
((n >> 16) & 0xFF) + "." +
|
||||
((n >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
return util;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,319 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* 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;
|
||||
|
||||
const CONNECTION_STATUS_DISCONNECTED = "disconnected";
|
||||
const CONNECTION_STATUS_CONNECTING = "connecting";
|
||||
const CONNECTION_STATUS_CONNECTED = "connected";
|
||||
const CONNECTION_STATUS_DISCONNECTING = "disconnecting";
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["WifiP2pWorkerObserver"];
|
||||
|
||||
// WifiP2pWorkerObserver resides in WifiWorker to handle DOM message
|
||||
// by either 1) returning internally maintained information or
|
||||
// 2) delegating to aDomMsgResponder. It is also responsible
|
||||
// for observing events from WifiP2pManager and dispatch to DOM.
|
||||
//
|
||||
// @param aDomMsgResponder handles DOM messages, including
|
||||
// - setScanEnabled
|
||||
// - connect
|
||||
// - disconnect
|
||||
// - setPairingConfirmation
|
||||
// The instance is actually WifiP2pManager.
|
||||
this.WifiP2pWorkerObserver = function(aDomMsgResponder) {
|
||||
function debug(aMsg) {
|
||||
if (DEBUG) {
|
||||
dump('-------------- WifiP2pWorkerObserver: ' + aMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Private member variables.
|
||||
let _localDevice;
|
||||
let _peerList = {}; // List of P2pDevice.
|
||||
let _domManagers = [];
|
||||
let _enabled = false;
|
||||
let _groupOwner = null;
|
||||
let _currentPeer = null;
|
||||
|
||||
// Constructor of P2pDevice. It will be exposed to DOM.
|
||||
//
|
||||
// @param aPeer object representing a P2P device:
|
||||
// .name: string for the device name.
|
||||
// .address: Mac address.
|
||||
// .isGroupOwner: boolean to indicate if this device is the group owner.
|
||||
// .wpsCapabilities: array of string of {"pbc", "display", "keypad"}.
|
||||
function P2pDevice(aPeer) {
|
||||
this.address = aPeer.address;
|
||||
this.name = (aPeer.name ? aPeer.name : aPeer.address);
|
||||
this.isGroupOwner = aPeer.isGroupOwner;
|
||||
this.wpsCapabilities = aPeer.wpsCapabilities;
|
||||
this.connectionStatus = CONNECTION_STATUS_DISCONNECTED;
|
||||
|
||||
// Since this object will be exposed to web, defined the exposed
|
||||
// properties here.
|
||||
this.__exposedProps__ = {
|
||||
address: "r",
|
||||
name: "r",
|
||||
isGroupOwner: "r",
|
||||
wpsCapabilities: "r",
|
||||
connectionStatus: "r"
|
||||
};
|
||||
}
|
||||
|
||||
// Constructor of P2pGroupOwner.
|
||||
//
|
||||
// @param aGroupOwner:
|
||||
// .macAddress
|
||||
// .ipAddress
|
||||
// .passphrase
|
||||
// .ssid
|
||||
// .freq
|
||||
// .isLocal
|
||||
function P2pGroupOwner(aGroupOwner) {
|
||||
this.macAddress = aGroupOwner.macAddress; // The identifier to get further information.
|
||||
this.ipAddress = aGroupOwner.ipAddress;
|
||||
this.passphrase = aGroupOwner.passphrase;
|
||||
this.ssid = aGroupOwner.ssid; // e.g. DIRECT-xy.
|
||||
this.freq = aGroupOwner.freq;
|
||||
this.isLocal = aGroupOwner.isLocal;
|
||||
|
||||
let detail = _peerList[aGroupOwner.macAddress];
|
||||
if (detail) {
|
||||
this.name = detail.name;
|
||||
this.wpsCapabilities = detail.wpsCapabilities;
|
||||
} else if (_localDevice.address === this.macAddress) {
|
||||
this.name = _localDevice.name;
|
||||
this.wpsCapabilities = _localDevice.wpsCapabilities;
|
||||
} else {
|
||||
debug("We don't know this group owner: " + aGroupOwner.macAddress);
|
||||
this.name = aGroupOwner.macAddress;
|
||||
this.wpsCapabilities = [];
|
||||
}
|
||||
}
|
||||
|
||||
function fireEvent(aMessage, aData) {
|
||||
debug('domManager: ' + JSON.stringify(_domManagers));
|
||||
_domManagers.forEach(function(manager) {
|
||||
// Note: We should never have a dead message manager here because we
|
||||
// observe our child message managers shutting down below.
|
||||
manager.sendAsyncMessage("WifiP2pManager:" + aMessage, aData);
|
||||
});
|
||||
}
|
||||
|
||||
function addDomManager(aMsg) {
|
||||
if (-1 === _domManagers.indexOf(aMsg.manager)) {
|
||||
_domManagers.push(aMsg.manager);
|
||||
}
|
||||
}
|
||||
|
||||
function returnMessage(aMessage, aSuccess, aData, aMsg) {
|
||||
let rMsg = aMessage + ":Return:" + (aSuccess ? "OK" : "NO");
|
||||
aMsg.manager.sendAsyncMessage(rMsg,
|
||||
{ data: aData, rid: aMsg.rid, mid: aMsg.mid });
|
||||
}
|
||||
|
||||
function handlePeerListUpdated() {
|
||||
fireEvent("onpeerinfoupdate", {});
|
||||
}
|
||||
|
||||
// Return a literal object as the constructed object.
|
||||
return {
|
||||
onLocalDeviceChanged: function(aDevice) {
|
||||
_localDevice = aDevice;
|
||||
debug('Local device updated to: ' + JSON.stringify(_localDevice));
|
||||
},
|
||||
|
||||
onEnabled: function() {
|
||||
_peerList = [];
|
||||
_enabled = true;
|
||||
fireEvent("p2pUp", {});
|
||||
},
|
||||
|
||||
onDisbaled: function() {
|
||||
_enabled = false;
|
||||
fireEvent("p2pDown", {});
|
||||
},
|
||||
|
||||
onPeerFound: function(aPeer) {
|
||||
let newFoundPeer = new P2pDevice(aPeer);
|
||||
let origianlPeer = _peerList[aPeer.address];
|
||||
_peerList[aPeer.address] = newFoundPeer;
|
||||
if (origianlPeer) {
|
||||
newFoundPeer.connectionStatus = origianlPeer.connectionStatus;
|
||||
}
|
||||
handlePeerListUpdated();
|
||||
},
|
||||
|
||||
onPeerLost: function(aPeer) {
|
||||
let lostPeer = _peerList[aPeer.address];
|
||||
if (!lostPeer) {
|
||||
debug('Unknown peer lost: ' + aPeer.address);
|
||||
return;
|
||||
}
|
||||
delete _peerList[aPeer.address];
|
||||
handlePeerListUpdated();
|
||||
},
|
||||
|
||||
onConnecting: function(aPeer) {
|
||||
let peer = _peerList[aPeer.address];
|
||||
if (!peer) {
|
||||
debug('Unknown peer connecting: ' + aPeer.address);
|
||||
peer = new P2pDevice(aPeer);
|
||||
_peerList[aPeer.address] = peer;
|
||||
handlePeerListUpdated();
|
||||
}
|
||||
peer.connectionStatus = CONNECTION_STATUS_CONNECTING;
|
||||
|
||||
fireEvent('onconnecting', { peer: peer });
|
||||
},
|
||||
|
||||
onConnected: function(aGroupOwner, aPeer) {
|
||||
let go = new P2pGroupOwner(aGroupOwner);
|
||||
let peer = _peerList[aPeer.address];
|
||||
if (!peer) {
|
||||
debug('Unknown peer connected: ' + aPeer.address);
|
||||
peer = new P2pDevice(aPeer);
|
||||
_peerList[aPeer.address] = peer;
|
||||
handlePeerListUpdated();
|
||||
}
|
||||
peer.connectionStatus = CONNECTION_STATUS_CONNECTED;
|
||||
peer.isGroupOwner = (aPeer.address === aGroupOwner.address);
|
||||
|
||||
_groupOwner = go;
|
||||
_currentPeer = peer;
|
||||
|
||||
fireEvent('onconnected', { groupOwner: go, peer: peer });
|
||||
},
|
||||
|
||||
onDisconnected: function(aPeer) {
|
||||
let peer = _peerList[aPeer.address];
|
||||
if (!peer) {
|
||||
debug('Unknown peer disconnected: ' + aPeer.address);
|
||||
return;
|
||||
}
|
||||
|
||||
peer.connectionStatus = CONNECTION_STATUS_DISCONNECTED;
|
||||
|
||||
_groupOwner = null;
|
||||
_currentPeer = null;
|
||||
|
||||
fireEvent('ondisconnected', { peer: peer });
|
||||
},
|
||||
|
||||
getObservedDOMMessages: function() {
|
||||
return [
|
||||
"WifiP2pManager:getState",
|
||||
"WifiP2pManager:getPeerList",
|
||||
"WifiP2pManager:setScanEnabled",
|
||||
"WifiP2pManager:connect",
|
||||
"WifiP2pManager:disconnect",
|
||||
"WifiP2pManager:setPairingConfirmation",
|
||||
"WifiP2pManager:setDeviceName"
|
||||
];
|
||||
},
|
||||
|
||||
onDOMMessage: function(aMessage) {
|
||||
let msg = aMessage.data || {};
|
||||
msg.manager = aMessage.target;
|
||||
|
||||
if ("child-process-shutdown" === aMessage.name) {
|
||||
let i;
|
||||
if (-1 !== (i = _domManagers.indexOf(msg.manager))) {
|
||||
_domManagers.splice(i, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aMessage.target.assertPermission("wifi-manage")) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "WifiP2pManager:getState": // A new DOM manager is created.
|
||||
addDomManager(msg);
|
||||
return { // Synchronous call. Simply return it.
|
||||
enabled: _enabled,
|
||||
groupOwner: _groupOwner,
|
||||
currentPeer: _currentPeer
|
||||
};
|
||||
|
||||
case "WifiP2pManager:setScanEnabled":
|
||||
{
|
||||
let enabled = msg.data;
|
||||
|
||||
aDomMsgResponder.setScanEnabled(enabled, function(success) {
|
||||
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:getPeerList":
|
||||
{
|
||||
// Convert the object to an array.
|
||||
let peerArray = [];
|
||||
for (let key in _peerList) {
|
||||
if (_peerList.hasOwnProperty(key)) {
|
||||
peerArray.push(_peerList[key]);
|
||||
}
|
||||
}
|
||||
|
||||
returnMessage(aMessage.name, true, peerArray, msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:connect":
|
||||
{
|
||||
let peer = msg.data;
|
||||
|
||||
let onDoConnect = function(success) {
|
||||
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
|
||||
};
|
||||
|
||||
aDomMsgResponder.connect(peer.address, peer.wpsMethod,
|
||||
peer.goIntent, onDoConnect);
|
||||
}
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:disconnect":
|
||||
{
|
||||
let address = msg.data;
|
||||
|
||||
aDomMsgResponder.disconnect(address, function(success) {
|
||||
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:setPairingConfirmation":
|
||||
{
|
||||
let result = msg.data;
|
||||
aDomMsgResponder.setPairingConfirmation(result);
|
||||
returnMessage(aMessage.name, true, true, msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case "WifiP2pManager:setDeviceName":
|
||||
{
|
||||
let newDeviceName = msg.data;
|
||||
aDomMsgResponder.setDeviceName(newDeviceName, function(success) {
|
||||
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (0 === aMessage.name.indexOf("WifiP2pManager:")) {
|
||||
debug("DOM WifiP2pManager message not handled: " + aMessage.name);
|
||||
}
|
||||
} // End of switch.
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,357 +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 "WifiProxyService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "WifiUtils.h"
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
#include "GeckoTaskTracer.h"
|
||||
using namespace mozilla::tasktracer;
|
||||
#endif
|
||||
|
||||
#define NS_WIFIPROXYSERVICE_CID \
|
||||
{ 0xc6c9be7e, 0x744f, 0x4222, {0xb2, 0x03, 0xcd, 0x55, 0xdf, 0xc8, 0xbc, 0x12} }
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// The singleton Wifi service, to be used on the main thread.
|
||||
static StaticRefPtr<WifiProxyService> gWifiProxyService;
|
||||
|
||||
// The singleton supplicant class, that can be used on any thread.
|
||||
static UniquePtr<WpaSupplicant> gWpaSupplicant;
|
||||
|
||||
// Runnable used dispatch the WaitForEvent result on the main thread.
|
||||
class WifiEventDispatcher : public Runnable
|
||||
{
|
||||
public:
|
||||
WifiEventDispatcher(const nsAString& aEvent, const nsACString& aInterface)
|
||||
: mEvent(aEvent)
|
||||
, mInterface(aInterface)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
gWifiProxyService->DispatchWifiEvent(mEvent, mInterface);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mEvent;
|
||||
nsCString mInterface;
|
||||
};
|
||||
|
||||
// Runnable used to call WaitForEvent on the event thread.
|
||||
class EventRunnable : public Runnable
|
||||
{
|
||||
public:
|
||||
EventRunnable(const nsACString& aInterface)
|
||||
: mInterface(aInterface)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
nsAutoString event;
|
||||
gWpaSupplicant->WaitForEvent(event, mInterface);
|
||||
if (!event.IsEmpty()) {
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
// Make wifi initialization events to be the source events of TaskTracer,
|
||||
// and originate the rest correlation tasks from here.
|
||||
AutoSourceEvent taskTracerEvent(SourceEventType::Wifi);
|
||||
AddLabel("%s %s", mInterface.get(), NS_ConvertUTF16toUTF8(event).get());
|
||||
#endif
|
||||
nsCOMPtr<nsIRunnable> runnable = new WifiEventDispatcher(event, mInterface);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCString mInterface;
|
||||
};
|
||||
|
||||
// Runnable used dispatch the Command result on the main thread.
|
||||
class WifiResultDispatcher : public Runnable
|
||||
{
|
||||
public:
|
||||
WifiResultDispatcher(WifiResultOptions& aResult, const nsACString& aInterface)
|
||||
: mResult(aResult)
|
||||
, mInterface(aInterface)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
gWifiProxyService->DispatchWifiResult(mResult, mInterface);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
WifiResultOptions mResult;
|
||||
nsCString mInterface;
|
||||
};
|
||||
|
||||
// Runnable used to call SendCommand on the control thread.
|
||||
class ControlRunnable : public Runnable
|
||||
{
|
||||
public:
|
||||
ControlRunnable(CommandOptions aOptions, const nsACString& aInterface)
|
||||
: mOptions(aOptions)
|
||||
, mInterface(aInterface)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
WifiResultOptions result;
|
||||
if (gWpaSupplicant->ExecuteCommand(mOptions, result, mInterface)) {
|
||||
nsCOMPtr<nsIRunnable> runnable = new WifiResultDispatcher(result, mInterface);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
CommandOptions mOptions;
|
||||
nsCString mInterface;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(WifiProxyService, nsIWifiProxyService)
|
||||
|
||||
WifiProxyService::WifiProxyService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!gWifiProxyService);
|
||||
}
|
||||
|
||||
WifiProxyService::~WifiProxyService()
|
||||
{
|
||||
MOZ_ASSERT(!gWifiProxyService);
|
||||
}
|
||||
|
||||
already_AddRefed<WifiProxyService>
|
||||
WifiProxyService::FactoryCreate()
|
||||
{
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!gWifiProxyService) {
|
||||
gWifiProxyService = new WifiProxyService();
|
||||
ClearOnShutdown(&gWifiProxyService);
|
||||
|
||||
gWpaSupplicant = MakeUnique<WpaSupplicant>();
|
||||
ClearOnShutdown(&gWpaSupplicant);
|
||||
}
|
||||
|
||||
RefPtr<WifiProxyService> service = gWifiProxyService.get();
|
||||
return service.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiProxyService::Start(nsIWifiEventListener* aListener,
|
||||
const char ** aInterfaces,
|
||||
uint32_t aNumOfInterfaces)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aListener);
|
||||
|
||||
#if ANDROID_VERSION >= 19
|
||||
// KK changes the way mux'ing/demux'ing different supplicant interfaces
|
||||
// (e.g. wlan0/p2p0) from multi-sockets to single socket embedded with
|
||||
// prefixed interface name (e.g. IFNAME=wlan0 xxxxxx). Therefore, we use
|
||||
// the first given interface as the global interface for KK.
|
||||
aNumOfInterfaces = 1;
|
||||
#endif
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// Since EventRunnable runs in the manner of blocking, we have to
|
||||
// spin a thread for each interface.
|
||||
// (See the WpaSupplicant::WaitForEvent)
|
||||
mEventThreadList.SetLength(aNumOfInterfaces);
|
||||
for (uint32_t i = 0; i < aNumOfInterfaces; i++) {
|
||||
mEventThreadList[i].mInterface = aInterfaces[i];
|
||||
rv = NS_NewThread(getter_AddRefs(mEventThreadList[i].mThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Can't create wifi event thread");
|
||||
Shutdown();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
rv = NS_NewThread(getter_AddRefs(mControlThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Can't create wifi control thread");
|
||||
Shutdown();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mListener = aListener;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiProxyService::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (size_t i = 0; i < mEventThreadList.Length(); i++) {
|
||||
if (mEventThreadList[i].mThread) {
|
||||
mEventThreadList[i].mThread->Shutdown();
|
||||
mEventThreadList[i].mThread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
mEventThreadList.Clear();
|
||||
|
||||
if (mControlThread) {
|
||||
mControlThread->Shutdown();
|
||||
mControlThread = nullptr;
|
||||
}
|
||||
|
||||
mListener = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiProxyService::SendCommand(JS::Handle<JS::Value> aOptions,
|
||||
const nsACString& aInterface,
|
||||
JSContext* aCx)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
WifiCommandOptions options;
|
||||
|
||||
if (!options.Init(aCx, aOptions)) {
|
||||
NS_WARNING("Bad dictionary passed to WifiProxyService::SendCommand");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mControlThread) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Dispatch the command to the control thread.
|
||||
CommandOptions commandOptions(options);
|
||||
nsCOMPtr<nsIRunnable> runnable = new ControlRunnable(commandOptions, aInterface);
|
||||
mControlThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WifiProxyService::WaitForEvent(const nsACString& aInterface)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
#if ANDROID_VERSION >= 19
|
||||
// We will only have one global interface for KK.
|
||||
if (!mEventThreadList.IsEmpty()) {
|
||||
nsCOMPtr<nsIRunnable> runnable = new EventRunnable(aInterface);
|
||||
mEventThreadList[0].mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
#else
|
||||
// Dispatch to the event thread which has the given interface name
|
||||
for (size_t i = 0; i < mEventThreadList.Length(); i++) {
|
||||
if (mEventThreadList[i].mInterface.Equals(aInterface)) {
|
||||
nsCOMPtr<nsIRunnable> runnable = new EventRunnable(aInterface);
|
||||
mEventThreadList[i].mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void
|
||||
WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions, const nsACString& aInterface)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mozilla::AutoSafeJSContext cx;
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
|
||||
if (!ToJSValue(cx, aOptions, &val)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mListener) {
|
||||
// Call the listener with a JS value.
|
||||
mListener->OnCommand(val, aInterface);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WifiProxyService::DispatchWifiEvent(const nsAString& aEvent, const nsACString& aInterface)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
#if ANDROID_VERSION < 19
|
||||
mListener->OnWaitEvent(aEvent, aInterface);
|
||||
#else
|
||||
// The interface might be embedded in the event string such as
|
||||
// "IFNAME=wlan0 CTRL-EVENT-BSS-ADDED 65 3c:94:d5:7c:11:8b".
|
||||
// Parse the interface name from the event string and use p2p0
|
||||
// as the default interface if "IFNAME" is not found.
|
||||
nsAutoString event;
|
||||
nsAutoString embeddedInterface(NS_LITERAL_STRING("p2p0"));
|
||||
if (StringBeginsWith(aEvent, NS_LITERAL_STRING("IFNAME"))) {
|
||||
int32_t ifnameFrom = aEvent.FindChar('=') + 1;
|
||||
int32_t ifnameTo = aEvent.FindChar(' ') - 1;
|
||||
embeddedInterface = Substring(aEvent, ifnameFrom, ifnameTo - ifnameFrom + 1);
|
||||
event = Substring(aEvent, aEvent.FindChar(' ') + 1);
|
||||
}
|
||||
else {
|
||||
event = aEvent;
|
||||
}
|
||||
mListener->OnWaitEvent(event, NS_ConvertUTF16toUTF8(embeddedInterface));
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiProxyService,
|
||||
WifiProxyService::FactoryCreate)
|
||||
|
||||
NS_DEFINE_NAMED_CID(NS_WIFIPROXYSERVICE_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kWifiProxyServiceCIDs[] = {
|
||||
{ &kNS_WIFIPROXYSERVICE_CID, false, nullptr, WifiProxyServiceConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kWifiProxyServiceContracts[] = {
|
||||
{ "@mozilla.org/wifi/service;1", &kNS_WIFIPROXYSERVICE_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module kWifiProxyServiceModule = {
|
||||
mozilla::Module::kVersion,
|
||||
kWifiProxyServiceCIDs,
|
||||
kWifiProxyServiceContracts,
|
||||
nullptr
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
NSMODULE_DEFN(WifiProxyServiceModule) = &kWifiProxyServiceModule;
|
||||
@@ -1,49 +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 WifiProxyService_h
|
||||
#define WifiProxyService_h
|
||||
|
||||
#include "nsIWifiService.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsThread.h"
|
||||
#include "mozilla/dom/WifiOptionsBinding.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WifiProxyService final : public nsIWifiProxyService
|
||||
{
|
||||
private:
|
||||
struct EventThreadListEntry
|
||||
{
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
nsCString mInterface;
|
||||
};
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIWIFIPROXYSERVICE
|
||||
|
||||
static already_AddRefed<WifiProxyService>
|
||||
FactoryCreate();
|
||||
|
||||
void DispatchWifiEvent(const nsAString& aEvent, const nsACString& aInterface);
|
||||
void DispatchWifiResult(const mozilla::dom::WifiResultOptions& aOptions,
|
||||
const nsACString& aInterface);
|
||||
|
||||
private:
|
||||
WifiProxyService();
|
||||
~WifiProxyService();
|
||||
|
||||
nsTArray<EventThreadListEntry> mEventThreadList;
|
||||
nsCOMPtr<nsIThread> mControlThread;
|
||||
nsCOMPtr<nsIWifiEventListener> mListener;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // WifiProxyService_h
|
||||
@@ -1,521 +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 "WifiUtils.h"
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <cutils/properties.h>
|
||||
#include "prinit.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "js/CharacterEncoding.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
#define COMMAND_SIZE 256
|
||||
|
||||
// Intentionally not trying to dlclose() this handle. That's playing
|
||||
// Russian roulette with security bugs.
|
||||
static void* sWifiLib;
|
||||
static PRCallOnceType sInitWifiLib;
|
||||
|
||||
static PRStatus
|
||||
InitWifiLib()
|
||||
{
|
||||
sWifiLib = dlopen("/system/lib/libhardware_legacy.so", RTLD_LAZY);
|
||||
// We might fail to open the hardware lib. That's OK.
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
static void*
|
||||
GetSharedLibrary()
|
||||
{
|
||||
PR_CallOnce(&sInitWifiLib, InitWifiLib);
|
||||
return sWifiLib;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetWifiP2pSupported()
|
||||
{
|
||||
char propP2pSupported[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.moz.wifi.p2p_supported", propP2pSupported, "0");
|
||||
return (0 == strcmp(propP2pSupported, "1"));
|
||||
}
|
||||
|
||||
static int
|
||||
hex2num(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
hex2byte(const char* hex)
|
||||
{
|
||||
int a, b;
|
||||
a = hex2num(*hex++);
|
||||
if (a < 0)
|
||||
return -1;
|
||||
b = hex2num(*hex++);
|
||||
if (b < 0)
|
||||
return -1;
|
||||
return (a << 4) | b;
|
||||
}
|
||||
|
||||
// This function is equivalent to printf_decode() at src/utils/common.c in
|
||||
// the supplicant.
|
||||
|
||||
static uint32_t
|
||||
convertToBytes(char* buf, uint32_t maxlen, const char* str)
|
||||
{
|
||||
const char *pos = str;
|
||||
uint32_t len = 0;
|
||||
int val;
|
||||
|
||||
while (*pos) {
|
||||
if (len == maxlen)
|
||||
break;
|
||||
switch (*pos) {
|
||||
case '\\':
|
||||
pos++;
|
||||
switch (*pos) {
|
||||
case '\\':
|
||||
buf[len++] = '\\';
|
||||
pos++;
|
||||
break;
|
||||
case '"':
|
||||
buf[len++] = '"';
|
||||
pos++;
|
||||
break;
|
||||
case 'n':
|
||||
buf[len++] = '\n';
|
||||
pos++;
|
||||
break;
|
||||
case 'r':
|
||||
buf[len++] = '\r';
|
||||
pos++;
|
||||
break;
|
||||
case 't':
|
||||
buf[len++] = '\t';
|
||||
pos++;
|
||||
break;
|
||||
case 'e':
|
||||
buf[len++] = '\e';
|
||||
pos++;
|
||||
break;
|
||||
case 'x':
|
||||
pos++;
|
||||
val = hex2byte(pos);
|
||||
if (val < 0) {
|
||||
val = hex2num(*pos);
|
||||
if (val < 0)
|
||||
break;
|
||||
buf[len++] = val;
|
||||
pos++;
|
||||
} else {
|
||||
buf[len++] = val;
|
||||
pos += 2;
|
||||
}
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
val = *pos++ - '0';
|
||||
if (*pos >= '0' && *pos <= '7')
|
||||
val = val * 8 + (*pos++ - '0');
|
||||
if (*pos >= '0' && *pos <= '7')
|
||||
val = val * 8 + (*pos++ - '0');
|
||||
buf[len++] = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
buf[len++] = *pos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// This is the same algorithm as in InflateUTF8StringToBuffer with Copy and
|
||||
// while ignoring invalids.
|
||||
// https://mxr.mozilla.org/mozilla-central/source/js/src/vm/CharacterEncoding.cpp#231
|
||||
|
||||
static const uint32_t REPLACE_UTF8 = 0xFFFD;
|
||||
|
||||
static void
|
||||
LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut)
|
||||
{
|
||||
JS::UTF8Chars src(aInput, aLength);
|
||||
|
||||
char16_t dst[aLength]; // Allocating for worst case.
|
||||
|
||||
// Count how many char16_t characters are needed in the inflated string.
|
||||
// |i| is the index into |src|, and |j| is the the index into |dst|.
|
||||
size_t srclen = src.length();
|
||||
uint32_t j = 0;
|
||||
for (uint32_t i = 0; i < srclen; i++, j++) {
|
||||
uint32_t v = uint32_t(src[i]);
|
||||
if (v == uint32_t('\0') && i < srclen - 1) {
|
||||
// If the leading byte is '\0' and it's not the last byte,
|
||||
// just ignore it to prevent from being truncated. This could
|
||||
// be caused by |convertToBytes| (e.g. \x00 would be converted to '\0')
|
||||
j--;
|
||||
continue;
|
||||
}
|
||||
if (!(v & 0x80)) {
|
||||
// ASCII code unit. Simple copy.
|
||||
dst[j] = char16_t(v);
|
||||
} else {
|
||||
// Non-ASCII code unit. Determine its length in bytes (n).
|
||||
uint32_t n = 1;
|
||||
while (v & (0x80 >> n))
|
||||
n++;
|
||||
|
||||
#define INVALID(report, arg, n2) \
|
||||
do { \
|
||||
n = n2; \
|
||||
goto invalidMultiByteCodeUnit; \
|
||||
} while (0)
|
||||
|
||||
// Check the leading byte.
|
||||
if (n < 2 || n > 4)
|
||||
INVALID(ReportInvalidCharacter, i, 1);
|
||||
|
||||
// Check that |src| is large enough to hold an n-byte code unit.
|
||||
if (i + n > srclen)
|
||||
INVALID(ReportBufferTooSmall, /* dummy = */ 0, 1);
|
||||
|
||||
// Check the second byte. From Unicode Standard v6.2, Table 3-7
|
||||
// Well-Formed UTF-8 Byte Sequences.
|
||||
if ((v == 0xE0 && ((uint8_t)src[i + 1] & 0xE0) != 0xA0) || // E0 A0~BF
|
||||
(v == 0xED && ((uint8_t)src[i + 1] & 0xE0) != 0x80) || // ED 80~9F
|
||||
(v == 0xF0 && ((uint8_t)src[i + 1] & 0xF0) == 0x80) || // F0 90~BF
|
||||
(v == 0xF4 && ((uint8_t)src[i + 1] & 0xF0) != 0x80)) // F4 80~8F
|
||||
{
|
||||
INVALID(ReportInvalidCharacter, i, 1);
|
||||
}
|
||||
|
||||
// Check the continuation bytes.
|
||||
for (uint32_t m = 1; m < n; m++)
|
||||
if ((src[i + m] & 0xC0) != 0x80)
|
||||
INVALID(ReportInvalidCharacter, i, m);
|
||||
|
||||
// Determine the code unit's length in char16_t units and act accordingly.
|
||||
v = JS::Utf8ToOneUcs4Char((uint8_t *)&src[i], n);
|
||||
if (v < 0x10000) {
|
||||
// The n-byte UTF8 code unit will fit in a single char16_t.
|
||||
dst[j] = char16_t(v);
|
||||
} else {
|
||||
v -= 0x10000;
|
||||
if (v <= 0xFFFFF) {
|
||||
// The n-byte UTF8 code unit will fit in two char16_t units.
|
||||
dst[j] = char16_t((v >> 10) + 0xD800);
|
||||
j++;
|
||||
dst[j] = char16_t((v & 0x3FF) + 0xDC00);
|
||||
} else {
|
||||
// The n-byte UTF8 code unit won't fit in two char16_t units.
|
||||
INVALID(ReportTooBigCharacter, v, 1);
|
||||
}
|
||||
}
|
||||
|
||||
invalidMultiByteCodeUnit:
|
||||
// Move i to the last byte of the multi-byte code unit; the loop
|
||||
// header will do the final i++ to move to the start of the next
|
||||
// code unit.
|
||||
i += n - 1;
|
||||
}
|
||||
}
|
||||
|
||||
dst[j] = 0;
|
||||
aOut = dst;
|
||||
}
|
||||
|
||||
// Helper to check we have loaded the hardware shared library.
|
||||
#define CHECK_HWLIB(ret) \
|
||||
void* hwLib = GetSharedLibrary(); \
|
||||
if (!hwLib) { \
|
||||
NS_WARNING("No /system/lib/libhardware_legacy.so"); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define DEFAULT_IMPL(name, ret, args...) \
|
||||
DEFINE_DLFUNC(name, ret, args...) \
|
||||
ret do_##name(args) { \
|
||||
USE_DLFUNC(name) \
|
||||
return name(args); \
|
||||
}
|
||||
|
||||
// ICS implementation.
|
||||
class ICSWpaSupplicantImpl : public WpaSupplicantImpl
|
||||
{
|
||||
public:
|
||||
DEFAULT_IMPL(wifi_load_driver, int32_t, )
|
||||
DEFAULT_IMPL(wifi_unload_driver, int32_t, )
|
||||
|
||||
DEFINE_DLFUNC(wifi_wait_for_event, int32_t, char*, size_t)
|
||||
int32_t do_wifi_wait_for_event(const char *iface, char *buf, size_t len) {
|
||||
USE_DLFUNC(wifi_wait_for_event)
|
||||
return wifi_wait_for_event(buf, len);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*)
|
||||
int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) {
|
||||
USE_DLFUNC(wifi_command)
|
||||
return wifi_command(cmd, buf, len);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_start_supplicant, int32_t, )
|
||||
int32_t do_wifi_start_supplicant(int32_t) {
|
||||
USE_DLFUNC(wifi_start_supplicant)
|
||||
return wifi_start_supplicant();
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_stop_supplicant, int32_t)
|
||||
int32_t do_wifi_stop_supplicant(int32_t) {
|
||||
USE_DLFUNC(wifi_stop_supplicant)
|
||||
return wifi_stop_supplicant();
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, )
|
||||
int32_t do_wifi_connect_to_supplicant(const char* iface) {
|
||||
USE_DLFUNC(wifi_connect_to_supplicant)
|
||||
return wifi_connect_to_supplicant();
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_close_supplicant_connection, void, )
|
||||
void do_wifi_close_supplicant_connection(const char* iface) {
|
||||
USE_DLFUNC(wifi_close_supplicant_connection)
|
||||
return wifi_close_supplicant_connection();
|
||||
}
|
||||
};
|
||||
|
||||
// JB implementation.
|
||||
// We only redefine the methods that have a different signature than on ICS.
|
||||
class JBWpaSupplicantImpl : public ICSWpaSupplicantImpl
|
||||
{
|
||||
public:
|
||||
DEFINE_DLFUNC(wifi_wait_for_event, int32_t, const char*, char*, size_t)
|
||||
int32_t do_wifi_wait_for_event(const char* iface, char* buf, size_t len) {
|
||||
USE_DLFUNC(wifi_wait_for_event)
|
||||
return wifi_wait_for_event(iface, buf, len);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_command, int32_t, const char*, const char*, char*, size_t*)
|
||||
int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) {
|
||||
USE_DLFUNC(wifi_command)
|
||||
return wifi_command(iface, cmd, buf, len);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t)
|
||||
int32_t do_wifi_start_supplicant(int32_t arg) {
|
||||
USE_DLFUNC(wifi_start_supplicant)
|
||||
return wifi_start_supplicant(arg);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t)
|
||||
int32_t do_wifi_stop_supplicant(int32_t arg) {
|
||||
USE_DLFUNC(wifi_stop_supplicant)
|
||||
return wifi_stop_supplicant(arg);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, const char*)
|
||||
int32_t do_wifi_connect_to_supplicant(const char* iface) {
|
||||
USE_DLFUNC(wifi_connect_to_supplicant)
|
||||
return wifi_connect_to_supplicant(iface);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_close_supplicant_connection, void, const char*)
|
||||
void do_wifi_close_supplicant_connection(const char* iface) {
|
||||
USE_DLFUNC(wifi_close_supplicant_connection)
|
||||
wifi_close_supplicant_connection(iface);
|
||||
}
|
||||
};
|
||||
|
||||
// KK implementation.
|
||||
// We only redefine the methods that have a different signature than on ICS.
|
||||
class KKWpaSupplicantImpl : public ICSWpaSupplicantImpl
|
||||
{
|
||||
public:
|
||||
DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t)
|
||||
int32_t do_wifi_start_supplicant(int32_t arg) {
|
||||
USE_DLFUNC(wifi_start_supplicant)
|
||||
return wifi_start_supplicant(arg);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t)
|
||||
int32_t do_wifi_stop_supplicant(int32_t arg) {
|
||||
USE_DLFUNC(wifi_stop_supplicant)
|
||||
return wifi_stop_supplicant(arg);
|
||||
}
|
||||
|
||||
DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*)
|
||||
int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) {
|
||||
char command[COMMAND_SIZE];
|
||||
if (!strcmp(iface, "p2p0")) {
|
||||
// Commands for p2p0 interface don't need prefix
|
||||
SprintfLiteral(command, "%s", cmd);
|
||||
}
|
||||
else {
|
||||
SprintfLiteral(command, "IFNAME=%s %s", iface, cmd);
|
||||
}
|
||||
USE_DLFUNC(wifi_command)
|
||||
return wifi_command(command, buf, len);
|
||||
}
|
||||
};
|
||||
|
||||
// Concrete class to use to access the wpa supplicant.
|
||||
WpaSupplicant::WpaSupplicant()
|
||||
{
|
||||
char propVersion[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.build.version.sdk", propVersion, "0");
|
||||
mSdkVersion = strtol(propVersion, nullptr, 10);
|
||||
|
||||
if (mSdkVersion < 16) {
|
||||
mImpl = MakeUnique<ICSWpaSupplicantImpl>();
|
||||
} else if (mSdkVersion < 19) {
|
||||
mImpl = MakeUnique<JBWpaSupplicantImpl>();
|
||||
} else {
|
||||
mImpl = MakeUnique<KKWpaSupplicantImpl>();
|
||||
}
|
||||
mWifiHotspotUtils = MakeUnique<WifiHotspotUtils>();
|
||||
};
|
||||
|
||||
void WpaSupplicant::WaitForEvent(nsAString& aEvent, const nsCString& aInterface)
|
||||
{
|
||||
CHECK_HWLIB()
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
int32_t ret = mImpl->do_wifi_wait_for_event(aInterface.get(), buffer, BUFFER_SIZE);
|
||||
CheckBuffer(buffer, ret, aEvent);
|
||||
}
|
||||
|
||||
#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get()
|
||||
|
||||
/**
|
||||
* Make a subnet mask.
|
||||
*/
|
||||
uint32_t WpaSupplicant::MakeMask(uint32_t len) {
|
||||
uint32_t mask = 0;
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
mask |= (0x80000000 >> i);
|
||||
}
|
||||
return ntohl(mask);
|
||||
}
|
||||
|
||||
bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
|
||||
WifiResultOptions& aResult,
|
||||
const nsCString& aInterface)
|
||||
{
|
||||
CHECK_HWLIB(false)
|
||||
|
||||
if (!mWifiHotspotUtils->GetSharedLibrary()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Always correlate the opaque ids.
|
||||
aResult.mId = aOptions.mId;
|
||||
|
||||
if (aOptions.mCmd.EqualsLiteral("command")) {
|
||||
size_t len = BUFFER_SIZE - 1;
|
||||
char buffer[BUFFER_SIZE];
|
||||
NS_ConvertUTF16toUTF8 request(aOptions.mRequest);
|
||||
aResult.mStatus = mImpl->do_wifi_command(aInterface.get(), request.get(), buffer, &len);
|
||||
nsString value;
|
||||
if (aResult.mStatus == 0) {
|
||||
if (buffer[len - 1] == '\n') { // remove trailing new lines.
|
||||
len--;
|
||||
}
|
||||
buffer[len] = '\0';
|
||||
CheckBuffer(buffer, len, value);
|
||||
}
|
||||
aResult.mReply = value;
|
||||
} else if (aOptions.mCmd.EqualsLiteral("close_supplicant_connection")) {
|
||||
mImpl->do_wifi_close_supplicant_connection(aInterface.get());
|
||||
} else if (aOptions.mCmd.EqualsLiteral("load_driver")) {
|
||||
aResult.mStatus = mImpl->do_wifi_load_driver();
|
||||
} else if (aOptions.mCmd.EqualsLiteral("unload_driver")) {
|
||||
aResult.mStatus = mImpl->do_wifi_unload_driver();
|
||||
} else if (aOptions.mCmd.EqualsLiteral("start_supplicant")) {
|
||||
aResult.mStatus = mImpl->do_wifi_start_supplicant(GetWifiP2pSupported() ? 1 : 0);
|
||||
} else if (aOptions.mCmd.EqualsLiteral("stop_supplicant")) {
|
||||
aResult.mStatus = mImpl->do_wifi_stop_supplicant(0);
|
||||
} else if (aOptions.mCmd.EqualsLiteral("connect_to_supplicant")) {
|
||||
aResult.mStatus = mImpl->do_wifi_connect_to_supplicant(aInterface.get());
|
||||
} else if (aOptions.mCmd.EqualsLiteral("hostapd_command")) {
|
||||
size_t len = BUFFER_SIZE - 1;
|
||||
char buffer[BUFFER_SIZE];
|
||||
NS_ConvertUTF16toUTF8 request(aOptions.mRequest);
|
||||
aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_command(request.get(),
|
||||
buffer,
|
||||
&len);
|
||||
nsString value;
|
||||
if (aResult.mStatus == 0) {
|
||||
if (buffer[len - 1] == '\n') { // remove trailing new lines.
|
||||
len--;
|
||||
}
|
||||
buffer[len] = '\0';
|
||||
CheckBuffer(buffer, len, value);
|
||||
}
|
||||
aResult.mReply = value;
|
||||
} else if (aOptions.mCmd.EqualsLiteral("hostapd_get_stations")) {
|
||||
aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_get_stations();
|
||||
} else if (aOptions.mCmd.EqualsLiteral("connect_to_hostapd")) {
|
||||
aResult.mStatus = mWifiHotspotUtils->do_wifi_connect_to_hostapd();
|
||||
} else if (aOptions.mCmd.EqualsLiteral("close_hostapd_connection")) {
|
||||
aResult.mStatus = mWifiHotspotUtils->do_wifi_close_hostapd_connection();
|
||||
|
||||
} else {
|
||||
NS_WARNING("WpaSupplicant::ExecuteCommand : Unknown command");
|
||||
printf_stderr("WpaSupplicant::ExecuteCommand : Unknown command: %s",
|
||||
NS_ConvertUTF16toUTF8(aOptions.mCmd).get());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Checks the buffer and do the utf processing.
|
||||
void
|
||||
WpaSupplicant::CheckBuffer(char* buffer, int32_t length,
|
||||
nsAString& aEvent)
|
||||
{
|
||||
if (length <= 0 || length >= (BUFFER_SIZE - 1)) {
|
||||
NS_WARNING("WpaSupplicant::CheckBuffer: Invalid buffer length");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSdkVersion < 18) {
|
||||
buffer[length] = 0;
|
||||
LossyConvertUTF8toUTF16(buffer, length, aEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
// After Android JB4.3, the SSIDs have been converted into printable form.
|
||||
// In most of cases, SSIDs do not use unprintable characters, but IEEE 802.11
|
||||
// standard does not limit the used character set, so anything could be used
|
||||
// in an SSID. Convert it to raw data form here.
|
||||
char bytesBuffer[BUFFER_SIZE];
|
||||
uint32_t bytes = convertToBytes(bytesBuffer, length, buffer);
|
||||
if (bytes <= 0 || bytes >= BUFFER_SIZE) {
|
||||
NS_WARNING("WpaSupplicant::CheckBuffer: Invalid bytesbuffer length");
|
||||
return;
|
||||
}
|
||||
bytesBuffer[bytes] = 0;
|
||||
LossyConvertUTF8toUTF16(bytesBuffer, bytes, aEvent);
|
||||
}
|
||||
@@ -1,107 +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/. */
|
||||
|
||||
/**
|
||||
* Abstraction on top of the wifi support from libhardware_legacy that we
|
||||
* use to talk to the wpa_supplicant.
|
||||
*/
|
||||
|
||||
#ifndef WifiUtils_h
|
||||
#define WifiUtils_h
|
||||
|
||||
#include "nsString.h"
|
||||
#include "mozilla/dom/WifiOptionsBinding.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "WifiHotspotUtils.h"
|
||||
|
||||
// Needed to add a copy constructor to WifiCommandOptions.
|
||||
struct CommandOptions
|
||||
{
|
||||
public:
|
||||
CommandOptions(const mozilla::dom::WifiCommandOptions& aOther) {
|
||||
|
||||
#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_FIELD(mRequest, EmptyString())
|
||||
|
||||
#undef COPY_OPT_FIELD
|
||||
#undef COPY_FIELD
|
||||
}
|
||||
|
||||
// All the fields, not Optional<> anymore to get copy constructors.
|
||||
nsString mCmd;
|
||||
int32_t mId;
|
||||
nsString mRequest;
|
||||
};
|
||||
|
||||
// Abstract class that exposes libhardware_legacy functions we need for
|
||||
// wifi management.
|
||||
// We use the ICS signatures here since they are likely more future-proof.
|
||||
class WpaSupplicantImpl
|
||||
{
|
||||
public:
|
||||
// Suppress warning from |UniquePtr|
|
||||
virtual ~WpaSupplicantImpl() {}
|
||||
|
||||
virtual int32_t
|
||||
do_wifi_wait_for_event(const char *iface, char *buf, size_t len) = 0; // KK == ICS != JB
|
||||
|
||||
virtual int32_t
|
||||
do_wifi_command(const char* iface, const char* cmd, char* buff, size_t* len) = 0; // KK == ICS != JB
|
||||
|
||||
virtual int32_t
|
||||
do_wifi_load_driver() = 0;
|
||||
|
||||
virtual int32_t
|
||||
do_wifi_unload_driver() = 0;
|
||||
|
||||
virtual int32_t
|
||||
do_wifi_start_supplicant(int32_t) = 0; // ICS != JB == KK
|
||||
|
||||
virtual int32_t
|
||||
do_wifi_stop_supplicant(int32_t) = 0; //ICS != JB == KK
|
||||
|
||||
virtual int32_t
|
||||
do_wifi_connect_to_supplicant(const char* iface) = 0; // KK == ICS != JB
|
||||
|
||||
virtual void
|
||||
do_wifi_close_supplicant_connection(const char* iface) = 0; // KK == ICS != JB
|
||||
};
|
||||
|
||||
// Concrete class to use to access the wpa supplicant.
|
||||
class WpaSupplicant final
|
||||
{
|
||||
public:
|
||||
WpaSupplicant();
|
||||
|
||||
// Use nsCString as the type of aInterface to guarantee it's
|
||||
// null-terminated so that we can pass it to c API without
|
||||
// conversion
|
||||
void WaitForEvent(nsAString& aEvent, const nsCString& aInterface);
|
||||
bool ExecuteCommand(CommandOptions aOptions,
|
||||
mozilla::dom::WifiResultOptions& result,
|
||||
const nsCString& aInterface);
|
||||
|
||||
private:
|
||||
UniquePtr<WpaSupplicantImpl> mImpl;
|
||||
UniquePtr<WifiHotspotUtils> mWifiHotspotUtils;
|
||||
|
||||
uint32_t mSdkVersion;
|
||||
|
||||
protected:
|
||||
void CheckBuffer(char* buffer, int32_t length, nsAString& aEvent);
|
||||
uint32_t MakeMask(uint32_t len);
|
||||
};
|
||||
|
||||
#endif // WifiUtils_h
|
||||
@@ -1,9 +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/. */
|
||||
|
||||
#define NS_WIFIWORKER_CID \
|
||||
{ 0xA14E8977, 0xD259, 0x433A, \
|
||||
{ 0xA8, 0x8D, 0x58, 0xDD, 0x44, 0x65, 0x7E, 0x5B } }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
component {a14e8977-d259-433a-a88d-58dd44657e5b} WifiWorker.js
|
||||
@@ -1,41 +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/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIWifi.idl',
|
||||
'nsIWifiCertService.idl',
|
||||
'nsIWifiService.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_wifi'
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'DOMWifiManager.js',
|
||||
'DOMWifiManager.manifest',
|
||||
'DOMWifiP2pManager.js',
|
||||
'DOMWifiP2pManager.manifest',
|
||||
'WifiWorker.js',
|
||||
'WifiWorker.manifest',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'StateMachine.jsm',
|
||||
'WifiCommand.jsm',
|
||||
'WifiNetUtil.jsm',
|
||||
'WifiP2pManager.jsm',
|
||||
'WifiP2pWorkerObserver.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
UNIFIED_SOURCES = [
|
||||
'WifiCertService.cpp',
|
||||
'WifiHotspotUtils.cpp',
|
||||
'WifiProxyService.cpp',
|
||||
'WifiUtils.cpp',
|
||||
]
|
||||
DEFINES['CERT_AddTempCertToPerm'] = '__CERT_AddTempCertToPerm'
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user