зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1008091 - Send network change events on FxOS and Linux. r=sworkman
This commit is contained in:
Родитель
11eb2398e3
Коммит
a7f268f4f7
|
@ -58,6 +58,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
|||
'../system/android',
|
||||
]
|
||||
|
||||
elif CONFIG['OS_ARCH'] == 'Linux':
|
||||
LOCAL_INCLUDES += [
|
||||
'../system/linux',
|
||||
]
|
||||
|
||||
if CONFIG['NECKO_COOKIES']:
|
||||
LOCAL_INCLUDES += [
|
||||
'../cookie',
|
||||
|
|
|
@ -376,6 +376,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsQtNetworkLinkService, Init)
|
|||
#elif defined(MOZ_WIDGET_ANDROID)
|
||||
#include "nsAndroidNetworkLinkService.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAndroidNetworkLinkService)
|
||||
#elif defined(XP_LINUX)
|
||||
#include "nsNotifyAddrListener_Linux.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNotifyAddrListener, Init)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -791,6 +794,8 @@ NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
|
|||
NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
|
||||
#elif defined(MOZ_WIDGET_ANDROID)
|
||||
NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
|
||||
#elif defined(XP_LINUX)
|
||||
NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
|
||||
#endif
|
||||
NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID);
|
||||
|
@ -935,6 +940,8 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
|||
{ &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsQtNetworkLinkServiceConstructor },
|
||||
#elif defined(MOZ_WIDGET_ANDROID)
|
||||
{ &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsAndroidNetworkLinkServiceConstructor },
|
||||
#elif defined(XP_LINUX)
|
||||
{ &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsNotifyAddrListenerConstructor },
|
||||
#endif
|
||||
{ &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor },
|
||||
{ &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor },
|
||||
|
@ -1082,6 +1089,8 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
|
|||
{ NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
|
||||
#elif defined(MOZ_WIDGET_ANDROID)
|
||||
{ NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
|
||||
#elif defined(XP_LINUX)
|
||||
{ NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
|
||||
#endif
|
||||
{ NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID },
|
||||
{ NS_REDIRECTCHANNELREGISTRAR_CONTRACTID, &kNS_REDIRECTCHANNELREGISTRAR_CID },
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; 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/.
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'Linux':
|
||||
SOURCES += [
|
||||
'nsNotifyAddrListener_Linux.cpp',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
|
@ -0,0 +1,318 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set et sw=4 ts=4: */
|
||||
/* 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 <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsNotifyAddrListener_Linux.h"
|
||||
#include "nsString.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <cutils/properties.h>
|
||||
#endif
|
||||
|
||||
/* a shorter name that better explains what it does */
|
||||
#define EINTR_RETRY(x) MOZ_TEMP_FAILURE_RETRY(x)
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo *gNotifyAddrLog = nullptr;
|
||||
#define LOG(args) PR_LOG(gNotifyAddrLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#endif
|
||||
|
||||
#define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
|
||||
nsINetworkLinkService,
|
||||
nsIRunnable,
|
||||
nsIObserver)
|
||||
|
||||
nsNotifyAddrListener::nsNotifyAddrListener()
|
||||
: mLinkUp(true) // assume true by default
|
||||
, mStatusKnown(false)
|
||||
, mAllowChangedEvent(true)
|
||||
, mChildThreadShutdown(false)
|
||||
{
|
||||
mShutdownPipe[0] = -1;
|
||||
mShutdownPipe[1] = -1;
|
||||
}
|
||||
|
||||
nsNotifyAddrListener::~nsNotifyAddrListener()
|
||||
{
|
||||
MOZ_ASSERT(!mThread, "nsNotifyAddrListener thread shutdown failed");
|
||||
|
||||
if (mShutdownPipe[0] != -1) {
|
||||
EINTR_RETRY(close(mShutdownPipe[0]));
|
||||
}
|
||||
if (mShutdownPipe[1] != -1) {
|
||||
EINTR_RETRY(close(mShutdownPipe[1]));
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNotifyAddrListener::GetIsLinkUp(bool *aIsUp)
|
||||
{
|
||||
// XXX This function has not yet been implemented for this platform
|
||||
*aIsUp = mLinkUp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNotifyAddrListener::GetLinkStatusKnown(bool *aIsUp)
|
||||
{
|
||||
// XXX This function has not yet been implemented for this platform
|
||||
*aIsUp = mStatusKnown;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNotifyAddrListener::GetLinkType(uint32_t *aLinkType)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aLinkType);
|
||||
|
||||
// XXX This function has not yet been implemented for this platform
|
||||
*aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct rtmsg *route_entry;
|
||||
|
||||
// The buffer size below, (4095) was chosen partly based on testing and
|
||||
// partly on existing sample source code using this size. It needs to be
|
||||
// large enough to hold the netlink messages from the kernel.
|
||||
char buffer[4095];
|
||||
|
||||
// Receiving netlink socket data
|
||||
ssize_t rc = EINTR_RETRY(recv(aNetlinkSocket, buffer, sizeof(buffer), 0));
|
||||
if (rc < 0) {
|
||||
return;
|
||||
}
|
||||
size_t netlink_bytes = rc;
|
||||
|
||||
nlh = reinterpret_cast<struct nlmsghdr *>(buffer);
|
||||
|
||||
bool networkChange = false;
|
||||
|
||||
for (; NLMSG_OK(nlh, netlink_bytes);
|
||||
nlh = NLMSG_NEXT(nlh, netlink_bytes)) {
|
||||
|
||||
if (NLMSG_DONE == nlh->nlmsg_type) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch(nlh->nlmsg_type) {
|
||||
case RTM_DELROUTE:
|
||||
case RTM_NEWROUTE:
|
||||
// Get the route data
|
||||
route_entry = static_cast<struct rtmsg *>(NLMSG_DATA(nlh));
|
||||
|
||||
// We are just intrested in main routing table
|
||||
if (route_entry->rtm_table != RT_TABLE_MAIN)
|
||||
continue;
|
||||
|
||||
networkChange = true;
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
networkChange = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (networkChange && mAllowChangedEvent) {
|
||||
SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNotifyAddrListener::Run()
|
||||
{
|
||||
PR_SetCurrentThreadName("Link Monitor");
|
||||
|
||||
int netlinkSocket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (netlinkSocket < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
struct sockaddr_nl addr;
|
||||
memset(&addr, 0, sizeof(addr)); // clear addr
|
||||
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR |
|
||||
RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
|
||||
|
||||
if (bind(netlinkSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
// failure!
|
||||
EINTR_RETRY(close(netlinkSocket));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// switch the socket into non-blocking
|
||||
int flags = fcntl(netlinkSocket, F_GETFL, 0);
|
||||
(void)fcntl(netlinkSocket, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
struct pollfd fds[2];
|
||||
fds[0].fd = mShutdownPipe[0];
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].revents = 0;
|
||||
|
||||
fds[1].fd = netlinkSocket;
|
||||
fds[1].events = POLLIN;
|
||||
fds[1].revents = 0;
|
||||
|
||||
// when in b2g emulator, work around bug 1112499
|
||||
int pollTimeout = -1;
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
char propQemu[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.kernel.qemu", propQemu, "");
|
||||
pollTimeout = !strncmp(propQemu, "1", 1) ? 100 : -1;
|
||||
#endif
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
bool shutdown = false;
|
||||
while (!shutdown) {
|
||||
int rc = EINTR_RETRY(poll(fds, 2, pollTimeout));
|
||||
|
||||
if (rc > 0) {
|
||||
if (fds[0].revents & POLLIN) {
|
||||
// shutdown, abort the loop!
|
||||
LOG(("thread shutdown received, dying...\n"));
|
||||
shutdown = true;
|
||||
} else if (fds[1].revents & POLLIN) {
|
||||
LOG(("netlink message received, handling it...\n"));
|
||||
OnNetlinkMessage(netlinkSocket);
|
||||
}
|
||||
} else if (rc < 0) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
break;
|
||||
}
|
||||
if (mChildThreadShutdown) {
|
||||
LOG(("thread shutdown via variable, dying...\n"));
|
||||
shutdown = true;
|
||||
}
|
||||
}
|
||||
|
||||
EINTR_RETRY(close(netlinkSocket));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNotifyAddrListener::Observe(nsISupports *subject,
|
||||
const char *topic,
|
||||
const char16_t *data)
|
||||
{
|
||||
if (!strcmp("xpcom-shutdown-threads", topic)) {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNotifyAddrListener::Init(void)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gNotifyAddrLog)
|
||||
gNotifyAddrLog = PR_NewLogModule("nsNotifyAddr");
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (!observerService)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsresult rv = observerService->AddObserver(this, "xpcom-shutdown-threads",
|
||||
false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Preferences::AddBoolVarCache(&mAllowChangedEvent,
|
||||
NETWORK_NOTIFY_CHANGED_PREF, true);
|
||||
|
||||
rv = NS_NewThread(getter_AddRefs(mThread), this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (-1 == pipe(mShutdownPipe)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNotifyAddrListener::Shutdown(void)
|
||||
{
|
||||
// remove xpcom shutdown observer
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService)
|
||||
observerService->RemoveObserver(this, "xpcom-shutdown-threads");
|
||||
|
||||
LOG(("write() to signal thread shutdown\n"));
|
||||
|
||||
// awake the thread to make it terminate
|
||||
ssize_t rc = EINTR_RETRY(write(mShutdownPipe[1], "1", 1));
|
||||
LOG(("write() returned %d, errno == %d\n", (int)rc, errno));
|
||||
|
||||
mChildThreadShutdown = true;
|
||||
|
||||
nsresult rv = mThread->Shutdown();
|
||||
|
||||
// Have to break the cycle here, otherwise nsNotifyAddrListener holds
|
||||
// onto the thread and the thread holds onto the nsNotifyAddrListener
|
||||
// via its mRunnable
|
||||
mThread = nullptr;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Sends the given event. Assumes aEventID never goes out of scope (static
|
||||
* strings are ideal).
|
||||
*/
|
||||
nsresult
|
||||
nsNotifyAddrListener::SendEvent(const char *aEventID)
|
||||
{
|
||||
if (!aEventID)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsIRunnable> event = new ChangeEvent(this, aEventID);
|
||||
if (NS_FAILED(rv = NS_DispatchToMainThread(event)))
|
||||
NS_WARNING("Failed to dispatch ChangeEvent");
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNotifyAddrListener::ChangeEvent::Run()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService)
|
||||
observerService->NotifyObservers(
|
||||
mService, NS_NETWORK_LINK_TOPIC,
|
||||
NS_ConvertASCIItoUTF16(mEventID).get());
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set et sw=4 ts=4: */
|
||||
/* 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 NSNOTIFYADDRLISTENER_LINUX_H_
|
||||
#define NSNOTIFYADDRLISTENER_LINUX_H_
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nsINetworkLinkService.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
class nsNotifyAddrListener : public nsINetworkLinkService,
|
||||
public nsIRunnable,
|
||||
public nsIObserver
|
||||
{
|
||||
virtual ~nsNotifyAddrListener();
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSINETWORKLINKSERVICE
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsNotifyAddrListener();
|
||||
nsresult Init(void);
|
||||
|
||||
private:
|
||||
class ChangeEvent : public nsRunnable {
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
ChangeEvent(nsINetworkLinkService *aService, const char *aEventID)
|
||||
: mService(aService), mEventID(aEventID) {
|
||||
}
|
||||
private:
|
||||
nsCOMPtr<nsINetworkLinkService> mService;
|
||||
const char *mEventID;
|
||||
};
|
||||
|
||||
// Called when xpcom-shutdown-threads is received.
|
||||
nsresult Shutdown(void);
|
||||
|
||||
// Sends the network event.
|
||||
nsresult SendEvent(const char *aEventID);
|
||||
|
||||
// Deals with incoming NETLINK messages.
|
||||
void OnNetlinkMessage(int NetlinkSocket);
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
|
||||
// The network is up.
|
||||
bool mLinkUp;
|
||||
|
||||
// The network's up/down status is known.
|
||||
bool mStatusKnown;
|
||||
|
||||
// A pipe to signal shutdown with.
|
||||
int mShutdownPipe[2];
|
||||
|
||||
// Network changed events are enabled
|
||||
bool mAllowChangedEvent;
|
||||
|
||||
// Flag to signal child thread kill with
|
||||
bool mChildThreadShutdown;
|
||||
};
|
||||
|
||||
#endif /* NSNOTIFYADDRLISTENER_LINUX_H_ */
|
|
@ -16,3 +16,5 @@ if CONFIG['MOZ_ENABLE_QTNETWORK']:
|
|||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
DIRS += ['android']
|
||||
|
||||
elif CONFIG['OS_ARCH'] == 'Linux':
|
||||
DIRS += ['linux']
|
||||
|
|
|
@ -161,6 +161,16 @@ void ReadAhead(filedesc_t aFd, const size_t aOffset = 0,
|
|||
const size_t aCount = SIZE_MAX);
|
||||
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK) || defined(XP_UNIX)
|
||||
#define MOZ_TEMP_FAILURE_RETRY(exp) (__extension__({ \
|
||||
typeof (exp) _rc; \
|
||||
do { \
|
||||
_rc = (exp); \
|
||||
} while (_rc == -1 && errno == EINTR); \
|
||||
_rc; \
|
||||
}))
|
||||
#endif
|
||||
|
||||
/* Define ReadSysFile() only on GONK to avoid unnecessary lubxul bloat.
|
||||
Also define it in debug builds, so that unit tests for it can be written
|
||||
and run in non-GONK builds. */
|
||||
|
@ -170,14 +180,6 @@ and run in non-GONK builds. */
|
|||
#define ReadSysFile_PRESENT
|
||||
#endif /* ReadSysFile_PRESENT */
|
||||
|
||||
#define MOZ_TEMP_FAILURE_RETRY(exp) (__extension__({ \
|
||||
typeof (exp) _rc; \
|
||||
do { \
|
||||
_rc = (exp); \
|
||||
} while (_rc == -1 && errno == EINTR); \
|
||||
_rc; \
|
||||
}))
|
||||
|
||||
/**
|
||||
* Read the contents of a file.
|
||||
* This function is intended for reading a single-lined text files from
|
||||
|
|
Загрузка…
Ссылка в новой задаче