michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* Copyright 2012 Mozilla Foundation and Mozilla contributors michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "GonkGPSGeolocationProvider.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsGeoPosition.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsINetworkManager.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "prtime.h" michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: #include "nsIDOMIccInfo.h" michael@0: #include "nsIDOMMobileConnection.h" michael@0: #include "nsIRadioInterfaceLayer.h" michael@0: #endif michael@0: michael@0: #define SETTING_DEBUG_ENABLED "geolocation.debugging.enabled" michael@0: michael@0: #ifdef AGPS_TYPE_INVALID michael@0: #define AGPS_HAVE_DUAL_APN michael@0: #endif michael@0: michael@0: #define FLUSH_AIDE_DATA 0 michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static const int kDefaultPeriod = 1000; // ms michael@0: static int gGPSDebugging = false; michael@0: michael@0: static const char* kNetworkConnStateChangedTopic = "network-connection-state-changed"; michael@0: michael@0: // While most methods of GonkGPSGeolocationProvider should only be michael@0: // called from main thread, we deliberately put the Init and ShutdownGPS michael@0: // methods off main thread to avoid blocking. michael@0: NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider, michael@0: nsIGeolocationProvider, michael@0: nsIObserver, michael@0: nsISettingsServiceCallback) michael@0: michael@0: /* static */ GonkGPSGeolocationProvider* GonkGPSGeolocationProvider::sSingleton = nullptr; michael@0: GpsCallbacks GonkGPSGeolocationProvider::mCallbacks = { michael@0: sizeof(GpsCallbacks), michael@0: LocationCallback, michael@0: StatusCallback, michael@0: SvStatusCallback, michael@0: NmeaCallback, michael@0: SetCapabilitiesCallback, michael@0: AcquireWakelockCallback, michael@0: ReleaseWakelockCallback, michael@0: CreateThreadCallback, michael@0: #ifdef GPS_CAPABILITY_ON_DEMAND_TIME michael@0: RequestUtcTimeCallback, michael@0: #endif michael@0: }; michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: AGpsCallbacks michael@0: GonkGPSGeolocationProvider::mAGPSCallbacks = { michael@0: AGPSStatusCallback, michael@0: CreateThreadCallback, michael@0: }; michael@0: michael@0: AGpsRilCallbacks michael@0: GonkGPSGeolocationProvider::mAGPSRILCallbacks = { michael@0: AGPSRILSetIDCallback, michael@0: AGPSRILRefLocCallback, michael@0: CreateThreadCallback, michael@0: }; michael@0: #endif // MOZ_B2G_RIL michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location) michael@0: { michael@0: class UpdateLocationEvent : public nsRunnable { michael@0: public: michael@0: UpdateLocationEvent(nsGeoPosition* aPosition) michael@0: : mPosition(aPosition) michael@0: {} michael@0: NS_IMETHOD Run() { michael@0: nsRefPtr provider = michael@0: GonkGPSGeolocationProvider::GetSingleton(); michael@0: provider->mLastGPSDerivedLocationTime = PR_Now(); michael@0: nsCOMPtr callback = provider->mLocationCallback; michael@0: if (callback) { michael@0: callback->Update(mPosition); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsRefPtr mPosition; michael@0: }; michael@0: michael@0: MOZ_ASSERT(location); michael@0: michael@0: nsRefPtr somewhere = new nsGeoPosition(location->latitude, michael@0: location->longitude, michael@0: location->altitude, michael@0: location->accuracy, michael@0: location->accuracy, michael@0: location->bearing, michael@0: location->speed, michael@0: location->timestamp); michael@0: NS_DispatchToMainThread(new UpdateLocationEvent(somewhere)); michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status) michael@0: { michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::SvStatusCallback(GpsSvStatus* sv_info) michael@0: { michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::NmeaCallback(GpsUtcTime timestamp, const char* nmea, int length) michael@0: { michael@0: if (gGPSDebugging) { michael@0: nsContentUtils::LogMessageToConsole("NMEA: timestamp:\t%lld", timestamp); michael@0: nsContentUtils::LogMessageToConsole("NMEA: nmea: \t%s", nmea); michael@0: nsContentUtils::LogMessageToConsole("NMEA length: \%d", length); michael@0: } michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::SetCapabilitiesCallback(uint32_t capabilities) michael@0: { michael@0: class UpdateCapabilitiesEvent : public nsRunnable { michael@0: public: michael@0: UpdateCapabilitiesEvent(uint32_t aCapabilities) michael@0: : mCapabilities(aCapabilities) michael@0: {} michael@0: NS_IMETHOD Run() { michael@0: nsRefPtr provider = michael@0: GonkGPSGeolocationProvider::GetSingleton(); michael@0: michael@0: provider->mSupportsScheduling = mCapabilities & GPS_CAPABILITY_SCHEDULING; michael@0: #ifdef MOZ_B2G_RIL michael@0: provider->mSupportsMSB = mCapabilities & GPS_CAPABILITY_MSB; michael@0: provider->mSupportsMSA = mCapabilities & GPS_CAPABILITY_MSA; michael@0: #endif michael@0: provider->mSupportsSingleShot = mCapabilities & GPS_CAPABILITY_SINGLE_SHOT; michael@0: #ifdef GPS_CAPABILITY_ON_DEMAND_TIME michael@0: provider->mSupportsTimeInjection = mCapabilities & GPS_CAPABILITY_ON_DEMAND_TIME; michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: uint32_t mCapabilities; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new UpdateCapabilitiesEvent(capabilities)); michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::AcquireWakelockCallback() michael@0: { michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::ReleaseWakelockCallback() michael@0: { michael@0: } michael@0: michael@0: typedef void *(*pthread_func)(void *); michael@0: michael@0: /** Callback for creating a thread that can call into the JS codes. michael@0: */ michael@0: pthread_t michael@0: GonkGPSGeolocationProvider::CreateThreadCallback(const char* name, void (*start)(void *), void* arg) michael@0: { michael@0: pthread_t thread; michael@0: pthread_attr_t attr; michael@0: michael@0: pthread_attr_init(&attr); michael@0: michael@0: /* Unfortunately pthread_create and the callback disagreed on what michael@0: * start function should return. michael@0: */ michael@0: pthread_create(&thread, &attr, reinterpret_cast(start), arg); michael@0: michael@0: return thread; michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::RequestUtcTimeCallback() michael@0: { michael@0: } michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: void michael@0: GonkGPSGeolocationProvider::AGPSStatusCallback(AGpsStatus* status) michael@0: { michael@0: MOZ_ASSERT(status); michael@0: michael@0: class AGPSStatusEvent : public nsRunnable { michael@0: public: michael@0: AGPSStatusEvent(AGpsStatusValue aStatus) michael@0: : mStatus(aStatus) michael@0: {} michael@0: NS_IMETHOD Run() { michael@0: nsRefPtr provider = michael@0: GonkGPSGeolocationProvider::GetSingleton(); michael@0: michael@0: switch (mStatus) { michael@0: case GPS_REQUEST_AGPS_DATA_CONN: michael@0: provider->RequestDataConnection(); michael@0: break; michael@0: case GPS_RELEASE_AGPS_DATA_CONN: michael@0: provider->ReleaseDataConnection(); michael@0: break; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: AGpsStatusValue mStatus; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new AGPSStatusEvent(status->status)); michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::AGPSRILSetIDCallback(uint32_t flags) michael@0: { michael@0: class RequestSetIDEvent : public nsRunnable { michael@0: public: michael@0: RequestSetIDEvent(uint32_t flags) michael@0: : mFlags(flags) michael@0: {} michael@0: NS_IMETHOD Run() { michael@0: nsRefPtr provider = michael@0: GonkGPSGeolocationProvider::GetSingleton(); michael@0: provider->RequestSetID(mFlags); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: uint32_t mFlags; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new RequestSetIDEvent(flags)); michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::AGPSRILRefLocCallback(uint32_t flags) michael@0: { michael@0: class RequestRefLocEvent : public nsRunnable { michael@0: public: michael@0: RequestRefLocEvent() michael@0: {} michael@0: NS_IMETHOD Run() { michael@0: nsRefPtr provider = michael@0: GonkGPSGeolocationProvider::GetSingleton(); michael@0: provider->SetReferenceLocation(); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: if (flags & AGPS_RIL_REQUEST_REFLOC_CELLID) { michael@0: NS_DispatchToMainThread(new RequestRefLocEvent()); michael@0: } michael@0: } michael@0: #endif // MOZ_B2G_RIL michael@0: michael@0: GonkGPSGeolocationProvider::GonkGPSGeolocationProvider() michael@0: : mStarted(false) michael@0: , mSupportsScheduling(false) michael@0: #ifdef MOZ_B2G_RIL michael@0: , mSupportsMSB(false) michael@0: , mSupportsMSA(false) michael@0: #endif michael@0: , mSupportsSingleShot(false) michael@0: , mSupportsTimeInjection(false) michael@0: , mGpsInterface(nullptr) michael@0: { michael@0: } michael@0: michael@0: GonkGPSGeolocationProvider::~GonkGPSGeolocationProvider() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!mStarted, "Must call Shutdown before destruction"); michael@0: michael@0: sSingleton = nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: GonkGPSGeolocationProvider::GetSingleton() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!sSingleton) michael@0: sSingleton = new GonkGPSGeolocationProvider(); michael@0: michael@0: nsRefPtr provider = sSingleton; michael@0: return provider.forget(); michael@0: } michael@0: michael@0: const GpsInterface* michael@0: GonkGPSGeolocationProvider::GetGPSInterface() michael@0: { michael@0: hw_module_t* module; michael@0: michael@0: if (hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module)) michael@0: return nullptr; michael@0: michael@0: hw_device_t* device; michael@0: if (module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device)) michael@0: return nullptr; michael@0: michael@0: gps_device_t* gps_device = (gps_device_t *)device; michael@0: const GpsInterface* result = gps_device->get_gps_interface(gps_device); michael@0: michael@0: if (result->size != sizeof(GpsInterface)) { michael@0: return nullptr; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: int32_t michael@0: GonkGPSGeolocationProvider::GetDataConnectionState() michael@0: { michael@0: if (!mRadioInterface) { michael@0: return nsINetworkInterface::NETWORK_STATE_UNKNOWN; michael@0: } michael@0: michael@0: int32_t state; michael@0: mRadioInterface->GetDataCallStateByType(NS_LITERAL_STRING("supl"), &state); michael@0: return state; michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::SetAGpsDataConn(nsAString& aApn) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mAGpsInterface); michael@0: michael@0: int32_t connectionState = GetDataConnectionState(); michael@0: if (connectionState == nsINetworkInterface::NETWORK_STATE_CONNECTED) { michael@0: NS_ConvertUTF16toUTF8 apn(aApn); michael@0: #ifdef AGPS_HAVE_DUAL_APN michael@0: mAGpsInterface->data_conn_open(AGPS_TYPE_SUPL, michael@0: apn.get(), michael@0: AGPS_APN_BEARER_IPV4); michael@0: #else michael@0: mAGpsInterface->data_conn_open(apn.get()); michael@0: #endif michael@0: } else if (connectionState == nsINetworkInterface::NETWORK_STATE_DISCONNECTED) { michael@0: #ifdef AGPS_HAVE_DUAL_APN michael@0: mAGpsInterface->data_conn_closed(AGPS_TYPE_SUPL); michael@0: #else michael@0: mAGpsInterface->data_conn_closed(); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: #endif // MOZ_B2G_RIL michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::RequestSettingValue(char* aKey) michael@0: { michael@0: MOZ_ASSERT(aKey); michael@0: nsCOMPtr ss = do_GetService("@mozilla.org/settingsService;1"); michael@0: if (!ss) { michael@0: MOZ_ASSERT(ss); michael@0: return; michael@0: } michael@0: nsCOMPtr lock; michael@0: ss->CreateLock(nullptr, getter_AddRefs(lock)); michael@0: lock->Get(aKey, this); michael@0: } michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: void michael@0: GonkGPSGeolocationProvider::RequestDataConnection() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mRadioInterface) { michael@0: return; michael@0: } michael@0: michael@0: if (GetDataConnectionState() == nsINetworkInterface::NETWORK_STATE_CONNECTED) { michael@0: // Connection is already established, we don't need to setup again. michael@0: // We just get supl APN and make AGPS data connection state updated. michael@0: RequestSettingValue("ril.supl.apn"); michael@0: } else { michael@0: mRadioInterface->SetupDataCallByType(NS_LITERAL_STRING("supl")); michael@0: } michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::ReleaseDataConnection() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mRadioInterface) { michael@0: return; michael@0: } michael@0: michael@0: mRadioInterface->DeactivateDataCallByType(NS_LITERAL_STRING("supl")); michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::RequestSetID(uint32_t flags) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mRadioInterface) { michael@0: return; michael@0: } michael@0: michael@0: AGpsSetIDType type = AGPS_SETID_TYPE_NONE; michael@0: michael@0: nsCOMPtr rilCtx; michael@0: mRadioInterface->GetRilContext(getter_AddRefs(rilCtx)); michael@0: michael@0: if (rilCtx) { michael@0: nsAutoString id; michael@0: if (flags & AGPS_RIL_REQUEST_SETID_IMSI) { michael@0: type = AGPS_SETID_TYPE_IMSI; michael@0: rilCtx->GetImsi(id); michael@0: } michael@0: michael@0: if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) { michael@0: nsCOMPtr iccInfo; michael@0: rilCtx->GetIccInfo(getter_AddRefs(iccInfo)); michael@0: if (iccInfo) { michael@0: nsCOMPtr gsmIccInfo = do_QueryInterface(iccInfo); michael@0: if (gsmIccInfo) { michael@0: type = AGPS_SETID_TYPE_MSISDN; michael@0: gsmIccInfo->GetMsisdn(id); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_ConvertUTF16toUTF8 idBytes(id); michael@0: mAGpsRilInterface->set_set_id(type, idBytes.get()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::SetReferenceLocation() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mRadioInterface) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr rilCtx; michael@0: mRadioInterface->GetRilContext(getter_AddRefs(rilCtx)); michael@0: michael@0: AGpsRefLocation location; michael@0: michael@0: // TODO: Bug 772750 - get mobile connection technology from rilcontext michael@0: location.type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID; michael@0: michael@0: if (rilCtx) { michael@0: nsCOMPtr iccInfo; michael@0: rilCtx->GetIccInfo(getter_AddRefs(iccInfo)); michael@0: if (iccInfo) { michael@0: nsresult result; michael@0: nsAutoString mcc, mnc; michael@0: michael@0: iccInfo->GetMcc(mcc); michael@0: iccInfo->GetMnc(mnc); michael@0: michael@0: location.u.cellID.mcc = mcc.ToInteger(&result); michael@0: if (result != NS_OK) { michael@0: NS_WARNING("Cannot parse mcc to integer"); michael@0: location.u.cellID.mcc = 0; michael@0: } michael@0: michael@0: location.u.cellID.mnc = mnc.ToInteger(&result); michael@0: if (result != NS_OK) { michael@0: NS_WARNING("Cannot parse mnc to integer"); michael@0: location.u.cellID.mnc = 0; michael@0: } michael@0: } michael@0: nsCOMPtr voice; michael@0: rilCtx->GetVoice(getter_AddRefs(voice)); michael@0: if (voice) { michael@0: nsCOMPtr cell; michael@0: voice->GetCell(getter_AddRefs(cell)); michael@0: if (cell) { michael@0: int32_t lac; michael@0: int64_t cid; michael@0: michael@0: cell->GetGsmLocationAreaCode(&lac); michael@0: // The valid range of LAC is 0x0 to 0xffff which is defined in michael@0: // hardware/ril/include/telephony/ril.h michael@0: if (lac >= 0x0 && lac <= 0xffff) { michael@0: location.u.cellID.lac = lac; michael@0: } michael@0: michael@0: cell->GetGsmCellId(&cid); michael@0: // The valid range of cell id is 0x0 to 0xffffffff which is defined in michael@0: // hardware/ril/include/telephony/ril.h michael@0: if (cid >= 0x0 && cid <= 0xffffffff) { michael@0: location.u.cellID.cid = cid; michael@0: } michael@0: } michael@0: } michael@0: if (mAGpsRilInterface) { michael@0: mAGpsRilInterface->set_ref_location(&location, sizeof(location)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: #endif // MOZ_B2G_RIL michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::InjectLocation(double latitude, michael@0: double longitude, michael@0: float accuracy) michael@0: { michael@0: if (gGPSDebugging) { michael@0: nsContentUtils::LogMessageToConsole("*** injecting location"); michael@0: nsContentUtils::LogMessageToConsole("*** lat: %f", latitude); michael@0: nsContentUtils::LogMessageToConsole("*** lon: %f", longitude); michael@0: nsContentUtils::LogMessageToConsole("*** accuracy: %f", accuracy); michael@0: } michael@0: michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (!mGpsInterface) { michael@0: return; michael@0: } michael@0: michael@0: mGpsInterface->inject_location(latitude, longitude, accuracy); michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::Init() michael@0: { michael@0: // Must not be main thread. Some GPS driver's first init takes very long. michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: mGpsInterface = GetGPSInterface(); michael@0: if (!mGpsInterface) { michael@0: return; michael@0: } michael@0: michael@0: if (mGpsInterface->init(&mCallbacks) != 0) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: mAGpsInterface = michael@0: static_cast(mGpsInterface->get_extension(AGPS_INTERFACE)); michael@0: if (mAGpsInterface) { michael@0: mAGpsInterface->init(&mAGPSCallbacks); michael@0: } michael@0: michael@0: mAGpsRilInterface = michael@0: static_cast(mGpsInterface->get_extension(AGPS_RIL_INTERFACE)); michael@0: if (mAGpsRilInterface) { michael@0: mAGpsRilInterface->init(&mAGPSRILCallbacks); michael@0: } michael@0: #endif michael@0: michael@0: NS_DispatchToMainThread(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::StartGPS)); michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::StartGPS() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mGpsInterface); michael@0: michael@0: int32_t update = Preferences::GetInt("geo.default.update", kDefaultPeriod); michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: if (mSupportsMSA || mSupportsMSB) { michael@0: SetupAGPS(); michael@0: } michael@0: #endif michael@0: michael@0: int positionMode = GPS_POSITION_MODE_STANDALONE; michael@0: bool singleShot = false; michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: // XXX: If we know this is a single shot request, use MSA can be faster. michael@0: if (singleShot && mSupportsMSA) { michael@0: positionMode = GPS_POSITION_MODE_MS_ASSISTED; michael@0: } else if (mSupportsMSB) { michael@0: positionMode = GPS_POSITION_MODE_MS_BASED; michael@0: } michael@0: #endif michael@0: if (!mSupportsScheduling) { michael@0: update = kDefaultPeriod; michael@0: } michael@0: michael@0: mGpsInterface->set_position_mode(positionMode, michael@0: GPS_POSITION_RECURRENCE_PERIODIC, michael@0: update, 0, 0); michael@0: #if FLUSH_AIDE_DATA michael@0: // Delete cached data michael@0: mGpsInterface->delete_aiding_data(GPS_DELETE_ALL); michael@0: #endif michael@0: michael@0: mGpsInterface->start(); michael@0: } michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: void michael@0: GonkGPSGeolocationProvider::SetupAGPS() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mAGpsInterface); michael@0: michael@0: const nsAdoptingCString& suplServer = Preferences::GetCString("geo.gps.supl_server"); michael@0: int32_t suplPort = Preferences::GetInt("geo.gps.supl_port", -1); michael@0: if (!suplServer.IsEmpty() && suplPort > 0) { michael@0: mAGpsInterface->set_server(AGPS_TYPE_SUPL, suplServer.get(), suplPort); michael@0: } else { michael@0: NS_WARNING("Cannot get SUPL server settings"); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (obs) { michael@0: obs->AddObserver(this, kNetworkConnStateChangedTopic, false); michael@0: } michael@0: michael@0: nsCOMPtr ril = do_GetService("@mozilla.org/ril;1"); michael@0: if (ril) { michael@0: // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in michael@0: // MultiSIM configuration michael@0: ril->GetRadioInterface(0 /* clientId */, getter_AddRefs(mRadioInterface)); michael@0: } michael@0: } michael@0: #endif // MOZ_B2G_RIL michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider::NetworkLocationUpdate, michael@0: nsIGeolocationUpdate) michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::NetworkLocationUpdate::Update(nsIDOMGeoPosition *position) michael@0: { michael@0: nsRefPtr provider = michael@0: GonkGPSGeolocationProvider::GetSingleton(); michael@0: michael@0: nsCOMPtr coords; michael@0: position->GetCoords(getter_AddRefs(coords)); michael@0: if (!coords) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // if we haven't seen anything from the GPS device for 1s, michael@0: // use this network derived location. michael@0: int64_t diff = PR_Now() - provider->mLastGPSDerivedLocationTime; michael@0: if (provider->mLocationCallback && diff > kDefaultPeriod) { michael@0: provider->mLocationCallback->Update(position); michael@0: } michael@0: michael@0: double lat, lon, acc; michael@0: coords->GetLatitude(&lat); michael@0: coords->GetLongitude(&lon); michael@0: coords->GetAccuracy(&acc); michael@0: provider->InjectLocation(lat, lon, acc); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::NetworkLocationUpdate::LocationUpdatePending() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::NetworkLocationUpdate::NotifyError(uint16_t error) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::Startup() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: RequestSettingValue(SETTING_DEBUG_ENABLED); michael@0: if (mStarted) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mInitThread) { michael@0: nsresult rv = NS_NewThread(getter_AddRefs(mInitThread)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: mInitThread->Dispatch(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::Init), michael@0: NS_DISPATCH_NORMAL); michael@0: michael@0: mNetworkLocationProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1"); michael@0: if (mNetworkLocationProvider) { michael@0: nsresult rv = mNetworkLocationProvider->Startup(); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsRefPtr update = new NetworkLocationUpdate(); michael@0: mNetworkLocationProvider->Watch(update); michael@0: } michael@0: } michael@0: michael@0: mLastGPSDerivedLocationTime = 0; michael@0: mStarted = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mLocationCallback = aCallback; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::Shutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mStarted) { michael@0: return NS_OK; michael@0: } michael@0: mStarted = false; michael@0: if (mNetworkLocationProvider) { michael@0: mNetworkLocationProvider->Shutdown(); michael@0: mNetworkLocationProvider = nullptr; michael@0: } michael@0: #ifdef MOZ_B2G_RIL michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (obs) { michael@0: obs->RemoveObserver(this, kNetworkConnStateChangedTopic); michael@0: } michael@0: #endif michael@0: michael@0: mInitThread->Dispatch(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::ShutdownGPS), michael@0: NS_DISPATCH_NORMAL); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: GonkGPSGeolocationProvider::ShutdownGPS() michael@0: { michael@0: MOZ_ASSERT(!mStarted, "Should only be called after Shutdown"); michael@0: michael@0: if (mGpsInterface) { michael@0: mGpsInterface->stop(); michael@0: mGpsInterface->cleanup(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::SetHighAccuracy(bool) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: if (!strcmp(aTopic, kNetworkConnStateChangedTopic)) { michael@0: nsCOMPtr iface = do_QueryInterface(aSubject); michael@0: if (!iface) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: RequestSettingValue("ril.supl.apn"); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** nsISettingsServiceCallback **/ michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::Handle(const nsAString& aName, michael@0: JS::Handle aResult) michael@0: { michael@0: #ifdef MOZ_B2G_RIL michael@0: if (aName.EqualsLiteral("ril.supl.apn")) { michael@0: // When we get the APN, we attempt to call data_call_open of AGPS. michael@0: if (aResult.isString()) { michael@0: JSContext *cx = nsContentUtils::GetCurrentJSContext(); michael@0: NS_ENSURE_TRUE(cx, NS_OK); michael@0: michael@0: // NB: No need to enter a compartment to read the contents of a string. michael@0: nsDependentJSString apn; michael@0: apn.init(cx, aResult.toString()); michael@0: if (!apn.IsEmpty()) { michael@0: SetAGpsDataConn(apn); michael@0: } michael@0: } michael@0: } else michael@0: #endif // MOZ_B2G_RIL michael@0: if (aName.EqualsLiteral(SETTING_DEBUG_ENABLED)) { michael@0: gGPSDebugging = aResult.isBoolean() ? aResult.toBoolean() : false; michael@0: return NS_OK; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: GonkGPSGeolocationProvider::HandleError(const nsAString& aErrorMessage) michael@0: { michael@0: return NS_OK; michael@0: }