1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtPositioning module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "qgeopositioninfosource_winrt_p.h"
38 
39 #include <QtCore/qcoreapplication.h>
40 #include <QtCore/qfunctions_winrt.h>
41 #include <QtCore/qloggingcategory.h>
42 #include <QtCore/qmutex.h>
43 #ifdef Q_OS_WINRT
44 #include <QtCore/private/qeventdispatcher_winrt_p.h>
45 #endif
46 
47 #include <functional>
48 #include <windows.system.h>
49 #include <windows.devices.geolocation.h>
50 #include <windows.foundation.h>
51 #include <windows.foundation.collections.h>
52 
53 using namespace Microsoft::WRL;
54 using namespace Microsoft::WRL::Wrappers;
55 using namespace ABI::Windows::Devices::Geolocation;
56 using namespace ABI::Windows::Foundation;
57 using namespace ABI::Windows::Foundation::Collections;
58 
59 typedef ITypedEventHandler<Geolocator *, PositionChangedEventArgs *> GeoLocatorPositionHandler;
60 typedef ITypedEventHandler<Geolocator *, StatusChangedEventArgs *> GeoLocatorStatusHandler;
61 typedef IAsyncOperationCompletedHandler<Geoposition*> PositionHandler;
62 typedef IAsyncOperationCompletedHandler<GeolocationAccessStatus> AccessHandler;
63 
64 Q_DECLARE_LOGGING_CATEGORY(lcPositioningWinRT)
65 
66 Q_DECLARE_METATYPE(QGeoPositionInfoSource::Error)
67 
68 QT_BEGIN_NAMESPACE
69 
70 #ifndef Q_OS_WINRT
71 namespace QEventDispatcherWinRT {
runOnXamlThread(const std::function<HRESULT ()> & delegate,bool waitForRun=true)72 HRESULT runOnXamlThread(const std::function<HRESULT ()> &delegate, bool waitForRun = true)
73 {
74     Q_UNUSED(waitForRun);
75     return delegate();
76 }
77 }
78 
await(const ComPtr<IAsyncOperation<GeolocationAccessStatus>> & asyncOp,GeolocationAccessStatus * result)79 static inline HRESULT await(const ComPtr<IAsyncOperation<GeolocationAccessStatus>> &asyncOp,
80                             GeolocationAccessStatus *result)
81 {
82     ComPtr<IAsyncInfo> asyncInfo;
83     HRESULT hr = asyncOp.As(&asyncInfo);
84     if (FAILED(hr))
85         return hr;
86 
87     AsyncStatus status;
88     while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started)
89         QThread::yieldCurrentThread();
90 
91     if (FAILED(hr) || status != AsyncStatus::Completed) {
92         HRESULT ec;
93         hr = asyncInfo->get_ErrorCode(&ec);
94         if (FAILED(hr))
95             return hr;
96         hr = asyncInfo->Close();
97         if (FAILED(hr))
98             return hr;
99         return ec;
100     }
101 
102     if (FAILED(hr))
103         return hr;
104 
105     return asyncOp->GetResults(result);
106 }
107 #endif // !Q_OS_WINRT
108 
109 enum class InitializationState {
110     Uninitialized,
111     Initializing,
112     Initialized
113 };
114 
115 class QGeoPositionInfoSourceWinRTPrivate {
116 public:
117     ComPtr<IGeolocator> locator;
118     mutable ComPtr<IGeolocatorStatics> statics;
119     QTimer periodicTimer;
120     QTimer singleUpdateTimer;
121     QGeoPositionInfo lastPosition;
122     QGeoPositionInfoSource::Error positionError = QGeoPositionInfoSource::NoError;
123     EventRegistrationToken statusToken;
124     EventRegistrationToken positionToken;
125     QMutex mutex;
126     bool updatesOngoing = false;
127     int minimumUpdateInterval = -1;
128     int updateInterval = -1;
129     InitializationState initState = InitializationState::Uninitialized;
130 
131     PositionStatus positionStatus = PositionStatus_NotInitialized;
132 };
133 
QGeoPositionInfoSourceWinRT(QObject * parent)134 QGeoPositionInfoSourceWinRT::QGeoPositionInfoSourceWinRT(QObject *parent)
135     : QGeoPositionInfoSource(parent)
136     , d_ptr(new QGeoPositionInfoSourceWinRTPrivate)
137 {
138     qRegisterMetaType<QGeoPositionInfoSource::Error>();
139     qCDebug(lcPositioningWinRT) << __FUNCTION__;
140     CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
141     Q_D(QGeoPositionInfoSourceWinRT);
142     d->positionError = QGeoPositionInfoSource::NoError;
143     d->updatesOngoing = false;
144     d->positionToken.value = 0;
145     d->statusToken.value = 0;
146 }
147 
~QGeoPositionInfoSourceWinRT()148 QGeoPositionInfoSourceWinRT::~QGeoPositionInfoSourceWinRT()
149 {
150     qCDebug(lcPositioningWinRT) << __FUNCTION__;
151     CoUninitialize();
152 }
153 
init()154 int QGeoPositionInfoSourceWinRT::init()
155 {
156     Q_D(QGeoPositionInfoSourceWinRT);
157     Q_ASSERT(d->initState != InitializationState::Initializing);
158     if (d->initState == InitializationState::Initialized)
159         return 0;
160 
161     qCDebug(lcPositioningWinRT) << __FUNCTION__;
162     d->initState = InitializationState::Initializing;
163     if (!requestAccess()) {
164         d->initState = InitializationState::Uninitialized;
165         setError(QGeoPositionInfoSource::AccessError);
166         qWarning ("Location access failed.");
167         return -1;
168     }
169     HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() {
170         HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
171                                         &d->locator);
172         RETURN_HR_IF_FAILED("Could not initialize native location services.");
173 
174         if (d->minimumUpdateInterval == -1) {
175             UINT32 interval;
176             hr = d->locator->get_ReportInterval(&interval);
177             RETURN_HR_IF_FAILED("Could not retrieve report interval.");
178             d->minimumUpdateInterval = static_cast<int>(interval);
179         }
180         if (d->updateInterval == -1)
181             d->updateInterval = d->minimumUpdateInterval;
182         setUpdateInterval(d->updateInterval);
183 
184         return hr;
185     });
186     if (FAILED(hr)) {
187         d->initState = InitializationState::Uninitialized;
188         setError(QGeoPositionInfoSource::UnknownSourceError);
189         return -1;
190     }
191 
192     d->periodicTimer.setSingleShot(true);
193     connect(&d->periodicTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate);
194 
195     d->singleUpdateTimer.setSingleShot(true);
196     connect(&d->singleUpdateTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::singleUpdateTimeOut);
197 
198     QGeoPositionInfoSource::PositioningMethods preferredMethods = preferredPositioningMethods();
199     if (preferredMethods == QGeoPositionInfoSource::NoPositioningMethods)
200         preferredMethods = QGeoPositionInfoSource::AllPositioningMethods;
201     setPreferredPositioningMethods(preferredMethods);
202 
203     connect(this, &QGeoPositionInfoSourceWinRT::nativePositionUpdate, this, &QGeoPositionInfoSourceWinRT::updateSynchronized);
204     d->initState = InitializationState::Initialized;
205     return 0;
206 }
207 
lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const208 QGeoPositionInfo QGeoPositionInfoSourceWinRT::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
209 {
210     qCDebug(lcPositioningWinRT) << __FUNCTION__;
211     Q_D(const QGeoPositionInfoSourceWinRT);
212     Q_UNUSED(fromSatellitePositioningMethodsOnly);
213     return d->lastPosition;
214 }
215 
supportedPositioningMethods() const216 QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceWinRT::supportedPositioningMethods() const
217 {
218     return requestAccess() ? QGeoPositionInfoSource::AllPositioningMethods
219                            : QGeoPositionInfoSource::NoPositioningMethods;
220 }
221 
setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)222 void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)
223 {
224     qCDebug(lcPositioningWinRT) << __FUNCTION__ << methods;
225     Q_D(QGeoPositionInfoSourceWinRT);
226 
227     PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
228     QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
229     if (previousPreferredPositioningMethods == preferredPositioningMethods()
230             || d->initState == InitializationState::Uninitialized) {
231         return;
232     }
233 
234     const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
235 
236     if (needsRestart)
237         stopHandler();
238 
239     PositionAccuracy acc = methods & PositioningMethod::SatellitePositioningMethods ?
240                 PositionAccuracy::PositionAccuracy_High :
241                 PositionAccuracy::PositionAccuracy_Default;
242     HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([d, acc]() {
243         return d->locator->put_DesiredAccuracy(acc);
244     });
245     RETURN_VOID_IF_FAILED("Could not set positioning accuracy.");
246 
247     if (needsRestart)
248         startHandler();
249 }
250 
setUpdateInterval(int msec)251 void QGeoPositionInfoSourceWinRT::setUpdateInterval(int msec)
252 {
253     qCDebug(lcPositioningWinRT) << __FUNCTION__ << msec;
254     Q_D(QGeoPositionInfoSourceWinRT);
255     if (d->initState == InitializationState::Uninitialized) {
256         d->updateInterval = msec;
257         return;
258     }
259 
260     // minimumUpdateInterval is initialized to the lowest possible update interval in init().
261     // Passing 0 will cause an error on Windows 10.
262     // See https://docs.microsoft.com/en-us/uwp/api/windows.devices.geolocation.geolocator.reportinterval
263     if (msec != 0 && msec < minimumUpdateInterval())
264         msec = minimumUpdateInterval();
265 
266     const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
267 
268     if (needsRestart)
269         stopHandler();
270 
271     HRESULT hr = d->locator->put_ReportInterval(static_cast<UINT32>(msec));
272     if (FAILED(hr)) {
273         setError(QGeoPositionInfoSource::UnknownSourceError);
274         qErrnoWarning(hr, "Failed to set update interval");
275         return;
276     }
277 
278     d->updateInterval = msec;
279     d->periodicTimer.setInterval(d->updateInterval);
280 
281     QGeoPositionInfoSource::setUpdateInterval(d->updateInterval);
282 
283     if (needsRestart)
284         startHandler();
285 }
286 
minimumUpdateInterval() const287 int QGeoPositionInfoSourceWinRT::minimumUpdateInterval() const
288 {
289     Q_D(const QGeoPositionInfoSourceWinRT);
290     return d->minimumUpdateInterval == -1 ? 1000 : d->minimumUpdateInterval;
291 }
292 
startUpdates()293 void QGeoPositionInfoSourceWinRT::startUpdates()
294 {
295     qCDebug(lcPositioningWinRT) << __FUNCTION__;
296     Q_D(QGeoPositionInfoSourceWinRT);
297 
298     setError(QGeoPositionInfoSource::NoError);
299     if (init() < 0)
300         return;
301 
302     if (d->updatesOngoing)
303         return;
304 
305     if (!startHandler())
306         return;
307     d->updatesOngoing = true;
308     d->periodicTimer.start();
309 }
310 
stopUpdates()311 void QGeoPositionInfoSourceWinRT::stopUpdates()
312 {
313     qCDebug(lcPositioningWinRT) << __FUNCTION__;
314     Q_D(QGeoPositionInfoSourceWinRT);
315 
316     if (init() < 0)
317         return;
318 
319     stopHandler();
320     d->updatesOngoing = false;
321     d->periodicTimer.stop();
322 }
323 
startHandler()324 bool QGeoPositionInfoSourceWinRT::startHandler()
325 {
326     qCDebug(lcPositioningWinRT) << __FUNCTION__;
327     Q_D(QGeoPositionInfoSourceWinRT);
328 
329     // Check if already attached
330     if (d->positionToken.value != 0)
331         return true;
332 
333     if (preferredPositioningMethods() == QGeoPositionInfoSource::NoPositioningMethods) {
334         setError(QGeoPositionInfoSource::UnknownSourceError);
335         return false;
336     }
337 
338     if (!requestAccess()) {
339         setError(QGeoPositionInfoSource::AccessError);
340         return false;
341     }
342 
343     HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() {
344         HRESULT hr;
345 
346         // We need to call this at least once on Windows 10 Mobile.
347         // Unfortunately this operation does not have a completion handler
348         // registered. That could have helped in the single update case
349         ComPtr<IAsyncOperation<Geoposition*>> op;
350         hr = d->locator->GetGeopositionAsync(&op);
351         RETURN_HR_IF_FAILED("Could not start position operation");
352 
353         hr = d->locator->add_PositionChanged(Callback<GeoLocatorPositionHandler>(this,
354                                                                                  &QGeoPositionInfoSourceWinRT::onPositionChanged).Get(),
355                                              &d->positionToken);
356         RETURN_HR_IF_FAILED("Could not add position handler");
357 
358         hr = d->locator->add_StatusChanged(Callback<GeoLocatorStatusHandler>(this,
359                                                                                  &QGeoPositionInfoSourceWinRT::onStatusChanged).Get(),
360                                              &d->statusToken);
361         RETURN_HR_IF_FAILED("Could not add status handler");
362         return hr;
363     });
364     if (FAILED(hr)) {
365         setError(QGeoPositionInfoSource::UnknownSourceError);
366         return false;
367     }
368 
369     return true;
370 }
371 
stopHandler()372 void QGeoPositionInfoSourceWinRT::stopHandler()
373 {
374     qCDebug(lcPositioningWinRT) << __FUNCTION__;
375     Q_D(QGeoPositionInfoSourceWinRT);
376 
377     if (!d->positionToken.value)
378         return;
379     QEventDispatcherWinRT::runOnXamlThread([d]() {
380         d->locator->remove_PositionChanged(d->positionToken);
381         d->locator->remove_StatusChanged(d->statusToken);
382         return S_OK;
383     });
384     d->positionToken.value = 0;
385     d->statusToken.value = 0;
386 }
387 
requestUpdate(int timeout)388 void QGeoPositionInfoSourceWinRT::requestUpdate(int timeout)
389 {
390     qCDebug(lcPositioningWinRT) << __FUNCTION__ << timeout;
391     Q_D(QGeoPositionInfoSourceWinRT);
392 
393     if (init() < 0)
394         return;
395 
396     setError(QGeoPositionInfoSource::NoError);
397     if (timeout != 0 && timeout < minimumUpdateInterval()) {
398         emit updateTimeout();
399         return;
400     }
401 
402     if (timeout == 0)
403         timeout = 2*60*1000; // Maximum time for cold start (see Android)
404 
405     startHandler();
406     d->singleUpdateTimer.start(timeout);
407 }
408 
virtualPositionUpdate()409 void QGeoPositionInfoSourceWinRT::virtualPositionUpdate()
410 {
411     qCDebug(lcPositioningWinRT) << __FUNCTION__;
412     Q_D(QGeoPositionInfoSourceWinRT);
413     QMutexLocker locker(&d->mutex);
414 
415     // The operating system did not provide information in time
416     // Hence we send a virtual position update to keep same behavior
417     // between backends.
418     // This only applies to the periodic timer, not for single requests
419     // We can only do this if we received a valid position before
420     if (d->lastPosition.isValid()) {
421         QGeoPositionInfo sent = d->lastPosition;
422         sent.setTimestamp(sent.timestamp().addMSecs(updateInterval()));
423         d->lastPosition = sent;
424         emit positionUpdated(sent);
425     }
426     d->periodicTimer.start();
427 }
428 
singleUpdateTimeOut()429 void QGeoPositionInfoSourceWinRT::singleUpdateTimeOut()
430 {
431     Q_D(QGeoPositionInfoSourceWinRT);
432     QMutexLocker locker(&d->mutex);
433 
434     if (d->singleUpdateTimer.isActive()) {
435         emit updateTimeout();
436         if (!d->updatesOngoing)
437             stopHandler();
438     }
439 }
440 
updateSynchronized(QGeoPositionInfo currentInfo)441 void QGeoPositionInfoSourceWinRT::updateSynchronized(QGeoPositionInfo currentInfo)
442 {
443     qCDebug(lcPositioningWinRT) << __FUNCTION__ << currentInfo;
444     Q_D(QGeoPositionInfoSourceWinRT);
445     QMutexLocker locker(&d->mutex);
446 
447     d->periodicTimer.stop();
448     d->lastPosition = currentInfo;
449 
450     if (d->updatesOngoing)
451         d->periodicTimer.start();
452 
453     if (d->singleUpdateTimer.isActive()) {
454         d->singleUpdateTimer.stop();
455         if (!d->updatesOngoing)
456         stopHandler();
457     }
458 
459     emit positionUpdated(currentInfo);
460 }
461 
error() const462 QGeoPositionInfoSource::Error QGeoPositionInfoSourceWinRT::error() const
463 {
464     Q_D(const QGeoPositionInfoSourceWinRT);
465     qCDebug(lcPositioningWinRT) << __FUNCTION__ << d->positionError;
466 
467     // If the last encountered error was "Access denied", it is possible that the location service
468     // has been enabled by now so that we are clear again.
469     if ((d->positionError == QGeoPositionInfoSource::AccessError
470          || d->positionError == QGeoPositionInfoSource::UnknownSourceError) && requestAccess())
471         return QGeoPositionInfoSource::NoError;
472 
473     return d->positionError;
474 }
475 
setError(QGeoPositionInfoSource::Error positionError)476 void QGeoPositionInfoSourceWinRT::setError(QGeoPositionInfoSource::Error positionError)
477 {
478     Q_D(QGeoPositionInfoSourceWinRT);
479 
480     if (positionError == d->positionError)
481         return;
482 
483     qCDebug(lcPositioningWinRT) << __FUNCTION__ << positionError;
484     d->positionError = positionError;
485     if (positionError != QGeoPositionInfoSource::NoError)
486         emit QGeoPositionInfoSource::error(positionError);
487 }
488 
reactOnError(QGeoPositionInfoSource::Error positionError)489 void QGeoPositionInfoSourceWinRT::reactOnError(QGeoPositionInfoSource::Error positionError)
490 {
491     setError(positionError);
492     stopUpdates();
493 }
494 
onPositionChanged(IGeolocator * locator,IPositionChangedEventArgs * args)495 HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPositionChangedEventArgs *args)
496 {
497     qCDebug(lcPositioningWinRT) << __FUNCTION__;
498     Q_UNUSED(locator);
499 
500     HRESULT hr;
501     ComPtr<IGeoposition> position;
502     hr = args->get_Position(&position);
503     RETURN_HR_IF_FAILED("Could not access position object.");
504 
505     QGeoPositionInfo currentInfo;
506 
507     ComPtr<IGeocoordinate> coord;
508     hr = position->get_Coordinate(&coord);
509     if (FAILED(hr))
510         qErrnoWarning(hr, "Could not access coordinate");
511 
512     ComPtr<IGeocoordinateWithPoint> pointCoordinate;
513     hr = coord.As(&pointCoordinate);
514     if (FAILED(hr))
515         qErrnoWarning(hr, "Could not cast coordinate.");
516 
517     ComPtr<IGeopoint> point;
518     hr = pointCoordinate->get_Point(&point);
519     if (FAILED(hr))
520         qErrnoWarning(hr, "Could not obtain coordinate's point.");
521 
522     BasicGeoposition pos;
523     hr = point->get_Position(&pos);
524     if (FAILED(hr))
525         qErrnoWarning(hr, "Could not obtain point's position.");
526 
527     DOUBLE lat = pos.Latitude;
528     DOUBLE lon = pos.Longitude;
529     DOUBLE alt = pos.Altitude;
530 
531     bool altitudeAvailable = false;
532     ComPtr<IGeoshape> shape;
533     hr = point.As(&shape);
534     if (SUCCEEDED(hr) && shape) {
535         AltitudeReferenceSystem altitudeSystem;
536         hr = shape->get_AltitudeReferenceSystem(&altitudeSystem);
537         if (SUCCEEDED(hr) && altitudeSystem == AltitudeReferenceSystem_Geoid)
538             altitudeAvailable = true;
539     }
540     if (altitudeAvailable)
541         currentInfo.setCoordinate(QGeoCoordinate(lat, lon, alt));
542     else
543         currentInfo.setCoordinate(QGeoCoordinate(lat, lon));
544 
545     DOUBLE accuracy;
546     hr = coord->get_Accuracy(&accuracy);
547     if (SUCCEEDED(hr))
548         currentInfo.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy);
549 
550     IReference<double> *altAccuracy;
551     hr = coord->get_AltitudeAccuracy(&altAccuracy);
552     if (SUCCEEDED(hr) && altAccuracy) {
553         double value;
554         hr = altAccuracy->get_Value(&value);
555         currentInfo.setAttribute(QGeoPositionInfo::VerticalAccuracy, value);
556     }
557 
558     IReference<double> *speed;
559     hr = coord->get_Speed(&speed);
560     if (SUCCEEDED(hr) && speed) {
561         double value;
562         hr = speed->get_Value(&value);
563         currentInfo.setAttribute(QGeoPositionInfo::GroundSpeed, value);
564     }
565 
566     IReference<double> *heading;
567     hr = coord->get_Heading(&heading);
568     if (SUCCEEDED(hr) && heading) {
569         double value;
570         hr = heading->get_Value(&value);
571         double mod = 0;
572         value = modf(value, &mod);
573         value += static_cast<int>(mod) % 360;
574         if (value >=0 && value <= 359) // get_Value might return nan/-nan
575             currentInfo.setAttribute(QGeoPositionInfo::Direction, value);
576     }
577 
578     DateTime dateTime;
579     hr = coord->get_Timestamp(&dateTime);
580 
581     if (dateTime.UniversalTime > 0) {
582         ULARGE_INTEGER uLarge;
583         uLarge.QuadPart = dateTime.UniversalTime;
584         FILETIME fileTime;
585         fileTime.dwHighDateTime = uLarge.HighPart;
586         fileTime.dwLowDateTime = uLarge.LowPart;
587         SYSTEMTIME systemTime;
588         if (FileTimeToSystemTime(&fileTime, &systemTime)) {
589             currentInfo.setTimestamp(QDateTime(QDate(systemTime.wYear, systemTime.wMonth,
590                                                      systemTime.wDay),
591                                                QTime(systemTime.wHour, systemTime.wMinute,
592                                                      systemTime.wSecond, systemTime.wMilliseconds),
593                                                Qt::UTC));
594         }
595     }
596 
597     emit nativePositionUpdate(currentInfo);
598 
599     return S_OK;
600 }
601 
isDisabledStatus(PositionStatus status)602 static inline bool isDisabledStatus(PositionStatus status)
603 {
604     return status == PositionStatus_NoData || status == PositionStatus_Disabled
605             || status == PositionStatus_NotAvailable;
606 }
607 
onStatusChanged(IGeolocator *,IStatusChangedEventArgs * args)608 HRESULT QGeoPositionInfoSourceWinRT::onStatusChanged(IGeolocator *, IStatusChangedEventArgs *args)
609 {
610     Q_D(QGeoPositionInfoSourceWinRT);
611 
612     const PositionStatus oldStatus = d->positionStatus;
613     HRESULT hr = args->get_Status(&d->positionStatus);
614     RETURN_HR_IF_FAILED("Could not obtain position status");
615     qCDebug(lcPositioningWinRT) << __FUNCTION__ << d->positionStatus;
616     QGeoPositionInfoSource::Error error = QGeoPositionInfoSource::NoError;
617     switch (d->positionStatus) {
618     case PositionStatus::PositionStatus_NotAvailable:
619         error = QGeoPositionInfoSource::UnknownSourceError;
620         break;
621     case PositionStatus::PositionStatus_Disabled:
622         error = QGeoPositionInfoSource::AccessError;
623         break;
624     case PositionStatus::PositionStatus_NoData:
625         error = QGeoPositionInfoSource::ClosedError;
626         break;
627     }
628     if (error != QGeoPositionInfoSource::NoError) {
629         QMetaObject::invokeMethod(this, "reactOnError", Qt::QueuedConnection,
630                                   Q_ARG(QGeoPositionInfoSource::Error,
631                                         QGeoPositionInfoSource::UnknownSourceError));
632     }
633 
634     if (isDisabledStatus(oldStatus) != isDisabledStatus(d->positionStatus))
635         emit supportedPositioningMethodsChanged();
636 
637     return S_OK;
638 }
639 
requestAccess() const640 bool QGeoPositionInfoSourceWinRT::requestAccess() const
641 {
642     Q_D(const QGeoPositionInfoSourceWinRT);
643     qCDebug(lcPositioningWinRT) << __FUNCTION__;
644     GeolocationAccessStatus accessStatus;
645 
646     ComPtr<IAsyncOperation<GeolocationAccessStatus>> op;
647     HRESULT hr;
648     hr = QEventDispatcherWinRT::runOnXamlThread([&op, d]() {
649         HRESULT hr;
650         if (!d->statics) {
651             hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
652                                         IID_PPV_ARGS(&d->statics));
653             RETURN_HR_IF_FAILED("Could not access Geolocation Statics.");
654         }
655 
656         hr = d->statics->RequestAccessAsync(&op);
657         return hr;
658     });
659     if (FAILED(hr)) {
660         qCDebug(lcPositioningWinRT) << __FUNCTION__ << "Requesting access from Xaml thread failed";
661         return false;
662     }
663 
664     // We cannot wait inside the XamlThread as that would deadlock
665 #ifdef Q_OS_WINRT
666     QWinRTFunctions::await(op, &accessStatus);
667 #else
668     await(op, &accessStatus);
669 #endif
670     return accessStatus == GeolocationAccessStatus_Allowed;
671 }
672 
673 QT_END_NAMESPACE
674