michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsNetworkLinkService.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsString.h" michael@0: #include "nsCRT.h" michael@0: michael@0: #import michael@0: #import michael@0: michael@0: NS_IMPL_ISUPPORTS(nsNetworkLinkService, michael@0: nsINetworkLinkService, michael@0: nsIObserver) michael@0: michael@0: nsNetworkLinkService::nsNetworkLinkService() michael@0: : mLinkUp(true) michael@0: , mStatusKnown(false) michael@0: , mReachability(NULL) michael@0: , mCFRunLoop(NULL) michael@0: { michael@0: } michael@0: michael@0: nsNetworkLinkService::~nsNetworkLinkService() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNetworkLinkService::GetIsLinkUp(bool *aIsUp) michael@0: { michael@0: *aIsUp = mLinkUp; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNetworkLinkService::GetLinkStatusKnown(bool *aIsUp) michael@0: { michael@0: *aIsUp = mStatusKnown; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNetworkLinkService::GetLinkType(uint32_t *aLinkType) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aLinkType); michael@0: michael@0: // XXX This function has not yet been implemented for this platform michael@0: *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNetworkLinkService::Observe(nsISupports *subject, michael@0: const char *topic, michael@0: const char16_t *data) michael@0: { michael@0: if (!strcmp(topic, "xpcom-shutdown")) { michael@0: Shutdown(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsNetworkLinkService::Init(void) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr observerService = michael@0: do_GetService("@mozilla.org/observer-service;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = observerService->AddObserver(this, "xpcom-shutdown", false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // If the network reachability API can reach 0.0.0.0 without michael@0: // requiring a connection, there is a network interface available. michael@0: struct sockaddr_in addr; michael@0: bzero(&addr, sizeof(addr)); michael@0: addr.sin_len = sizeof(addr); michael@0: addr.sin_family = AF_INET; michael@0: mReachability = michael@0: ::SCNetworkReachabilityCreateWithAddress(NULL, michael@0: (struct sockaddr *)&addr); michael@0: if (!mReachability) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: SCNetworkReachabilityContext context = {0, this, NULL, NULL, NULL}; michael@0: if (!::SCNetworkReachabilitySetCallback(mReachability, michael@0: ReachabilityChanged, michael@0: &context)) { michael@0: NS_WARNING("SCNetworkReachabilitySetCallback failed."); michael@0: ::CFRelease(mReachability); michael@0: mReachability = NULL; michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // Get the current run loop. This service is initialized at startup, michael@0: // so we shouldn't run in to any problems with modal dialog run loops. michael@0: mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; michael@0: if (!mCFRunLoop) { michael@0: NS_WARNING("Could not get current run loop."); michael@0: ::CFRelease(mReachability); michael@0: mReachability = NULL; michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: ::CFRetain(mCFRunLoop); michael@0: michael@0: if (!::SCNetworkReachabilityScheduleWithRunLoop(mReachability, mCFRunLoop, michael@0: kCFRunLoopDefaultMode)) { michael@0: NS_WARNING("SCNetworkReachabilityScheduleWIthRunLoop failed."); michael@0: ::CFRelease(mReachability); michael@0: mReachability = NULL; michael@0: ::CFRelease(mCFRunLoop); michael@0: mCFRunLoop = NULL; michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: UpdateReachability(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsNetworkLinkService::Shutdown() michael@0: { michael@0: if (!::SCNetworkReachabilityUnscheduleFromRunLoop(mReachability, michael@0: mCFRunLoop, michael@0: kCFRunLoopDefaultMode)) { michael@0: NS_WARNING("SCNetworkReachabilityUnscheduleFromRunLoop failed."); michael@0: } michael@0: michael@0: ::CFRelease(mReachability); michael@0: mReachability = NULL; michael@0: michael@0: ::CFRelease(mCFRunLoop); michael@0: mCFRunLoop = NULL; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsNetworkLinkService::UpdateReachability() michael@0: { michael@0: if (!mReachability) { michael@0: return; michael@0: } michael@0: michael@0: SCNetworkConnectionFlags flags; michael@0: if (!::SCNetworkReachabilityGetFlags(mReachability, &flags)) { michael@0: mStatusKnown = false; michael@0: return; michael@0: } michael@0: michael@0: bool reachable = (flags & kSCNetworkFlagsReachable) != 0; michael@0: bool needsConnection = (flags & kSCNetworkFlagsConnectionRequired) != 0; michael@0: michael@0: mLinkUp = (reachable && !needsConnection); michael@0: mStatusKnown = true; michael@0: } michael@0: michael@0: void michael@0: nsNetworkLinkService::SendEvent() michael@0: { michael@0: nsCOMPtr observerService = michael@0: do_GetService("@mozilla.org/observer-service;1"); michael@0: if (!observerService) michael@0: return; michael@0: michael@0: const char *event; michael@0: if (!mStatusKnown) michael@0: event = NS_NETWORK_LINK_DATA_UNKNOWN; michael@0: else michael@0: event = mLinkUp ? NS_NETWORK_LINK_DATA_UP michael@0: : NS_NETWORK_LINK_DATA_DOWN; michael@0: michael@0: observerService->NotifyObservers(static_cast(this), michael@0: NS_NETWORK_LINK_TOPIC, michael@0: NS_ConvertASCIItoUTF16(event).get()); michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: nsNetworkLinkService::ReachabilityChanged(SCNetworkReachabilityRef target, michael@0: SCNetworkConnectionFlags flags, michael@0: void *info) michael@0: { michael@0: nsNetworkLinkService *service = michael@0: static_cast(info); michael@0: michael@0: service->UpdateReachability(); michael@0: service->SendEvent(); michael@0: }