Remove other gonk widget conditionals and unused files.

Tag #288.
This commit is contained in:
wolfbeast
2018-05-13 22:46:04 +02:00
committed by Roy Tam
parent 5830ff1f2b
commit 74df182488
232 changed files with 256 additions and 36730 deletions
-11
View File
@@ -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
View File
@@ -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',
+1 -12
View File
@@ -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");
-5
View File
@@ -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:
-5
View File
@@ -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']
-4
View File
@@ -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
View File
@@ -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']:
-84
View File
@@ -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);
}
}
-50
View File
@@ -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_
-87
View File
@@ -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;
}
}
-59
View File
@@ -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_
-84
View File
@@ -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);
}
}
-50
View File
@@ -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_
-415
View File
@@ -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;
}
}
-250
View File
@@ -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_
-138
View File
@@ -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);
}
}
-87
View File
@@ -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 "Theisombrand".
*/
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
-22
View File
@@ -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'
-13
View File
@@ -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_
-23
View File
@@ -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']:
+1 -1
View File
@@ -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 += [
-10
View File
@@ -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__'
-7
View File
@@ -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']
-655
View File
@@ -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]);
-2
View File
@@ -1,2 +0,0 @@
component {a96441dd-36b3-4f7f-963b-2c032e28a039} EthernetManager.js
contract @mozilla.org/ethernetManager;1 {a96441dd-36b3-4f7f-963b-2c032e28a039}
-200
View File
@@ -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;
}
-75
View File
@@ -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
-388
View File
@@ -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]);
-14
View File
@@ -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
-90
View File
@@ -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}
-6
View File
@@ -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);
};
-22
View File
@@ -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',
-1
View File
@@ -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
-217
View File
@@ -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
-66
View File
@@ -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
-23
View File
@@ -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'
-3
View File
@@ -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>
-92
View File
@@ -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) {};
}
-4
View File
@@ -1,4 +0,0 @@
#TetheringManager.js
component {bd8a831c-d8ec-4f00-8803-606e50781097} TetheringManager.js
contract @mozilla.org/tetheringmanager;1 {bd8a831c-d8ec-4f00-8803-606e50781097}
-12
View File
@@ -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'
-768
View File
@@ -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));
});
-73
View File
@@ -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;
};
-13
View File
@@ -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;
};
-12
View File
@@ -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;
};
-96
View File
@@ -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;
};
-18
View File
@@ -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;
};
-46
View File
@@ -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 = "";
};
-347
View File
@@ -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;
};
-147
View File
@@ -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 = "";
};
-19
View File
@@ -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
View File
@@ -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',
-543
View File
@@ -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) {};
}
-18
View File
@@ -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}
-328
View File
@@ -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]);
-6
View File
@@ -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}
-205
View File
@@ -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;
};
-536
View File
@@ -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;
-40
View File
@@ -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
-594
View File
@@ -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;
};
-188
View File
@@ -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;
}
-46
View File
@@ -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
-154
View File
@@ -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
-319
View File
@@ -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.
}
};
};
-357
View File
@@ -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;
-49
View File
@@ -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
-521
View File
@@ -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);
}
-107
View File
@@ -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
-9
View File
@@ -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
View File
@@ -1 +0,0 @@
component {a14e8977-d259-433a-a88d-58dd44657e5b} WifiWorker.js
-41
View File
@@ -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