Bug 1008091 - Send network change events on FxOS and Linux. r=sworkman

This commit is contained in:
Daniel Stenberg 2015-01-07 03:20:00 -05:00
Родитель 11eb2398e3
Коммит a7f268f4f7
7 изменённых файлов: 437 добавлений и 8 удалений

Просмотреть файл

@ -58,6 +58,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
'../system/android', '../system/android',
] ]
elif CONFIG['OS_ARCH'] == 'Linux':
LOCAL_INCLUDES += [
'../system/linux',
]
if CONFIG['NECKO_COOKIES']: if CONFIG['NECKO_COOKIES']:
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [
'../cookie', '../cookie',

Просмотреть файл

@ -376,6 +376,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsQtNetworkLinkService, Init)
#elif defined(MOZ_WIDGET_ANDROID) #elif defined(MOZ_WIDGET_ANDROID)
#include "nsAndroidNetworkLinkService.h" #include "nsAndroidNetworkLinkService.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAndroidNetworkLinkService) NS_GENERIC_FACTORY_CONSTRUCTOR(nsAndroidNetworkLinkService)
#elif defined(XP_LINUX)
#include "nsNotifyAddrListener_Linux.h"
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNotifyAddrListener, Init)
#endif #endif
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -791,6 +794,8 @@ NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
#elif defined(MOZ_WIDGET_ANDROID) #elif defined(MOZ_WIDGET_ANDROID)
NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
#elif defined(XP_LINUX)
NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
#endif #endif
NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID); NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID);
NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_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 }, { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsQtNetworkLinkServiceConstructor },
#elif defined(MOZ_WIDGET_ANDROID) #elif defined(MOZ_WIDGET_ANDROID)
{ &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsAndroidNetworkLinkServiceConstructor }, { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsAndroidNetworkLinkServiceConstructor },
#elif defined(XP_LINUX)
{ &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsNotifyAddrListenerConstructor },
#endif #endif
{ &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor }, { &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor },
{ &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor }, { &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 }, { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
#elif defined(MOZ_WIDGET_ANDROID) #elif defined(MOZ_WIDGET_ANDROID)
{ NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID }, { 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 #endif
{ NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID }, { NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID },
{ NS_REDIRECTCHANNELREGISTRAR_CONTRACTID, &kNS_REDIRECTCHANNELREGISTRAR_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': if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
DIRS += ['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); 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. /* 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 Also define it in debug builds, so that unit tests for it can be written
and run in non-GONK builds. */ and run in non-GONK builds. */
@ -170,14 +180,6 @@ and run in non-GONK builds. */
#define ReadSysFile_PRESENT #define ReadSysFile_PRESENT
#endif /* 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. * Read the contents of a file.
* This function is intended for reading a single-lined text files from * This function is intended for reading a single-lined text files from