/* -*- Mode: C++; 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/. */ #include "nsAndroidNetworkLinkService.h" #include "nsServiceManagerUtils.h" #include "nsIObserverService.h" #include "mozilla/StaticPrefs_network.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Services.h" #include "mozilla/Logging.h" #include "AndroidBridge.h" #include "mozilla/java/GeckoAppShellWrappers.h" #include "mozilla/jni/Utils.h" namespace java = mozilla::java; namespace jni = mozilla::jni; static mozilla::LazyLogModule gNotifyAddrLog("nsAndroidNetworkLinkService"); #define LOG(args) MOZ_LOG(gNotifyAddrLog, mozilla::LogLevel::Debug, args) NS_IMPL_ISUPPORTS(nsAndroidNetworkLinkService, nsINetworkLinkService, nsIObserver) nsAndroidNetworkLinkService::nsAndroidNetworkLinkService() : mStatusIsKnown(false) {} nsresult nsAndroidNetworkLinkService::Init() { nsCOMPtr observerService = mozilla::services::GetObserverService(); if (!observerService) { return NS_ERROR_FAILURE; } nsresult rv; rv = observerService->AddObserver(this, "xpcom-shutdown-threads", false); NS_ENSURE_SUCCESS(rv, rv); mNetlinkSvc = new mozilla::net::NetlinkService(); rv = mNetlinkSvc->Init(this); if (NS_FAILED(rv)) { mNetlinkSvc = nullptr; LOG(("Cannot initialize NetlinkService [rv=0x%08" PRIx32 "]", static_cast(rv))); return rv; } NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult nsAndroidNetworkLinkService::Shutdown() { // remove xpcom shutdown observer nsCOMPtr observerService = mozilla::services::GetObserverService(); if (observerService) observerService->RemoveObserver(this, "xpcom-shutdown-threads"); if (mNetlinkSvc) { mNetlinkSvc->Shutdown(); mNetlinkSvc = nullptr; } return NS_OK; } NS_IMETHODIMP nsAndroidNetworkLinkService::Observe(nsISupports* subject, const char* topic, const char16_t* data) { if (!strcmp("xpcom-shutdown-threads", topic)) { Shutdown(); } return NS_OK; } NS_IMETHODIMP nsAndroidNetworkLinkService::GetIsLinkUp(bool* aIsUp) { if (mNetlinkSvc && mStatusIsKnown) { mNetlinkSvc->GetIsLinkUp(aIsUp); return NS_OK; } if (!mozilla::AndroidBridge::Bridge()) { // Fail soft here and assume a connection exists NS_WARNING("GetIsLinkUp is not supported without a bridge connection"); *aIsUp = true; return NS_OK; } *aIsUp = java::GeckoAppShell::IsNetworkLinkUp(); return NS_OK; } NS_IMETHODIMP nsAndroidNetworkLinkService::GetLinkStatusKnown(bool* aIsKnown) { if (mStatusIsKnown) { *aIsKnown = true; return NS_OK; } NS_ENSURE_TRUE(mozilla::AndroidBridge::Bridge(), NS_ERROR_NOT_IMPLEMENTED); *aIsKnown = java::GeckoAppShell::IsNetworkLinkKnown(); return NS_OK; } NS_IMETHODIMP nsAndroidNetworkLinkService::GetLinkType(uint32_t* aLinkType) { NS_ENSURE_ARG_POINTER(aLinkType); if (!mozilla::AndroidBridge::Bridge()) { // Fail soft here and assume a connection exists NS_WARNING("GetLinkType is not supported without a bridge connection"); *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN; return NS_OK; } *aLinkType = java::GeckoAppShell::GetNetworkLinkType(); return NS_OK; } NS_IMETHODIMP nsAndroidNetworkLinkService::GetNetworkID(nsACString& aNetworkID) { if (!mNetlinkSvc) { return NS_ERROR_NOT_AVAILABLE; } mNetlinkSvc->GetNetworkID(aNetworkID); return NS_OK; } NS_IMETHODIMP nsAndroidNetworkLinkService::GetDnsSuffixList( nsTArray& aDnsSuffixList) { aDnsSuffixList.Clear(); if (!jni::IsAvailable()) { NS_WARNING("GetDnsSuffixList is not supported without JNI"); return NS_ERROR_NOT_AVAILABLE; } jni::String::LocalRef suffixList; nsresult rv = java::GeckoAppShell::GetDNSDomains(&suffixList); if (NS_FAILED(rv)) { return rv; } if (!suffixList || !suffixList->Length()) { return NS_OK; } nsAutoCString list(suffixList->ToCString()); for (const nsACString& suffix : list.Split(',')) { aDnsSuffixList.AppendElement(suffix); } return NS_OK; } NS_IMETHODIMP nsAndroidNetworkLinkService::GetResolvers( nsTArray>& aResolvers) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsAndroidNetworkLinkService::GetNativeResolvers( nsTArray& aResolvers) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsAndroidNetworkLinkService::GetPlatformDNSIndications( uint32_t* aPlatformDNSIndications) { return NS_ERROR_NOT_IMPLEMENTED; } void nsAndroidNetworkLinkService::OnNetworkChanged() { if (mozilla::StaticPrefs::network_notify_changed()) { RefPtr self = this; NS_DispatchToMainThread(NS_NewRunnableFunction( "nsAndroidNetworkLinkService::OnNetworkChanged", [self]() { self->NotifyObservers(NS_NETWORK_LINK_TOPIC, NS_NETWORK_LINK_DATA_CHANGED); })); } } void nsAndroidNetworkLinkService::OnNetworkIDChanged() { RefPtr self = this; NS_DispatchToMainThread(NS_NewRunnableFunction( "nsAndroidNetworkLinkService::OnNetworkIDChanged", [self]() { self->NotifyObservers(NS_NETWORK_ID_CHANGED_TOPIC, nullptr); })); } void nsAndroidNetworkLinkService::OnLinkUp() { RefPtr self = this; NS_DispatchToMainThread( NS_NewRunnableFunction("nsAndroidNetworkLinkService::OnLinkUp", [self]() { self->NotifyObservers(NS_NETWORK_LINK_TOPIC, NS_NETWORK_LINK_DATA_UP); })); } void nsAndroidNetworkLinkService::OnLinkDown() { RefPtr self = this; NS_DispatchToMainThread(NS_NewRunnableFunction( "nsAndroidNetworkLinkService::OnLinkDown", [self]() { self->NotifyObservers(NS_NETWORK_LINK_TOPIC, NS_NETWORK_LINK_DATA_DOWN); })); } void nsAndroidNetworkLinkService::OnLinkStatusKnown() { mStatusIsKnown = true; } void nsAndroidNetworkLinkService::OnDnsSuffixListUpdated() { RefPtr self = this; NS_DispatchToMainThread(NS_NewRunnableFunction( "nsAndroidNetworkLinkService::OnDnsSuffixListUpdated", [self]() { self->NotifyObservers(NS_DNS_SUFFIX_LIST_UPDATED_TOPIC, nullptr); })); } /* Sends the given event. Assumes aTopic/aData never goes out of scope (static * strings are ideal). */ void nsAndroidNetworkLinkService::NotifyObservers(const char* aTopic, const char* aData) { MOZ_ASSERT(NS_IsMainThread()); LOG(("nsAndroidNetworkLinkService::NotifyObservers: topic:%s data:%s\n", aTopic, aData ? aData : "")); nsCOMPtr observerService = mozilla::services::GetObserverService(); if (observerService) { observerService->NotifyObservers( static_cast(this), aTopic, aData ? NS_ConvertASCIItoUTF16(aData).get() : nullptr); } }