Merge "Fix for the issue N_SE-29643." into tizen_2.1
[platform/framework/native/locations.git] / src / FLoc_LocationProviderImpl.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /**
19  * @file        FLoc_LocationProviderImpl.cpp
20  * @brief       This is the implementation file for the %_LocationProviderImpl class.
21  *
22  * This implementation file contains the definitions of the %_LocationProviderImpl class methods.
23  */
24
25 #include <unique_ptr.h>
26 #include <FApp_AppManagerImpl.h>
27 #include <FAppApp.h>
28 #include <FAppTypes.h>
29 #include <FAppUiApp.h>
30 #include <FBaseColArrayList.h>
31 #include <FBaseDouble.h>
32 #include <FBaseRtEvent.h>
33 #include <FBaseRtTimer.h>
34 #include <FBaseSysLog.h>
35 #include <FLocLocationCriteria.h>
36 #include <FLocCoordinates.h>
37 #include <FLocILocationProviderListener.h>
38 #include <FLocLocation.h>
39 #include <FLocTypes.h>
40 #include <FSysPowerManager.h>
41 #include <FSysSystemTime.h>
42 #include <FSys_AlarmImpl.h>
43 #include <FSys_SettingInfoImpl.h>
44 #include "FLoc_Config.h"
45 #include "FLoc_LocationImpl.h"
46 #include "FLoc_LocationManager.h"
47 #include "FLoc_LocationMonitor.h"
48 #include "FLoc_LocationProviderImpl.h"
49 #include "FLoc_MathUtils.h"
50 #include "FLoc_Types.h"
51
52 using namespace Tizen::App;
53 using namespace Tizen::Base;
54 using namespace Tizen::Base::Collection;
55 using namespace Tizen::Base::Runtime;
56 using namespace Tizen::Base::Utility;
57 using namespace Tizen::System;
58 using namespace std;
59
60 namespace Tizen { namespace Locations
61 {
62
63 _LocationProviderImpl::_LocationProviderImpl(void)
64         : Tizen::Base::Runtime::_Event()
65         , __lastLocationAccuracy(LOC_ACCURACY_INVALID)
66         , __pLocationListener(null)
67         , __pLocationManager(null)
68 {
69 }
70
71 _LocationProviderImpl::~_LocationProviderImpl(void)
72 {
73         UiApp* pAppInstance = Tizen::App::UiApp::GetInstance();
74         if (pAppInstance)
75         {
76                 _AppManagerImpl* pAppManager = _AppManagerImpl::GetInstance();
77                 if (pAppManager)
78                 {
79                         pAppManager->RemoveActiveAppEventListener(*this);
80                 }
81         }
82
83         StopLocationUpdates();
84         RemoveAllMonitoringRegions();
85 }
86
87 result
88 _LocationProviderImpl::Construct(const LocationCriteria& criteria, ILocationProviderListener& listener)
89 {
90         _LocationManager* pLocationManager = _LocationManager::GetInstance();
91         SysTryReturn(NID_LOC, pLocationManager != null, GetLastResult(), GetLastResult(), "[%s] Failed to get the location manager instance.", GetErrorMessage(GetLastResult()));
92
93         std::unique_ptr< Tizen::Base::Collection::ArrayList, AllElementsDeleter > pRegionList(new (std::nothrow) ArrayList());
94         SysTryReturn(NID_LOC, pRegionList != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
95         result r = pRegionList->Construct();
96         SysTryReturn(NID_LOC, r == E_SUCCESS, r, r, "[%s] Failed to construct the list. Propagating.", GetErrorMessage(r));
97
98         std::unique_ptr< Tizen::Base::Runtime::Timer> pTimer (new (std::nothrow) Timer());
99         SysTryReturn(NID_LOC, pTimer != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
100         r = pTimer->Construct(*this);
101         SysTryReturn(NID_LOC, r == E_SUCCESS, r, r, "[%s] Failed to construct the timer. Propagating.", GetErrorMessage(r));
102
103         std::unique_ptr< Tizen::System::Alarm> pAlarm (new (std::nothrow) Alarm());
104         SysTryReturn(NID_LOC, pAlarm != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
105         r = pAlarm->Construct(*this);
106         SysTryReturn(NID_LOC, r == E_SUCCESS, r, r, "[%s] Failed to construct the alarm. Propagating.", GetErrorMessage(r));
107
108         UiApp* pAppInstance = Tizen::App::UiApp::GetInstance();
109         if (pAppInstance != null)
110         {
111                 _AppManagerImpl* pAppManager = _AppManagerImpl::GetInstance();
112                 SysTryReturn(NID_LOC, pAppManager, E_SYSTEM, E_SYSTEM, "[E_SYSTEM] System error occured.");
113
114                 r = pAppManager->AddActiveAppEventListener(*this);
115                 SysTryReturn(NID_LOC, r == E_SUCCESS, r, r, "[%s] Error occured during adding the event listener to app manager. Propagating.", GetErrorMessage(r));
116         }
117
118         std::unique_ptr< Tizen::Locations::Location > pLastLocation(_LocationImpl::GetLocationInstanceN());
119         SysTryReturn(NID_LOC, pLastLocation != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
120         std::unique_ptr<Tizen::Locations::Location> pLastRegionLocation (_LocationImpl::GetLocationInstanceN());
121         SysTryReturn(NID_LOC, pLastRegionLocation != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
122
123         _Event::Initialize();
124         _Event::AddListener(*this);
125
126         __regionMonitor.pRegionList = std::move(pRegionList);
127         __regionMonitor.pTimer = std::move(pTimer);
128         __regionMonitor.pAlarm = std::move(pAlarm);
129         __regionMonitor.pLocation = std::move(pLastRegionLocation);
130         __locationUpdater.pLocation = std::move(pLastLocation);
131         
132         __criteria = criteria;
133         __pLocationListener = &listener;
134         __pLocationManager = pLocationManager;
135
136         SysLog(NID_LOC, "Location provider constructed with the accuracy (%x).", criteria.GetAccuracy());
137         return E_SUCCESS;
138 }
139
140 result
141 _LocationProviderImpl::StartLocationUpdatesByInterval(int interval)
142 {
143         bool userConsent = GetUserPrivilege();
144         SysTryReturn(NID_LOC, userConsent, E_USER_NOT_CONSENTED, E_USER_NOT_CONSENTED, "[E_USER_NOT_CONSENTED] The user has disabled the required settings.");
145         SysTryReturn(NID_LOC, interval >= 1, E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The interval(%d) should be greater than or equal to 1", interval);
146
147         if (__locationUpdater.type == _LOCATION_UPDATE_TYPE_INTERVAL && __locationUpdater.updateInterval == interval)
148         {
149                 return E_SUCCESS;
150         }
151         const double INVALID_DISTANCE_THRESHOLD = 0.0;
152         return StartLocationUpdates(_LOCATION_UPDATE_TYPE_INTERVAL, interval, INVALID_DISTANCE_THRESHOLD);
153 }
154
155 result
156 _LocationProviderImpl::StartLocationUpdatesByDistance(double distance)
157 {
158         bool userConsent = GetUserPrivilege();
159         SysTryReturn(NID_LOC, userConsent, E_USER_NOT_CONSENTED, E_USER_NOT_CONSENTED, "[E_USER_NOT_CONSENTED] The user has disabled the required settings.");
160         SysTryReturn(NID_LOC, distance > 0.0, E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The distance(%lf) should be greater than 0.0", distance);
161         SysTryReturn(NID_LOC, Double::IsNaN(distance) == false, E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The distance is NaN.");
162
163         if (__locationUpdater.type == _LOCATION_UPDATE_TYPE_DISTANCE && (Double::Compare(__locationUpdater.distanceThreshold, distance) == 0))
164         {
165                 return E_SUCCESS;
166         }
167         const int INVALID_INTERVAL = 0;
168         return StartLocationUpdates(_LOCATION_UPDATE_TYPE_DISTANCE, INVALID_INTERVAL, distance);
169 }
170
171 result
172 _LocationProviderImpl::StopLocationUpdates(void)
173 {
174         SysLog(NID_LOC, "Stopping the location updates for the request ID (%ld)", __locationUpdater.reqId);
175         SysTryReturn(NID_LOC, __locationUpdater.type != _LOCATION_UPDATE_TYPE_NONE, E_INVALID_OPERATION, E_INVALID_OPERATION, "[E_INVALID_OPERATION] Location update has not been requested.");
176
177         result r = __pLocationManager->StopLocationUpdates(__locationUpdater.reqId);
178         ResetLocationUpdates();
179         return r;
180 }
181
182 void
183 _LocationProviderImpl::KeepLocationUpdateAwake(bool enable)
184 {
185         if (__locationUpdater.awakeEnabled == enable)
186         {
187                 return;
188         }
189         __locationUpdater.awakeEnabled = enable;
190
191         UiApp* appInstance = Tizen::App::UiApp::GetInstance();
192         if (appInstance == null)        // This is service APP. So should be handled now.
193         {
194                 SysLog(NID_LOC, "Handling the request awake mode(%d) for the service application.", enable);
195                 if (enable == true)
196                 {
197                         if (__locationUpdater.type != _LOCATION_UPDATE_TYPE_NONE && __locationUpdater.status == LOC_SVC_STATUS_PAUSED)
198                         {
199                                 SysLog(NID_LOC, "Requesting to start the location updates as the update type is (%x)", __locationUpdater.type);
200                                 __locationUpdater.status = LOC_SVC_STATUS_NOT_FIXED;
201                                 __pLocationManager->StartLocationUpdates(__criteria.GetAccuracy(), __locationUpdater.updateInterval, this, __locationUpdater.reqId);
202
203                                 NotifyServiceStatus(_LOC_PRV_EVENT_SEND_LOC_SVC_CB, __locationUpdater.status);
204                         }
205                 }
206                 else
207                 {
208                         if (__locationUpdater.type != _LOCATION_UPDATE_TYPE_NONE && (__locationUpdater.status == LOC_SVC_STATUS_RUNNING || __locationUpdater.status == LOC_SVC_STATUS_NOT_FIXED))
209                         {
210                                 SysLog(NID_LOC, "Requesting to stop the location updates as the update type is (%x)", __locationUpdater.type);
211                                 __locationUpdater.status = LOC_SVC_STATUS_PAUSED;
212                                 __pLocationManager->StopLocationUpdates(__locationUpdater.reqId);
213
214                                 NotifyServiceStatus(_LOC_PRV_EVENT_SEND_LOC_SVC_CB, __locationUpdater.status);
215                         }
216                 }
217         }
218 }
219
220 result
221 _LocationProviderImpl::AddMonitoringRegion(const Coordinates& regionCenter, double radius, RegionId& regionId)
222 {
223         bool userConsent = GetUserPrivilege();
224         SysTryReturn(NID_LOC, userConsent, E_USER_NOT_CONSENTED, E_USER_NOT_CONSENTED, "[E_USER_NOT_CONSENTED] The user has disabled the required settings.");
225         SysTryReturn(NID_LOC, radius >= 50.0 && radius <= 100000.00, E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The radius is not within the specified limits.");
226         SysTryReturn(NID_LOC, (!Double::IsNaN(radius) && !Double::IsNaN(regionCenter.GetLatitude()) && !Double::IsNaN(regionCenter.GetLongitude())),
227                                  E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] One of the value is NaN.");
228         SysLog(NID_LOC, "Requested to add the monitoring region with center (Latitude: %lf, Longitude %lf) and radius (%lf).", regionCenter.GetLatitude(), regionCenter.GetLongitude(), radius);
229
230         static int nextRegionId = 0;
231
232         std::unique_ptr< _RegionInfo > pRegionInfo(new (std::nothrow) _RegionInfo(regionCenter, radius, nextRegionId));
233         SysTryReturn(NID_LOC, pRegionInfo != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
234         result r = __regionMonitor.pRegionList->Add(*pRegionInfo.get());
235         SysTryReturn(NID_LOC, r == E_SUCCESS, r, r, "[%s] Failed to add the Region info into the list. Propogated.", GetErrorMessage(r));
236
237         pRegionInfo.release();
238         regionId = nextRegionId;
239         nextRegionId++;
240
241         if (__regionMonitor.status != LOC_SVC_STATUS_IDLE)
242         {
243                 return E_SUCCESS;
244         }
245
246         r = ActivateRegionMonitoring();
247         SysTryCatch(NID_LOC, r == E_SUCCESS, , r, "[%s] Failed to start the region monitoring. Propogating.", GetErrorMessage(r));
248
249         __regionMonitor.status = LOC_SVC_STATUS_NOT_FIXED;
250         NotifyServiceStatus(_LOC_PRV_EVENT_SEND_MONITOR_SVC_CB, __regionMonitor.status);
251         return E_SUCCESS;
252
253 CATCH:
254         __regionMonitor.pRegionList->RemoveAt(0);
255         return r;
256 }
257
258 result
259 _LocationProviderImpl::RemoveMonitoringRegion(RegionId regionId)
260 {
261         int count = __regionMonitor.pRegionList->GetCount();
262         bool isIdValid = false;
263         SysLog(NID_LOC, "Total regions currently monitored is (%d).", count);
264
265         for (int i = 0; i < count; i++)
266         {
267                 _RegionInfo* pRegionInfo = static_cast< _RegionInfo* >(__regionMonitor.pRegionList->GetAt(i));
268                 if (regionId == pRegionInfo->GetRegionId())
269                 {
270                         __regionMonitor.pRegionList->RemoveAt(i, true);
271                         isIdValid = true;
272                         break;
273                 }
274         }
275
276         SysTryReturn(NID_LOC, isIdValid == true, E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The region ID is invalid.");
277         if (__regionMonitor.pRegionList->GetCount() == 0)
278         {
279                 __regionMonitor.status = LOC_SVC_STATUS_IDLE;
280                 if (__locationUpdater.status == LOC_SVC_STATUS_IDLE)  // As we are using the same variable for both region monitoring and location updates, this set is required here.
281                 {
282                         __lastLocationAccuracy = LOC_ACCURACY_INVALID;
283                 }
284                 StopRegionMonitoring();
285         }
286         return E_SUCCESS;
287 }
288
289 void
290 _LocationProviderImpl::RemoveAllMonitoringRegions(void)
291 {
292         __regionMonitor.pRegionList->RemoveAll(true);
293
294         if (__regionMonitor.status != LOC_SVC_STATUS_IDLE)
295         {
296                 __regionMonitor.status = LOC_SVC_STATUS_IDLE;
297                 if (__locationUpdater.status == LOC_SVC_STATUS_IDLE)  // As we are using the same variable for both region monitoring and location updates, this set is required here.
298                 {
299                         __lastLocationAccuracy = LOC_ACCURACY_INVALID;
300                 }
301                 StopRegionMonitoring();
302         }
303         return;
304 }
305
306 LocationServiceStatus
307 _LocationProviderImpl::GetLocationUpdateStatus(void) const
308 {
309         return __locationUpdater.status;
310 }
311
312 LocationServiceStatus
313 _LocationProviderImpl::GetRegionMonitoringStatus(void) const
314 {
315         return __regionMonitor.status;
316 }
317
318 LocationAccuracy
319 _LocationProviderImpl::GetCurrentAccuracy(void) const
320 {
321         return __lastLocationAccuracy;
322 }
323
324 Location
325 _LocationProviderImpl::GetLocation(const LocationCriteria& criteria)
326 {
327         Location retLocation(_LocationImpl::GetLocationInstance());
328
329         bool userConsent = GetUserPrivilege();
330         SysTryReturn(NID_LOC, userConsent, retLocation, E_USER_NOT_CONSENTED, "[E_USER_NOT_CONSENTED] The user has disabled the required settings.");
331
332         Location* pLocation = null;
333         const int MAX_TIMEOUT = 30;
334         result r = E_SUCCESS;
335         _LocationManager* pLocationManager = null;
336
337         SysLog(NID_LOC, "Requesting for single location with criteria (%x).", criteria.GetAccuracy());
338
339         std::unique_ptr< _LocationMonitor > pLocMonitor(new (std::nothrow) _LocationMonitor());
340         SysTryCatch(NID_LOC, pLocMonitor, , E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
341         r = pLocMonitor->Construct(MAX_TIMEOUT, criteria.GetAccuracy());
342         SysTryCatch(NID_LOC, r == E_SUCCESS, , r, "[%s] Failed to construct the Location Monitor.", GetErrorMessage(r));
343
344         pLocationManager = _LocationManager::GetInstance();
345         SysTryCatch(NID_LOC, pLocationManager, , r, "[%s] Failed to get the location manager instance.", GetErrorMessage(r));
346         r = pLocationManager->RegisterLocationMonitor(pLocMonitor.get());
347         SysTryCatch(NID_LOC, r == E_SUCCESS, , r, "[%s] Failed to register the location monitor.", GetErrorMessage(r));
348
349         r = pLocMonitor->Wait();
350         if (!IsFailed(r))
351         {
352                 pLocation = pLocMonitor->GetLocationN();
353         }
354         if (pLocation)
355         {
356                 retLocation = *pLocation;
357         }
358         else
359         {
360                 SetLastResult(E_LOCATION_UNAVAILABLE);
361         }
362         delete pLocation;
363         return retLocation;
364
365 CATCH:
366         return retLocation;
367 }
368
369 Location
370 _LocationProviderImpl::GetLastKnownLocation(void)
371 {
372         Location retLocation(_LocationImpl::GetLocationInstance());
373
374         bool userConsent = GetUserPrivilege();
375         SysTryReturn(NID_LOC, userConsent, retLocation, E_USER_NOT_CONSENTED, "[E_USER_NOT_CONSENTED] The user has disabled the required settings.");
376
377         _LocationManager* pLocationManager = _LocationManager::GetInstance();
378         SysTryReturn(NID_LOC, pLocationManager, retLocation, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
379
380         retLocation = pLocationManager->GetLastKnownLocation();
381         if (!retLocation.IsValid())
382         {
383                 SetLastResult(E_LOCATION_UNAVAILABLE);
384         }
385         else
386         {
387                 ClearLastResult();
388         }
389         return retLocation;
390 }
391
392 // Private members
393
394 void
395 _LocationProviderImpl::OnLocationUpdated(RequestId reqId, const Tizen::Locations::Location& location)
396 {
397         SysLog(NID_LOC, "Location is updated from Location Manager for the request ID (%ld).", reqId);
398
399         std::unique_ptr< Location > pLocation(new (std::nothrow) Location(location));
400         SysTryReturnVoidResult(NID_LOC, pLocation, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
401         std::unique_ptr< _LocProviderEventArg > pLocProviderEventArg(new (std::nothrow) _LocProviderEventArg());
402         SysTryReturnVoidResult(NID_LOC, pLocProviderEventArg, E_OUT_OF_MEMORY, "[%s] Memory allocation failed.", GetErrorMessage(E_OUT_OF_MEMORY));
403
404         pLocProviderEventArg->SetEventType(_LOC_PRV_EVENT_SEND_LOC);
405         pLocProviderEventArg->SetLocation(pLocation.get());
406         pLocProviderEventArg->SetRequestId(reqId);
407         pLocation.release();
408
409         result r = _Event::FireAsync(*pLocProviderEventArg);
410         SysTryReturnVoidResult(NID_LOC, r == E_SUCCESS, r, "[%s] Failed to fire the event.", GetErrorMessage(r));
411         pLocProviderEventArg.release();
412
413         return;
414 }
415
416 void
417 _LocationProviderImpl::OnLocationEventReceivedN(RequestId reqId, Tizen::Locations::Location& location)
418 {
419         SysLog(NID_LOC, "Location Event received.");
420         bool isNew = false;
421         LocationAccuracy currentAccuracy = LOC_ACCURACY_INVALID;
422         long long lastLocationTime = 0;
423
424         Location* pLocation = &location;
425         _LocationImpl* pLocationImpl = _LocationImpl::GetInstance(*pLocation);
426
427         if (reqId == __locationUpdater.reqId)
428         {
429                 SysLog(NID_LOC, "The location is updated for Location request.");
430                 lastLocationTime = _LocationImpl::GetInstance(*__locationUpdater.pLocation.get())->GetTimestampInMs();
431         }
432         else if (reqId == __regionMonitor.reqId)
433         {
434                 SysLog(NID_LOC, "The location is updated for Region monitoring.");
435                 lastLocationTime = _LocationImpl::GetInstance(*__regionMonitor.pLocation.get())->GetTimestampInMs();
436         }
437
438         long long timeDifference = pLocationImpl->GetTimestampInMs() - lastLocationTime;
439         SysLog(NID_LOC, "Time difference between last location timestamp (%lld) and current location timestamp (%lld) is (%lld).", lastLocationTime, pLocationImpl->GetTimestampInMs(), timeDifference);
440         if (timeDifference > 0)
441         {
442                 isNew = true;
443                 currentAccuracy = __pLocationManager->GetAccuracyLevel(pLocation->GetHorizontalAccuracy());
444         }
445
446         if (currentAccuracy != __lastLocationAccuracy)
447         {
448                 SysLog(NID_LOC, "Notify the accuracy change.");
449                 __lastLocationAccuracy = currentAccuracy;
450                 __pLocationListener->OnAccuracyChanged(currentAccuracy);
451         }
452
453         if (reqId == __locationUpdater.reqId)
454         {
455                 HandleLocationUpdate(location, isNew);
456         }
457         else if (reqId == __regionMonitor.reqId)
458         {
459                 if (isNew)      // Copy the location only if it is new.
460                 {
461                         *__regionMonitor.pLocation = location;
462                 }
463
464                 if (currentAccuracy != LOC_ACCURACY_INVALID && currentAccuracy <= __criteria.GetAccuracy())
465                 {
466                         SysLog(NID_LOC, "Location criteria (accuracy: %ld) is met for handling region monitoring.", currentAccuracy);
467                         result r = __regionMonitor.pTimer->Cancel();
468                         SysTryLog(NID_LOC, r == E_SUCCESS, "Failed to cancel the timer.");
469
470                         r = __pLocationManager->StopLocationUpdates(__regionMonitor.reqId);
471                         SysTryLog(NID_LOC, r == E_SUCCESS, "Failed to stop the location updates.");
472                         HandleRegionMonitoring(location, isNew);
473                 }
474                 else
475                 {
476                         SysLog(NID_LOC, "Location criteria (accuracy: %ld) is not met for handling region monitoring.", currentAccuracy);
477                 }
478         }
479
480         delete pLocation;
481 }
482
483 void
484 _LocationProviderImpl::OnLocationUpdateStatusChanged(Tizen::Locations::LocationServiceStatus locSvcStatus)
485 {
486         __pLocationListener->OnLocationUpdateStatusChanged(locSvcStatus);
487 }
488
489 void
490 _LocationProviderImpl::OnRegionMonitoringStatusChanged(Tizen::Locations::LocationServiceStatus locSvcStatus)
491 {
492         __pLocationListener->OnRegionMonitoringStatusChanged(locSvcStatus);
493 }
494
495 void
496 _LocationProviderImpl::OnActiveAppChanged(const Tizen::App::AppId& appId)
497 {
498         Tizen::App::App* pApp = Tizen::App::App::GetInstance();
499         Tizen::App::AppId currentAppId = pApp->GetAppId();
500
501         SysLog(NID_LOC, "Active App ID is (%ls) and the current app Id is (%ls)", appId.GetPointer(), currentAppId.GetPointer());
502
503         if (currentAppId == appId)
504         {
505                 SysLog(NID_LOC, "Application is active.");
506
507                 if (__locationUpdater.status == LOC_SVC_STATUS_PAUSED)
508                 {
509                         SysLog(NID_LOC, "Start the location updates as the location update status is PAUSED.");
510                         __pLocationManager->StartLocationUpdates(__criteria.GetAccuracy(), __locationUpdater.updateInterval, this, __locationUpdater.reqId);
511                         __locationUpdater.status = LOC_SVC_STATUS_NOT_FIXED;
512                         __pLocationListener->OnLocationUpdateStatusChanged(__locationUpdater.status);
513                 }
514         }
515         else
516         {
517                 SysLog(NID_LOC, "Application is not active.");
518
519                 if (__locationUpdater.awakeEnabled == false && (__locationUpdater.status == LOC_SVC_STATUS_RUNNING || __locationUpdater.status == LOC_SVC_STATUS_NOT_FIXED))
520                 {
521                         SysLog(NID_LOC, "Stop the location updates as application is not active with awake mode as (%d) and location update state as (%x).", __locationUpdater.awakeEnabled, __locationUpdater.status);
522                         __pLocationManager->StopLocationUpdates(__locationUpdater.reqId);
523                         __locationUpdater.status = LOC_SVC_STATUS_PAUSED;
524                         __pLocationListener->OnLocationUpdateStatusChanged(__locationUpdater.status);
525                 }
526         }
527 }
528
529 void
530 _LocationProviderImpl::OnAlarmExpired(Alarm& alarm)
531 {
532         SysLog(NID_LOC, "Region Monitor Alarm expired.");
533         result r = ActivateRegionMonitoring();
534         SysTryReturnVoidResult(NID_LOC, r == E_SUCCESS, r, "[%s] Failed to start the region monitoring. Propogating.", GetErrorMessage(r));
535 }
536
537 void
538 _LocationProviderImpl::OnTimerExpired(Timer& timer)
539 {
540         SysLog(NID_LOC, "Region Monitor timer expired due to unavailability of location information.");
541         __pLocationManager->StopLocationUpdates(__regionMonitor.reqId);
542         HandleRegionMonitoring(*__regionMonitor.pLocation, __regionMonitor.pLocation->IsValid());
543 }
544
545 result
546 _LocationProviderImpl::StartLocationUpdates(LocationUpdateType updateType, int interval, double distance)
547 {
548         result r = E_SUCCESS;
549         bool startUpdate = true;
550
551         if (updateType == _LOCATION_UPDATE_TYPE_INTERVAL)
552         {
553                 __locationUpdater.updateInterval = interval;
554         }
555         else if (updateType == _LOCATION_UPDATE_TYPE_DISTANCE)
556         {
557                 __locationUpdater.updateInterval = DEFAULT_DISTANCE_CHECKING_INTERVAL;
558                 __locationUpdater.distanceThreshold = distance;
559         }
560
561         if  (!__locationUpdater.awakeEnabled)
562         {
563                 UiApp* pAppInstance = Tizen::App::UiApp::GetInstance();
564                 if (pAppInstance == null)
565                 {
566                         startUpdate = false;
567                 }
568                 else
569                 {
570                         AppUiState appUiState = pAppInstance->GetAppUiState();
571
572                         if (appUiState == APP_UI_STATE_BACKGROUND)
573                         {
574                                 SysLog(NID_LOC, "App is background.");
575                                 startUpdate = false;
576                         }
577                 }
578         }
579
580         if (startUpdate)
581         {
582                 if (__locationUpdater.type != _LOCATION_UPDATE_TYPE_NONE)
583                 {
584                         SysLog(NID_LOC, "Update session already running. Updating the interval to %d seconds", __locationUpdater.updateInterval);
585                         r = __pLocationManager->ChangeUpdateInterval(__locationUpdater.reqId, __locationUpdater.updateInterval);
586                         SysTryCatch(NID_LOC, r == E_SUCCESS, , r, "[%s] Failed to update the request interval. Propagating.", GetErrorMessage(r));
587                         __locationUpdater.type = updateType;
588                         return E_SUCCESS;
589                 }
590                 else
591                 {
592                         r = __pLocationManager->StartLocationUpdates(__criteria.GetAccuracy(), __locationUpdater.updateInterval, this, __locationUpdater.reqId);
593                         SysTryCatch(NID_LOC, r == E_SUCCESS, , r, "[%s] Failed to start the Native location updates. Propagating.", GetErrorMessage(r));
594                         __locationUpdater.status = LOC_SVC_STATUS_NOT_FIXED;
595                 }       
596         }
597         else
598         {
599                 __locationUpdater.status = LOC_SVC_STATUS_PAUSED;       
600         }
601
602         __locationUpdater.type = updateType;
603
604         SysLog(NID_LOC, "Update type is (%x). The request Id is (%ld) and the update status is (%x).", __locationUpdater.type, __locationUpdater.reqId, __locationUpdater.status);
605         NotifyServiceStatus(_LOC_PRV_EVENT_SEND_LOC_SVC_CB, __locationUpdater.status);
606
607         return E_SUCCESS;
608
609 CATCH:
610         __locationUpdater.type = _LOCATION_UPDATE_TYPE_NONE;
611         return r;
612 }
613
614 bool
615 _LocationProviderImpl::CheckDistanceThreshold(const Location& oldPosition, const Location& newPosition)
616 {
617         double displacement = 0.0;
618         const Coordinates coordOld = oldPosition.GetCoordinates();
619         const Coordinates coordNew = newPosition.GetCoordinates();
620
621         if (__locationUpdater.firstLocationUpdate)
622         {
623                 SysLog(NID_LOC, "First location update. So send true.");
624                 __locationUpdater.firstLocationUpdate = false;
625                 return true;
626         }
627         displacement = coordOld.GetDistanceTo(coordNew);
628         SysLog(NID_LOC, "Displacement is (%lf)", displacement);
629         return ((displacement > __locationUpdater.distanceThreshold) ? true : false);
630 }
631
632 void
633 _LocationProviderImpl::ResetLocationUpdates(void)
634 {
635         __locationUpdater.firstLocationUpdate = true;
636         __locationUpdater.reqId = -1;
637         __lastLocationAccuracy = LOC_ACCURACY_INVALID;
638         __locationUpdater.type = _LOCATION_UPDATE_TYPE_NONE;
639         __locationUpdater.status = LOC_SVC_STATUS_IDLE;
640         __locationUpdater.updateInterval = 0;
641         __locationUpdater.distanceThreshold = 0.0;
642 }
643
644 void
645 _LocationProviderImpl::FireImpl(Tizen::Base::Runtime::IEventListener& listener, const Tizen::Base::Runtime::IEventArg& arg)
646 {
647         _ILocProviderEventListener* pLocProviderEventListener = dynamic_cast< _ILocProviderEventListener* >(&listener);
648         SysTryReturnVoidResult(NID_LOC, pLocProviderEventListener, E_SYSTEM, "[E_INVALID_ARG] The listener is null.");
649
650         IEventArg* pArg = const_cast< IEventArg* >(&arg);
651         _LocProviderEventArg* pEventArg = dynamic_cast< _LocProviderEventArg* >(pArg);
652         SysTryReturnVoidResult(NID_LOC, pEventArg, E_SYSTEM, "[E_INVALID_ARG] Event argument is null.");
653
654         _LocProviderEventType eventType = pEventArg->GetEventType();
655         switch (eventType)
656         {
657         case _LOC_PRV_EVENT_SEND_LOC:
658                 pLocProviderEventListener->OnLocationEventReceivedN(pEventArg->GetRequestId(), *pEventArg->GetLocationN());
659                 break;
660
661         case _LOC_PRV_EVENT_SEND_LOC_SVC_CB:
662                 pLocProviderEventListener->OnLocationUpdateStatusChanged(pEventArg->GetLocServiceStatus());
663                 break;
664
665         case _LOC_PRV_EVENT_SEND_MONITOR_SVC_CB:
666                 pLocProviderEventListener->OnRegionMonitoringStatusChanged(pEventArg->GetLocServiceStatus());
667                 break;
668         }
669 }
670
671 void
672 _LocationProviderImpl::HandleLocationUpdate(Tizen::Locations::Location& location, bool isNew)
673 {
674         LocationServiceStatus newLocationUpdateStatus = __locationUpdater.status;
675
676         if (isNew)
677         {
678                 if (__locationUpdater.type != _LOCATION_UPDATE_TYPE_NONE && __locationUpdater.status != LOC_SVC_STATUS_PAUSED)
679                 {
680                         newLocationUpdateStatus = LOC_SVC_STATUS_RUNNING;
681                 }
682         }
683         else if (_LocationImpl::GetInstance(location)->IsDenied())
684         {
685                 SysLog(NID_LOC, "User consent not available.");
686                 if (__locationUpdater.type != _LOCATION_UPDATE_TYPE_NONE && __locationUpdater.status != LOC_SVC_STATUS_PAUSED)
687                 {
688                         newLocationUpdateStatus = LOC_SVC_STATUS_DENIED;
689                 }
690         }
691         else
692         {
693                 SysLog(NID_LOC, "Invalid Location Update.");
694                 if (__locationUpdater.type != _LOCATION_UPDATE_TYPE_NONE && __locationUpdater.status != LOC_SVC_STATUS_PAUSED)
695                 {
696                         newLocationUpdateStatus = LOC_SVC_STATUS_NOT_FIXED;
697                 }
698         }
699
700         if (newLocationUpdateStatus != __locationUpdater.status)
701         {
702                 SysLog(NID_LOC, "Location Update Satus changed to (%x). Notify the status.", newLocationUpdateStatus);
703                 __locationUpdater.status = newLocationUpdateStatus;
704                 __pLocationListener->OnLocationUpdateStatusChanged(__locationUpdater.status);
705         }
706
707         if (newLocationUpdateStatus == LOC_SVC_STATUS_RUNNING)
708         {
709                 if (__locationUpdater.type == _LOCATION_UPDATE_TYPE_DISTANCE)
710                 {
711                         if (CheckDistanceThreshold(*__locationUpdater.pLocation.get(), location) == true)
712                         {
713                                 SysLog(NID_LOC, "Location displacement exceeds the distance threshold (%lf). Notify the location.", __locationUpdater.distanceThreshold);
714                                 __pLocationListener->OnLocationUpdated(location);
715                                 *__locationUpdater.pLocation.get() = location;
716                         }
717                 }
718                 else if (__locationUpdater.type == _LOCATION_UPDATE_TYPE_INTERVAL)
719                 {
720                         SysLog(NID_LOC, "Location time interval expired. Notify the location.");
721                         __pLocationListener->OnLocationUpdated(location);
722                         *__locationUpdater.pLocation.get() = location;
723                 }
724         }
725 }
726
727 void
728 _LocationProviderImpl::HandleRegionMonitoring(Tizen::Locations::Location& location, bool isNew)
729 {
730         LocationServiceStatus newRegionMonitorStatus = __regionMonitor.status;
731
732         if (isNew)
733         {
734                 newRegionMonitorStatus = LOC_SVC_STATUS_RUNNING;
735         }
736         else if (_LocationImpl::GetInstance(location)->IsDenied())
737         {
738                 newRegionMonitorStatus = LOC_SVC_STATUS_DENIED;
739         }
740         else
741         {
742                 newRegionMonitorStatus = LOC_SVC_STATUS_NOT_FIXED;
743         }
744
745         if (newRegionMonitorStatus != __regionMonitor.status)
746         {
747                 SysLog(NID_LOC, "Region Monitoring Satus changed to (%x). Notify the status.", newRegionMonitorStatus);
748                 __regionMonitor.status = newRegionMonitorStatus;
749                 __pLocationListener->OnRegionMonitoringStatusChanged(__regionMonitor.status);
750         }
751
752         if (newRegionMonitorStatus == LOC_SVC_STATUS_RUNNING)
753         {
754                 NotifyRegionCrossedStatus(location);
755         }
756
757         SetNextRegionMonitoringTime();
758 }
759
760 void
761 _LocationProviderImpl::NotifyRegionCrossedStatus(const Tizen::Locations::Location& location)
762 {
763         int count = __regionMonitor.pRegionList->GetCount();
764
765         SysLog(NID_LOC, "Number of regions currently monitored is (%d)", count);
766
767         for (int i = 0; i < count; i++)
768         {
769                 _RegionInfo* pRegionInfo = static_cast< _RegionInfo* >(__regionMonitor.pRegionList->GetAt(i));
770
771                 if (pRegionInfo)
772                 {
773                         Coordinates regionCoordinate = pRegionInfo->GetCoordinate();
774                         _RegionState currentState = REGION_STATE_UNKNOWN;
775                         _RegionState previousState = pRegionInfo->GetPreviousValidState();
776                         bool notifyStateChange = false;
777
778                         currentState = GetRegionCurrentState(*pRegionInfo, location);
779                         SysLog(NID_LOC, "Current Region state is (%d) and Previous Region state is (%d)", currentState, previousState);
780
781                         if (currentState != REGION_STATE_UNKNOWN && previousState != currentState)
782                         {
783                                 notifyStateChange = true;
784                                 pRegionInfo->SetValidPreviousState(currentState);
785                         }
786
787                         if ((pRegionInfo->GetRegionState() == REGION_STATE_INSIDE || pRegionInfo->GetRegionState() == REGION_STATE_UNKNOWN) && currentState == REGION_STATE_OUTSIDE)
788                         {
789                                 if (notifyStateChange)
790                                 {
791                                         SysLog(NID_LOC, "Notify the boundary crossed event as previous valid region state is INSIDE and current state is OUTSIDE.");
792                                         __pLocationListener->OnRegionLeft(pRegionInfo->GetRegionId());
793                                 }
794                         }
795                         else if ((pRegionInfo->GetRegionState() == REGION_STATE_OUTSIDE || pRegionInfo->GetRegionState() == REGION_STATE_UNKNOWN) && currentState == REGION_STATE_INSIDE)
796                         {
797                                 if (notifyStateChange)
798                                 {
799                                         SysLog(NID_LOC, "Notify the boundary crossed event as previous valid region state is OUTSIDE and current state is INSIDE.");
800                                         __pLocationListener->OnRegionEntered(pRegionInfo->GetRegionId());
801                                 }
802                         }
803                         pRegionInfo->SetRegionState(currentState);
804                 }
805         }
806 }
807
808 _RegionState
809 _LocationProviderImpl::GetRegionCurrentState(const _RegionInfo& region, const Location& location)
810 {
811         TryReturn(location.GetHorizontalAccuracy() >= 0.0, REGION_STATE_UNKNOWN, "Location received with invalid accuracy");
812
813         SysLog(NID_LOC, "[RegionID %d] Region Information is (Center latitude: %lf, Center longitude: %lf, Region radius:%lf", region.GetRegionId(), region.GetCoordinate().GetLatitude(),
814                    region.GetCoordinate().GetLongitude(), region.GetRadius());
815         SysLog(NID_LOC, "[RegionID %d] Location Information is (Latitude: %lf, Longitude: %lf, Horizontal accuracy:%lf", region.GetRegionId(), location.GetCoordinates().GetLatitude(),
816                    location.GetCoordinates().GetLongitude(), location.GetHorizontalAccuracy());
817
818         _RegionState regionState = REGION_STATE_UNKNOWN;
819         double distanceBtwCenters = region.GetCoordinate().GetDistanceTo(location.GetCoordinates());
820         double regionRadius = region.GetRadius();
821         double locationRadius = location.GetHorizontalAccuracy();
822
823         SysLog(NID_LOC, "[RegionID %d]  The distance between centers is (%lf)", region.GetRegionId(), distanceBtwCenters);
824
825         if (distanceBtwCenters >= (regionRadius + locationRadius))
826         {
827                 regionState = REGION_STATE_OUTSIDE;
828         }
829         else if (distanceBtwCenters < regionRadius && Double::Compare(locationRadius, 0.0) == 0)
830         {
831                 SysLog(NID_LOC, "[RegionID %d] Location Radius is 0 and distance < regionRadius", region.GetRegionId());
832                 regionState = REGION_STATE_INSIDE;
833         }
834         else
835         {
836                 double radiusThreshold = (1 / Math::Sqrt(2)) * locationRadius;
837
838                 if (regionRadius < radiusThreshold)
839                 {
840                         SysLog(NID_LOC, "[RegionID %d] Region circle is less than 50 percent area of the location circle.", region.GetRegionId());
841                         regionState = (distanceBtwCenters >= locationRadius) ? REGION_STATE_OUTSIDE : REGION_STATE_UNKNOWN;
842
843                         return regionState;
844                 }
845                 else
846                 {
847                         double overlapRegion = _MathUtils::CalculateOverlapRegion(region, location);
848                         double upperThreshold = 0;
849                         double lowerThreshold = 0;
850                         double occupancy = 0;
851
852                         SysLog(NID_LOC, "[RegionID %d] OverlapRegion is (%lf)", region.GetRegionId(), overlapRegion);
853
854                         if (regionRadius >= locationRadius)
855                         {
856                                 // Calculate occupancy % with the location circle
857                                 SysLog(NID_LOC, "[RegionID %d] Region radius is bigger.", region.GetRegionId());
858                                 occupancy = (overlapRegion / (Math::GetPi() * locationRadius * locationRadius)) * 100;
859
860                                 upperThreshold = 60;
861                                 lowerThreshold = 40;
862                         }
863                         else if (regionRadius >= radiusThreshold)
864                         {
865                                 SysLog(NID_LOC, "[RegionID %d] The ratio of Region radius to location radius is between 0.707 and 1.", region.GetRegionId());
866                                 double thresholdValue = -136.51 * (regionRadius / locationRadius) + 146.51; // This equation varies the threshold value from 10 (for R/r = 1) to 50 (for R/r = 0.707)
867
868                                 SysLog(NID_LOC, "[RegionID %d] Threshold value is %lf.", region.GetRegionId(), thresholdValue);
869
870                                 occupancy = (overlapRegion / (Math::GetPi() * locationRadius * locationRadius)) * 100;
871
872                                 upperThreshold = 50 + thresholdValue;
873                                 lowerThreshold = 50 - thresholdValue;
874                         }
875
876                         // Decide the state with as per the inner and outer thresholds
877                         SysLog(NID_LOC, "[RegionID %d] Occupancy is (%lf), Upper threshold is (%lf) and lower threshold is (%lf).", region.GetRegionId(), occupancy, upperThreshold, lowerThreshold);
878                         regionState = (occupancy >= upperThreshold) ? REGION_STATE_INSIDE : ((occupancy <= lowerThreshold) ? REGION_STATE_OUTSIDE : REGION_STATE_UNKNOWN);
879                 }
880         }
881
882         SysLog(NID_LOC, "[RegionID %d] Returning region state as (%d).", region.GetRegionId(), regionState);
883         return regionState;
884 }
885
886 void
887 _LocationProviderImpl::NotifyServiceStatus(_LocProviderEventType eventType, LocationServiceStatus svcStatus)
888 {
889         std::unique_ptr< _LocProviderEventArg > pLocProviderEventArg(new (std::nothrow) _LocProviderEventArg());
890         if (pLocProviderEventArg)
891         {
892                 pLocProviderEventArg->SetEventType(eventType);
893                 pLocProviderEventArg->SetLocServiceStatus(svcStatus);
894
895                 result r = _Event::FireAsync(*pLocProviderEventArg.get());
896                 SysTryReturnVoidResult(NID_LOC, r == E_SUCCESS, r, "[%s] Failed to fire the event.", GetErrorMessage(r));
897
898                 pLocProviderEventArg.release();
899         }
900
901         return;
902 }
903
904 bool
905 _LocationProviderImpl::GetUserPrivilege(void)
906 {
907         _LocationManager* pLocMgr = _LocationManager::GetInstance();
908         SysTryReturn(NID_LOC, pLocMgr, false, E_SYSTEM, "[E_SYSTEM] Failed to get the location manager instance.");
909
910         bool appSettingEnabled = pLocMgr->IsAppEnabled();
911         if (appSettingEnabled == false)
912         {
913                 return false;
914         }
915
916         bool hasPrivilege = false;
917         bool gpsEnabled = true;
918         bool wpsEnabled = true;
919
920         result gps = _SettingInfoImpl::GetValue(L"http://tizen.org/setting/location.gps", gpsEnabled);
921         result wps = _SettingInfoImpl::GetValue(L"http://tizen.org/setting/location.wps", wpsEnabled);
922
923         hasPrivilege = gpsEnabled | wpsEnabled;
924         if (gps != E_SUCCESS || wps != E_SUCCESS || hasPrivilege == false)
925         {
926                 return false;
927         }
928         return true;
929 }
930
931 result
932 _LocationProviderImpl::ActivateRegionMonitoring(void)
933 {
934         long long currentTime;
935         SystemTime::GetTicks(currentTime);
936
937         SysLog(NID_LOC, "Current system time is %lld", currentTime);
938
939         _LocationImpl::GetInstance(*__regionMonitor.pLocation)->SetValidity(false);
940         _LocationImpl::GetInstance(*__regionMonitor.pLocation)->SetTimestamp(currentTime);
941
942         result r = __regionMonitor.pTimer->Start(DEFAULT_WAITING_TIME_FOR_FIXING_LOCATION * 1000);
943         SysTryReturn(NID_LOC, r == E_SUCCESS, r, r, "[%s] Failed to start the timer. Propogating.", GetErrorMessage(r));
944
945         r = __pLocationManager->StartLocationUpdates(__criteria.GetAccuracy(), MIN_LOCATION_UPDATE_INTERVAL, this, __regionMonitor.reqId);
946         SysTryReturn(NID_LOC, r == E_SUCCESS, r, r, "[%s] Failed to start the location updates. Propogating.", GetErrorMessage(r));
947
948         SysLog(NID_LOC, "Timer & Location updates are started.");
949         return E_SUCCESS;
950 }
951
952 void
953 _LocationProviderImpl::StopRegionMonitoring(void)
954 {
955         result r = _AlarmImpl::GetInstance(__regionMonitor.pAlarm.get())->Cancel();
956         SysTryLog(NID_LOC, r == E_SUCCESS, "[%s] Failed to stop the alarm. Ignored.", GetErrorMessage(r));
957
958         r = __regionMonitor.pTimer->Cancel();
959         SysTryLog(NID_LOC, r == E_SUCCESS, "[%s] Failed to stop the timer. Ignored.", GetErrorMessage(r));
960
961         r = __pLocationManager->StopLocationUpdates(__regionMonitor.reqId);
962         SysTryReturnVoidResult(NID_LOC, r == E_SUCCESS, r, "[%s] All regions are removed but failed to stop the location updates. Ignored.", GetErrorMessage(r));
963 }
964
965 void
966 _LocationProviderImpl::SetNextRegionMonitoringTime(void)
967 {
968         DateTime alarmDateTime;
969         long long alarmTime = DEFAULT_REGION_MONITORING_CYCLE_INTERVAL;
970         SystemTime::GetCurrentTime(TIME_MODE_WALL, alarmDateTime);
971
972         SysLog(NID_LOC, "Current System Time is %ls", alarmDateTime.ToString().GetPointer());
973
974         if (__regionMonitor.pLocation->IsValid())
975         {
976                 const int bufferTime = 5;   //Buffer of 5 seconds for determining the alarmTime;
977                 double minDistance = _MathUtils::GetShortestDistance(*__regionMonitor.pLocation, *__regionMonitor.pRegionList);
978                 long long newAlarmTime = ((int) minDistance / DEFAULT_AVG_SPEED) - bufferTime;  //Calculate the alarm time based on the shortest distance between current location and nearest region boundary.
979
980                 if (newAlarmTime > alarmTime)
981                 {
982                         alarmTime = newAlarmTime;
983                 }
984         }
985         alarmDateTime.AddSeconds(alarmTime);
986         result r = _AlarmImpl::GetInstance(__regionMonitor.pAlarm.get())->Set(alarmDateTime, 0, null);  
987         SysTryLog(NID_LOC, r == E_SUCCESS, "Failed to set the alarm for next cycle.");
988         
989         SysLog(NID_LOC, "Next alarm expires after %ld seconds.", alarmTime);
990 }
991 }}