|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* Copyright 2012 Mozilla Foundation and Mozilla contributors |
|
3 * |
|
4 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 * you may not use this file except in compliance with the License. |
|
6 * You may obtain a copy of the License at |
|
7 * |
|
8 * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 * |
|
10 * Unless required by applicable law or agreed to in writing, software |
|
11 * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 * See the License for the specific language governing permissions and |
|
14 * limitations under the License. |
|
15 */ |
|
16 |
|
17 #include <pthread.h> |
|
18 #include <hardware/gps.h> |
|
19 |
|
20 #include "GonkGPSGeolocationProvider.h" |
|
21 #include "mozilla/Preferences.h" |
|
22 #include "mozilla/Services.h" |
|
23 #include "nsGeoPosition.h" |
|
24 #include "nsIInterfaceRequestorUtils.h" |
|
25 #include "nsINetworkManager.h" |
|
26 #include "nsIObserverService.h" |
|
27 #include "nsJSUtils.h" |
|
28 #include "nsServiceManagerUtils.h" |
|
29 #include "nsThreadUtils.h" |
|
30 #include "nsContentUtils.h" |
|
31 #include "prtime.h" |
|
32 |
|
33 #ifdef MOZ_B2G_RIL |
|
34 #include "nsIDOMIccInfo.h" |
|
35 #include "nsIDOMMobileConnection.h" |
|
36 #include "nsIRadioInterfaceLayer.h" |
|
37 #endif |
|
38 |
|
39 #define SETTING_DEBUG_ENABLED "geolocation.debugging.enabled" |
|
40 |
|
41 #ifdef AGPS_TYPE_INVALID |
|
42 #define AGPS_HAVE_DUAL_APN |
|
43 #endif |
|
44 |
|
45 #define FLUSH_AIDE_DATA 0 |
|
46 |
|
47 using namespace mozilla; |
|
48 |
|
49 static const int kDefaultPeriod = 1000; // ms |
|
50 static int gGPSDebugging = false; |
|
51 |
|
52 static const char* kNetworkConnStateChangedTopic = "network-connection-state-changed"; |
|
53 |
|
54 // While most methods of GonkGPSGeolocationProvider should only be |
|
55 // called from main thread, we deliberately put the Init and ShutdownGPS |
|
56 // methods off main thread to avoid blocking. |
|
57 NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider, |
|
58 nsIGeolocationProvider, |
|
59 nsIObserver, |
|
60 nsISettingsServiceCallback) |
|
61 |
|
62 /* static */ GonkGPSGeolocationProvider* GonkGPSGeolocationProvider::sSingleton = nullptr; |
|
63 GpsCallbacks GonkGPSGeolocationProvider::mCallbacks = { |
|
64 sizeof(GpsCallbacks), |
|
65 LocationCallback, |
|
66 StatusCallback, |
|
67 SvStatusCallback, |
|
68 NmeaCallback, |
|
69 SetCapabilitiesCallback, |
|
70 AcquireWakelockCallback, |
|
71 ReleaseWakelockCallback, |
|
72 CreateThreadCallback, |
|
73 #ifdef GPS_CAPABILITY_ON_DEMAND_TIME |
|
74 RequestUtcTimeCallback, |
|
75 #endif |
|
76 }; |
|
77 |
|
78 #ifdef MOZ_B2G_RIL |
|
79 AGpsCallbacks |
|
80 GonkGPSGeolocationProvider::mAGPSCallbacks = { |
|
81 AGPSStatusCallback, |
|
82 CreateThreadCallback, |
|
83 }; |
|
84 |
|
85 AGpsRilCallbacks |
|
86 GonkGPSGeolocationProvider::mAGPSRILCallbacks = { |
|
87 AGPSRILSetIDCallback, |
|
88 AGPSRILRefLocCallback, |
|
89 CreateThreadCallback, |
|
90 }; |
|
91 #endif // MOZ_B2G_RIL |
|
92 |
|
93 void |
|
94 GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location) |
|
95 { |
|
96 class UpdateLocationEvent : public nsRunnable { |
|
97 public: |
|
98 UpdateLocationEvent(nsGeoPosition* aPosition) |
|
99 : mPosition(aPosition) |
|
100 {} |
|
101 NS_IMETHOD Run() { |
|
102 nsRefPtr<GonkGPSGeolocationProvider> provider = |
|
103 GonkGPSGeolocationProvider::GetSingleton(); |
|
104 provider->mLastGPSDerivedLocationTime = PR_Now(); |
|
105 nsCOMPtr<nsIGeolocationUpdate> callback = provider->mLocationCallback; |
|
106 if (callback) { |
|
107 callback->Update(mPosition); |
|
108 } |
|
109 return NS_OK; |
|
110 } |
|
111 private: |
|
112 nsRefPtr<nsGeoPosition> mPosition; |
|
113 }; |
|
114 |
|
115 MOZ_ASSERT(location); |
|
116 |
|
117 nsRefPtr<nsGeoPosition> somewhere = new nsGeoPosition(location->latitude, |
|
118 location->longitude, |
|
119 location->altitude, |
|
120 location->accuracy, |
|
121 location->accuracy, |
|
122 location->bearing, |
|
123 location->speed, |
|
124 location->timestamp); |
|
125 NS_DispatchToMainThread(new UpdateLocationEvent(somewhere)); |
|
126 } |
|
127 |
|
128 void |
|
129 GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status) |
|
130 { |
|
131 } |
|
132 |
|
133 void |
|
134 GonkGPSGeolocationProvider::SvStatusCallback(GpsSvStatus* sv_info) |
|
135 { |
|
136 } |
|
137 |
|
138 void |
|
139 GonkGPSGeolocationProvider::NmeaCallback(GpsUtcTime timestamp, const char* nmea, int length) |
|
140 { |
|
141 if (gGPSDebugging) { |
|
142 nsContentUtils::LogMessageToConsole("NMEA: timestamp:\t%lld", timestamp); |
|
143 nsContentUtils::LogMessageToConsole("NMEA: nmea: \t%s", nmea); |
|
144 nsContentUtils::LogMessageToConsole("NMEA length: \%d", length); |
|
145 } |
|
146 } |
|
147 |
|
148 void |
|
149 GonkGPSGeolocationProvider::SetCapabilitiesCallback(uint32_t capabilities) |
|
150 { |
|
151 class UpdateCapabilitiesEvent : public nsRunnable { |
|
152 public: |
|
153 UpdateCapabilitiesEvent(uint32_t aCapabilities) |
|
154 : mCapabilities(aCapabilities) |
|
155 {} |
|
156 NS_IMETHOD Run() { |
|
157 nsRefPtr<GonkGPSGeolocationProvider> provider = |
|
158 GonkGPSGeolocationProvider::GetSingleton(); |
|
159 |
|
160 provider->mSupportsScheduling = mCapabilities & GPS_CAPABILITY_SCHEDULING; |
|
161 #ifdef MOZ_B2G_RIL |
|
162 provider->mSupportsMSB = mCapabilities & GPS_CAPABILITY_MSB; |
|
163 provider->mSupportsMSA = mCapabilities & GPS_CAPABILITY_MSA; |
|
164 #endif |
|
165 provider->mSupportsSingleShot = mCapabilities & GPS_CAPABILITY_SINGLE_SHOT; |
|
166 #ifdef GPS_CAPABILITY_ON_DEMAND_TIME |
|
167 provider->mSupportsTimeInjection = mCapabilities & GPS_CAPABILITY_ON_DEMAND_TIME; |
|
168 #endif |
|
169 return NS_OK; |
|
170 } |
|
171 private: |
|
172 uint32_t mCapabilities; |
|
173 }; |
|
174 |
|
175 NS_DispatchToMainThread(new UpdateCapabilitiesEvent(capabilities)); |
|
176 } |
|
177 |
|
178 void |
|
179 GonkGPSGeolocationProvider::AcquireWakelockCallback() |
|
180 { |
|
181 } |
|
182 |
|
183 void |
|
184 GonkGPSGeolocationProvider::ReleaseWakelockCallback() |
|
185 { |
|
186 } |
|
187 |
|
188 typedef void *(*pthread_func)(void *); |
|
189 |
|
190 /** Callback for creating a thread that can call into the JS codes. |
|
191 */ |
|
192 pthread_t |
|
193 GonkGPSGeolocationProvider::CreateThreadCallback(const char* name, void (*start)(void *), void* arg) |
|
194 { |
|
195 pthread_t thread; |
|
196 pthread_attr_t attr; |
|
197 |
|
198 pthread_attr_init(&attr); |
|
199 |
|
200 /* Unfortunately pthread_create and the callback disagreed on what |
|
201 * start function should return. |
|
202 */ |
|
203 pthread_create(&thread, &attr, reinterpret_cast<pthread_func>(start), arg); |
|
204 |
|
205 return thread; |
|
206 } |
|
207 |
|
208 void |
|
209 GonkGPSGeolocationProvider::RequestUtcTimeCallback() |
|
210 { |
|
211 } |
|
212 |
|
213 #ifdef MOZ_B2G_RIL |
|
214 void |
|
215 GonkGPSGeolocationProvider::AGPSStatusCallback(AGpsStatus* status) |
|
216 { |
|
217 MOZ_ASSERT(status); |
|
218 |
|
219 class AGPSStatusEvent : public nsRunnable { |
|
220 public: |
|
221 AGPSStatusEvent(AGpsStatusValue aStatus) |
|
222 : mStatus(aStatus) |
|
223 {} |
|
224 NS_IMETHOD Run() { |
|
225 nsRefPtr<GonkGPSGeolocationProvider> provider = |
|
226 GonkGPSGeolocationProvider::GetSingleton(); |
|
227 |
|
228 switch (mStatus) { |
|
229 case GPS_REQUEST_AGPS_DATA_CONN: |
|
230 provider->RequestDataConnection(); |
|
231 break; |
|
232 case GPS_RELEASE_AGPS_DATA_CONN: |
|
233 provider->ReleaseDataConnection(); |
|
234 break; |
|
235 } |
|
236 return NS_OK; |
|
237 } |
|
238 private: |
|
239 AGpsStatusValue mStatus; |
|
240 }; |
|
241 |
|
242 NS_DispatchToMainThread(new AGPSStatusEvent(status->status)); |
|
243 } |
|
244 |
|
245 void |
|
246 GonkGPSGeolocationProvider::AGPSRILSetIDCallback(uint32_t flags) |
|
247 { |
|
248 class RequestSetIDEvent : public nsRunnable { |
|
249 public: |
|
250 RequestSetIDEvent(uint32_t flags) |
|
251 : mFlags(flags) |
|
252 {} |
|
253 NS_IMETHOD Run() { |
|
254 nsRefPtr<GonkGPSGeolocationProvider> provider = |
|
255 GonkGPSGeolocationProvider::GetSingleton(); |
|
256 provider->RequestSetID(mFlags); |
|
257 return NS_OK; |
|
258 } |
|
259 private: |
|
260 uint32_t mFlags; |
|
261 }; |
|
262 |
|
263 NS_DispatchToMainThread(new RequestSetIDEvent(flags)); |
|
264 } |
|
265 |
|
266 void |
|
267 GonkGPSGeolocationProvider::AGPSRILRefLocCallback(uint32_t flags) |
|
268 { |
|
269 class RequestRefLocEvent : public nsRunnable { |
|
270 public: |
|
271 RequestRefLocEvent() |
|
272 {} |
|
273 NS_IMETHOD Run() { |
|
274 nsRefPtr<GonkGPSGeolocationProvider> provider = |
|
275 GonkGPSGeolocationProvider::GetSingleton(); |
|
276 provider->SetReferenceLocation(); |
|
277 return NS_OK; |
|
278 } |
|
279 }; |
|
280 |
|
281 if (flags & AGPS_RIL_REQUEST_REFLOC_CELLID) { |
|
282 NS_DispatchToMainThread(new RequestRefLocEvent()); |
|
283 } |
|
284 } |
|
285 #endif // MOZ_B2G_RIL |
|
286 |
|
287 GonkGPSGeolocationProvider::GonkGPSGeolocationProvider() |
|
288 : mStarted(false) |
|
289 , mSupportsScheduling(false) |
|
290 #ifdef MOZ_B2G_RIL |
|
291 , mSupportsMSB(false) |
|
292 , mSupportsMSA(false) |
|
293 #endif |
|
294 , mSupportsSingleShot(false) |
|
295 , mSupportsTimeInjection(false) |
|
296 , mGpsInterface(nullptr) |
|
297 { |
|
298 } |
|
299 |
|
300 GonkGPSGeolocationProvider::~GonkGPSGeolocationProvider() |
|
301 { |
|
302 MOZ_ASSERT(NS_IsMainThread()); |
|
303 MOZ_ASSERT(!mStarted, "Must call Shutdown before destruction"); |
|
304 |
|
305 sSingleton = nullptr; |
|
306 } |
|
307 |
|
308 already_AddRefed<GonkGPSGeolocationProvider> |
|
309 GonkGPSGeolocationProvider::GetSingleton() |
|
310 { |
|
311 MOZ_ASSERT(NS_IsMainThread()); |
|
312 |
|
313 if (!sSingleton) |
|
314 sSingleton = new GonkGPSGeolocationProvider(); |
|
315 |
|
316 nsRefPtr<GonkGPSGeolocationProvider> provider = sSingleton; |
|
317 return provider.forget(); |
|
318 } |
|
319 |
|
320 const GpsInterface* |
|
321 GonkGPSGeolocationProvider::GetGPSInterface() |
|
322 { |
|
323 hw_module_t* module; |
|
324 |
|
325 if (hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module)) |
|
326 return nullptr; |
|
327 |
|
328 hw_device_t* device; |
|
329 if (module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device)) |
|
330 return nullptr; |
|
331 |
|
332 gps_device_t* gps_device = (gps_device_t *)device; |
|
333 const GpsInterface* result = gps_device->get_gps_interface(gps_device); |
|
334 |
|
335 if (result->size != sizeof(GpsInterface)) { |
|
336 return nullptr; |
|
337 } |
|
338 return result; |
|
339 } |
|
340 |
|
341 #ifdef MOZ_B2G_RIL |
|
342 int32_t |
|
343 GonkGPSGeolocationProvider::GetDataConnectionState() |
|
344 { |
|
345 if (!mRadioInterface) { |
|
346 return nsINetworkInterface::NETWORK_STATE_UNKNOWN; |
|
347 } |
|
348 |
|
349 int32_t state; |
|
350 mRadioInterface->GetDataCallStateByType(NS_LITERAL_STRING("supl"), &state); |
|
351 return state; |
|
352 } |
|
353 |
|
354 void |
|
355 GonkGPSGeolocationProvider::SetAGpsDataConn(nsAString& aApn) |
|
356 { |
|
357 MOZ_ASSERT(NS_IsMainThread()); |
|
358 MOZ_ASSERT(mAGpsInterface); |
|
359 |
|
360 int32_t connectionState = GetDataConnectionState(); |
|
361 if (connectionState == nsINetworkInterface::NETWORK_STATE_CONNECTED) { |
|
362 NS_ConvertUTF16toUTF8 apn(aApn); |
|
363 #ifdef AGPS_HAVE_DUAL_APN |
|
364 mAGpsInterface->data_conn_open(AGPS_TYPE_SUPL, |
|
365 apn.get(), |
|
366 AGPS_APN_BEARER_IPV4); |
|
367 #else |
|
368 mAGpsInterface->data_conn_open(apn.get()); |
|
369 #endif |
|
370 } else if (connectionState == nsINetworkInterface::NETWORK_STATE_DISCONNECTED) { |
|
371 #ifdef AGPS_HAVE_DUAL_APN |
|
372 mAGpsInterface->data_conn_closed(AGPS_TYPE_SUPL); |
|
373 #else |
|
374 mAGpsInterface->data_conn_closed(); |
|
375 #endif |
|
376 } |
|
377 } |
|
378 |
|
379 #endif // MOZ_B2G_RIL |
|
380 |
|
381 void |
|
382 GonkGPSGeolocationProvider::RequestSettingValue(char* aKey) |
|
383 { |
|
384 MOZ_ASSERT(aKey); |
|
385 nsCOMPtr<nsISettingsService> ss = do_GetService("@mozilla.org/settingsService;1"); |
|
386 if (!ss) { |
|
387 MOZ_ASSERT(ss); |
|
388 return; |
|
389 } |
|
390 nsCOMPtr<nsISettingsServiceLock> lock; |
|
391 ss->CreateLock(nullptr, getter_AddRefs(lock)); |
|
392 lock->Get(aKey, this); |
|
393 } |
|
394 |
|
395 #ifdef MOZ_B2G_RIL |
|
396 void |
|
397 GonkGPSGeolocationProvider::RequestDataConnection() |
|
398 { |
|
399 MOZ_ASSERT(NS_IsMainThread()); |
|
400 |
|
401 if (!mRadioInterface) { |
|
402 return; |
|
403 } |
|
404 |
|
405 if (GetDataConnectionState() == nsINetworkInterface::NETWORK_STATE_CONNECTED) { |
|
406 // Connection is already established, we don't need to setup again. |
|
407 // We just get supl APN and make AGPS data connection state updated. |
|
408 RequestSettingValue("ril.supl.apn"); |
|
409 } else { |
|
410 mRadioInterface->SetupDataCallByType(NS_LITERAL_STRING("supl")); |
|
411 } |
|
412 } |
|
413 |
|
414 void |
|
415 GonkGPSGeolocationProvider::ReleaseDataConnection() |
|
416 { |
|
417 MOZ_ASSERT(NS_IsMainThread()); |
|
418 |
|
419 if (!mRadioInterface) { |
|
420 return; |
|
421 } |
|
422 |
|
423 mRadioInterface->DeactivateDataCallByType(NS_LITERAL_STRING("supl")); |
|
424 } |
|
425 |
|
426 void |
|
427 GonkGPSGeolocationProvider::RequestSetID(uint32_t flags) |
|
428 { |
|
429 MOZ_ASSERT(NS_IsMainThread()); |
|
430 |
|
431 if (!mRadioInterface) { |
|
432 return; |
|
433 } |
|
434 |
|
435 AGpsSetIDType type = AGPS_SETID_TYPE_NONE; |
|
436 |
|
437 nsCOMPtr<nsIRilContext> rilCtx; |
|
438 mRadioInterface->GetRilContext(getter_AddRefs(rilCtx)); |
|
439 |
|
440 if (rilCtx) { |
|
441 nsAutoString id; |
|
442 if (flags & AGPS_RIL_REQUEST_SETID_IMSI) { |
|
443 type = AGPS_SETID_TYPE_IMSI; |
|
444 rilCtx->GetImsi(id); |
|
445 } |
|
446 |
|
447 if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) { |
|
448 nsCOMPtr<nsIDOMMozIccInfo> iccInfo; |
|
449 rilCtx->GetIccInfo(getter_AddRefs(iccInfo)); |
|
450 if (iccInfo) { |
|
451 nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo); |
|
452 if (gsmIccInfo) { |
|
453 type = AGPS_SETID_TYPE_MSISDN; |
|
454 gsmIccInfo->GetMsisdn(id); |
|
455 } |
|
456 } |
|
457 } |
|
458 |
|
459 NS_ConvertUTF16toUTF8 idBytes(id); |
|
460 mAGpsRilInterface->set_set_id(type, idBytes.get()); |
|
461 } |
|
462 } |
|
463 |
|
464 void |
|
465 GonkGPSGeolocationProvider::SetReferenceLocation() |
|
466 { |
|
467 MOZ_ASSERT(NS_IsMainThread()); |
|
468 |
|
469 if (!mRadioInterface) { |
|
470 return; |
|
471 } |
|
472 |
|
473 nsCOMPtr<nsIRilContext> rilCtx; |
|
474 mRadioInterface->GetRilContext(getter_AddRefs(rilCtx)); |
|
475 |
|
476 AGpsRefLocation location; |
|
477 |
|
478 // TODO: Bug 772750 - get mobile connection technology from rilcontext |
|
479 location.type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID; |
|
480 |
|
481 if (rilCtx) { |
|
482 nsCOMPtr<nsIDOMMozIccInfo> iccInfo; |
|
483 rilCtx->GetIccInfo(getter_AddRefs(iccInfo)); |
|
484 if (iccInfo) { |
|
485 nsresult result; |
|
486 nsAutoString mcc, mnc; |
|
487 |
|
488 iccInfo->GetMcc(mcc); |
|
489 iccInfo->GetMnc(mnc); |
|
490 |
|
491 location.u.cellID.mcc = mcc.ToInteger(&result); |
|
492 if (result != NS_OK) { |
|
493 NS_WARNING("Cannot parse mcc to integer"); |
|
494 location.u.cellID.mcc = 0; |
|
495 } |
|
496 |
|
497 location.u.cellID.mnc = mnc.ToInteger(&result); |
|
498 if (result != NS_OK) { |
|
499 NS_WARNING("Cannot parse mnc to integer"); |
|
500 location.u.cellID.mnc = 0; |
|
501 } |
|
502 } |
|
503 nsCOMPtr<nsIDOMMozMobileConnectionInfo> voice; |
|
504 rilCtx->GetVoice(getter_AddRefs(voice)); |
|
505 if (voice) { |
|
506 nsCOMPtr<nsIDOMMozMobileCellInfo> cell; |
|
507 voice->GetCell(getter_AddRefs(cell)); |
|
508 if (cell) { |
|
509 int32_t lac; |
|
510 int64_t cid; |
|
511 |
|
512 cell->GetGsmLocationAreaCode(&lac); |
|
513 // The valid range of LAC is 0x0 to 0xffff which is defined in |
|
514 // hardware/ril/include/telephony/ril.h |
|
515 if (lac >= 0x0 && lac <= 0xffff) { |
|
516 location.u.cellID.lac = lac; |
|
517 } |
|
518 |
|
519 cell->GetGsmCellId(&cid); |
|
520 // The valid range of cell id is 0x0 to 0xffffffff which is defined in |
|
521 // hardware/ril/include/telephony/ril.h |
|
522 if (cid >= 0x0 && cid <= 0xffffffff) { |
|
523 location.u.cellID.cid = cid; |
|
524 } |
|
525 } |
|
526 } |
|
527 if (mAGpsRilInterface) { |
|
528 mAGpsRilInterface->set_ref_location(&location, sizeof(location)); |
|
529 } |
|
530 } |
|
531 } |
|
532 |
|
533 #endif // MOZ_B2G_RIL |
|
534 |
|
535 void |
|
536 GonkGPSGeolocationProvider::InjectLocation(double latitude, |
|
537 double longitude, |
|
538 float accuracy) |
|
539 { |
|
540 if (gGPSDebugging) { |
|
541 nsContentUtils::LogMessageToConsole("*** injecting location"); |
|
542 nsContentUtils::LogMessageToConsole("*** lat: %f", latitude); |
|
543 nsContentUtils::LogMessageToConsole("*** lon: %f", longitude); |
|
544 nsContentUtils::LogMessageToConsole("*** accuracy: %f", accuracy); |
|
545 } |
|
546 |
|
547 MOZ_ASSERT(NS_IsMainThread()); |
|
548 if (!mGpsInterface) { |
|
549 return; |
|
550 } |
|
551 |
|
552 mGpsInterface->inject_location(latitude, longitude, accuracy); |
|
553 } |
|
554 |
|
555 void |
|
556 GonkGPSGeolocationProvider::Init() |
|
557 { |
|
558 // Must not be main thread. Some GPS driver's first init takes very long. |
|
559 MOZ_ASSERT(!NS_IsMainThread()); |
|
560 |
|
561 mGpsInterface = GetGPSInterface(); |
|
562 if (!mGpsInterface) { |
|
563 return; |
|
564 } |
|
565 |
|
566 if (mGpsInterface->init(&mCallbacks) != 0) { |
|
567 return; |
|
568 } |
|
569 |
|
570 #ifdef MOZ_B2G_RIL |
|
571 mAGpsInterface = |
|
572 static_cast<const AGpsInterface*>(mGpsInterface->get_extension(AGPS_INTERFACE)); |
|
573 if (mAGpsInterface) { |
|
574 mAGpsInterface->init(&mAGPSCallbacks); |
|
575 } |
|
576 |
|
577 mAGpsRilInterface = |
|
578 static_cast<const AGpsRilInterface*>(mGpsInterface->get_extension(AGPS_RIL_INTERFACE)); |
|
579 if (mAGpsRilInterface) { |
|
580 mAGpsRilInterface->init(&mAGPSRILCallbacks); |
|
581 } |
|
582 #endif |
|
583 |
|
584 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::StartGPS)); |
|
585 } |
|
586 |
|
587 void |
|
588 GonkGPSGeolocationProvider::StartGPS() |
|
589 { |
|
590 MOZ_ASSERT(NS_IsMainThread()); |
|
591 MOZ_ASSERT(mGpsInterface); |
|
592 |
|
593 int32_t update = Preferences::GetInt("geo.default.update", kDefaultPeriod); |
|
594 |
|
595 #ifdef MOZ_B2G_RIL |
|
596 if (mSupportsMSA || mSupportsMSB) { |
|
597 SetupAGPS(); |
|
598 } |
|
599 #endif |
|
600 |
|
601 int positionMode = GPS_POSITION_MODE_STANDALONE; |
|
602 bool singleShot = false; |
|
603 |
|
604 #ifdef MOZ_B2G_RIL |
|
605 // XXX: If we know this is a single shot request, use MSA can be faster. |
|
606 if (singleShot && mSupportsMSA) { |
|
607 positionMode = GPS_POSITION_MODE_MS_ASSISTED; |
|
608 } else if (mSupportsMSB) { |
|
609 positionMode = GPS_POSITION_MODE_MS_BASED; |
|
610 } |
|
611 #endif |
|
612 if (!mSupportsScheduling) { |
|
613 update = kDefaultPeriod; |
|
614 } |
|
615 |
|
616 mGpsInterface->set_position_mode(positionMode, |
|
617 GPS_POSITION_RECURRENCE_PERIODIC, |
|
618 update, 0, 0); |
|
619 #if FLUSH_AIDE_DATA |
|
620 // Delete cached data |
|
621 mGpsInterface->delete_aiding_data(GPS_DELETE_ALL); |
|
622 #endif |
|
623 |
|
624 mGpsInterface->start(); |
|
625 } |
|
626 |
|
627 #ifdef MOZ_B2G_RIL |
|
628 void |
|
629 GonkGPSGeolocationProvider::SetupAGPS() |
|
630 { |
|
631 MOZ_ASSERT(NS_IsMainThread()); |
|
632 MOZ_ASSERT(mAGpsInterface); |
|
633 |
|
634 const nsAdoptingCString& suplServer = Preferences::GetCString("geo.gps.supl_server"); |
|
635 int32_t suplPort = Preferences::GetInt("geo.gps.supl_port", -1); |
|
636 if (!suplServer.IsEmpty() && suplPort > 0) { |
|
637 mAGpsInterface->set_server(AGPS_TYPE_SUPL, suplServer.get(), suplPort); |
|
638 } else { |
|
639 NS_WARNING("Cannot get SUPL server settings"); |
|
640 return; |
|
641 } |
|
642 |
|
643 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
|
644 if (obs) { |
|
645 obs->AddObserver(this, kNetworkConnStateChangedTopic, false); |
|
646 } |
|
647 |
|
648 nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1"); |
|
649 if (ril) { |
|
650 // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in |
|
651 // MultiSIM configuration |
|
652 ril->GetRadioInterface(0 /* clientId */, getter_AddRefs(mRadioInterface)); |
|
653 } |
|
654 } |
|
655 #endif // MOZ_B2G_RIL |
|
656 |
|
657 |
|
658 NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider::NetworkLocationUpdate, |
|
659 nsIGeolocationUpdate) |
|
660 |
|
661 NS_IMETHODIMP |
|
662 GonkGPSGeolocationProvider::NetworkLocationUpdate::Update(nsIDOMGeoPosition *position) |
|
663 { |
|
664 nsRefPtr<GonkGPSGeolocationProvider> provider = |
|
665 GonkGPSGeolocationProvider::GetSingleton(); |
|
666 |
|
667 nsCOMPtr<nsIDOMGeoPositionCoords> coords; |
|
668 position->GetCoords(getter_AddRefs(coords)); |
|
669 if (!coords) { |
|
670 return NS_ERROR_FAILURE; |
|
671 } |
|
672 |
|
673 // if we haven't seen anything from the GPS device for 1s, |
|
674 // use this network derived location. |
|
675 int64_t diff = PR_Now() - provider->mLastGPSDerivedLocationTime; |
|
676 if (provider->mLocationCallback && diff > kDefaultPeriod) { |
|
677 provider->mLocationCallback->Update(position); |
|
678 } |
|
679 |
|
680 double lat, lon, acc; |
|
681 coords->GetLatitude(&lat); |
|
682 coords->GetLongitude(&lon); |
|
683 coords->GetAccuracy(&acc); |
|
684 provider->InjectLocation(lat, lon, acc); |
|
685 return NS_OK; |
|
686 } |
|
687 |
|
688 NS_IMETHODIMP |
|
689 GonkGPSGeolocationProvider::NetworkLocationUpdate::LocationUpdatePending() |
|
690 { |
|
691 return NS_OK; |
|
692 } |
|
693 |
|
694 NS_IMETHODIMP |
|
695 GonkGPSGeolocationProvider::NetworkLocationUpdate::NotifyError(uint16_t error) |
|
696 { |
|
697 return NS_OK; |
|
698 } |
|
699 |
|
700 NS_IMETHODIMP |
|
701 GonkGPSGeolocationProvider::Startup() |
|
702 { |
|
703 MOZ_ASSERT(NS_IsMainThread()); |
|
704 |
|
705 RequestSettingValue(SETTING_DEBUG_ENABLED); |
|
706 if (mStarted) { |
|
707 return NS_OK; |
|
708 } |
|
709 |
|
710 if (!mInitThread) { |
|
711 nsresult rv = NS_NewThread(getter_AddRefs(mInitThread)); |
|
712 NS_ENSURE_SUCCESS(rv, rv); |
|
713 } |
|
714 |
|
715 mInitThread->Dispatch(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::Init), |
|
716 NS_DISPATCH_NORMAL); |
|
717 |
|
718 mNetworkLocationProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1"); |
|
719 if (mNetworkLocationProvider) { |
|
720 nsresult rv = mNetworkLocationProvider->Startup(); |
|
721 if (NS_SUCCEEDED(rv)) { |
|
722 nsRefPtr<NetworkLocationUpdate> update = new NetworkLocationUpdate(); |
|
723 mNetworkLocationProvider->Watch(update); |
|
724 } |
|
725 } |
|
726 |
|
727 mLastGPSDerivedLocationTime = 0; |
|
728 mStarted = true; |
|
729 return NS_OK; |
|
730 } |
|
731 |
|
732 NS_IMETHODIMP |
|
733 GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback) |
|
734 { |
|
735 MOZ_ASSERT(NS_IsMainThread()); |
|
736 |
|
737 mLocationCallback = aCallback; |
|
738 return NS_OK; |
|
739 } |
|
740 |
|
741 NS_IMETHODIMP |
|
742 GonkGPSGeolocationProvider::Shutdown() |
|
743 { |
|
744 MOZ_ASSERT(NS_IsMainThread()); |
|
745 |
|
746 if (!mStarted) { |
|
747 return NS_OK; |
|
748 } |
|
749 mStarted = false; |
|
750 if (mNetworkLocationProvider) { |
|
751 mNetworkLocationProvider->Shutdown(); |
|
752 mNetworkLocationProvider = nullptr; |
|
753 } |
|
754 #ifdef MOZ_B2G_RIL |
|
755 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
|
756 if (obs) { |
|
757 obs->RemoveObserver(this, kNetworkConnStateChangedTopic); |
|
758 } |
|
759 #endif |
|
760 |
|
761 mInitThread->Dispatch(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::ShutdownGPS), |
|
762 NS_DISPATCH_NORMAL); |
|
763 |
|
764 return NS_OK; |
|
765 } |
|
766 |
|
767 void |
|
768 GonkGPSGeolocationProvider::ShutdownGPS() |
|
769 { |
|
770 MOZ_ASSERT(!mStarted, "Should only be called after Shutdown"); |
|
771 |
|
772 if (mGpsInterface) { |
|
773 mGpsInterface->stop(); |
|
774 mGpsInterface->cleanup(); |
|
775 } |
|
776 } |
|
777 |
|
778 NS_IMETHODIMP |
|
779 GonkGPSGeolocationProvider::SetHighAccuracy(bool) |
|
780 { |
|
781 return NS_OK; |
|
782 } |
|
783 |
|
784 NS_IMETHODIMP |
|
785 GonkGPSGeolocationProvider::Observe(nsISupports* aSubject, |
|
786 const char* aTopic, |
|
787 const char16_t* aData) |
|
788 { |
|
789 MOZ_ASSERT(NS_IsMainThread()); |
|
790 |
|
791 #ifdef MOZ_B2G_RIL |
|
792 if (!strcmp(aTopic, kNetworkConnStateChangedTopic)) { |
|
793 nsCOMPtr<nsIRilNetworkInterface> iface = do_QueryInterface(aSubject); |
|
794 if (!iface) { |
|
795 return NS_OK; |
|
796 } |
|
797 |
|
798 RequestSettingValue("ril.supl.apn"); |
|
799 } |
|
800 #endif |
|
801 |
|
802 return NS_OK; |
|
803 } |
|
804 |
|
805 /** nsISettingsServiceCallback **/ |
|
806 |
|
807 NS_IMETHODIMP |
|
808 GonkGPSGeolocationProvider::Handle(const nsAString& aName, |
|
809 JS::Handle<JS::Value> aResult) |
|
810 { |
|
811 #ifdef MOZ_B2G_RIL |
|
812 if (aName.EqualsLiteral("ril.supl.apn")) { |
|
813 // When we get the APN, we attempt to call data_call_open of AGPS. |
|
814 if (aResult.isString()) { |
|
815 JSContext *cx = nsContentUtils::GetCurrentJSContext(); |
|
816 NS_ENSURE_TRUE(cx, NS_OK); |
|
817 |
|
818 // NB: No need to enter a compartment to read the contents of a string. |
|
819 nsDependentJSString apn; |
|
820 apn.init(cx, aResult.toString()); |
|
821 if (!apn.IsEmpty()) { |
|
822 SetAGpsDataConn(apn); |
|
823 } |
|
824 } |
|
825 } else |
|
826 #endif // MOZ_B2G_RIL |
|
827 if (aName.EqualsLiteral(SETTING_DEBUG_ENABLED)) { |
|
828 gGPSDebugging = aResult.isBoolean() ? aResult.toBoolean() : false; |
|
829 return NS_OK; |
|
830 } |
|
831 return NS_OK; |
|
832 } |
|
833 |
|
834 NS_IMETHODIMP |
|
835 GonkGPSGeolocationProvider::HandleError(const nsAString& aErrorMessage) |
|
836 { |
|
837 return NS_OK; |
|
838 } |