1 /*
2  * Copyright (C) 2018 The Android Open Source Project
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 #ifndef CHRE_CORE_GNSS_MANAGER_H_
18 #define CHRE_CORE_GNSS_MANAGER_H_
19 
20 #include <cstdint>
21 
22 #include "chre/core/nanoapp.h"
23 #include "chre/core/settings.h"
24 #include "chre/platform/platform_gnss.h"
25 #include "chre/util/non_copyable.h"
26 #include "chre/util/system/debug_dump.h"
27 #include "chre/util/time.h"
28 
29 namespace chre {
30 
31 class GnssManager;
32 
33 /**
34  * A helper class that manages requests for a GNSS location or measurement
35  * session.
36  */
37 class GnssSession {
38  public:
39   /**
40    * Adds a request to a session asynchronously. The result is delivered
41    * through a CHRE_EVENT_GNSS_ASYNC_RESULT event.
42    *
43    * @param nanoapp The nanoapp adding the request.
44    * @param minInterval The minimum reporting interval for results.
45    * @param timeToNext The amount of time that the GNSS system is allowed to
46    *        delay generating a report.
47    * @param cookie A cookie that is round-tripped to provide context to the
48    *        nanoapp making the request.
49    *
50    * @return true if the request was accepted for processing.
51    */
52   bool addRequest(Nanoapp *nanoapp, Milliseconds minInterval,
53                   Milliseconds minTimeToNext, const void *cookie);
54 
55   /**
56    * Removes a request from a session asynchronously. The result is delivered
57    * through a CHRE_EVENT_GNSS_ASYNC_RESULT event.
58    *
59    * @param nanoapp The nanoapp removing the request.
60    * @param cookie A cookie that is round-tripped to provide context to the
61    *        nanoapp making the request.
62    *
63    * @return true if the request was accepted for processing.
64    */
65   bool removeRequest(Nanoapp *nanoapp, const void *cookie);
66 
67   /**
68    * Handles the result of a request to the PlatformGnss to request a change to
69    * the session.
70    *
71    * @param enabled true if the session is currently active.
72    * @param errorCode an error code that is used to indicate success or what
73    *        type of error has occured. See chreError enum in the CHRE API for
74    *        additional details.
75    */
76   void handleStatusChange(bool enabled, uint8_t errorCode);
77 
78   /**
79    * Handles a CHRE GNSS report (location/data) event.
80    *
81    * @param event The GNSS report event provided to the GNSS session. This
82    *        memory is guaranteed not to be modified until it has been explicitly
83    *        released through the PlatformGnss instance.
84    */
85   void handleReportEvent(void *event);
86 
87   /**
88    * @return true if an async response is pending from GNSS. This method should
89    * be used to check if a GNSS session request is in flight.
90    */
asyncResponsePending()91   bool asyncResponsePending() const {
92     return !mStateTransitions.empty() || mInternalRequestPending;
93   }
94 
95   /**
96    * Invoked when the host notifies CHRE of a settings change.
97    *
98    * @param setting The setting that changed.
99    * @param state The new setting state.
100    */
101   void onSettingChanged(Setting setting, SettingState state);
102 
103   /**
104    * Updates the platform GNSS request according to the current state. It should
105    * be used to synchronize the GNSS to the desired state, e.g. for setting
106    * updates or handling a state resync request.
107    *
108    * @param forceUpdate If true, force the platform GNSS request to be made.
109    *
110    * @return true if the invocation resulted in dispatching an internal
111    *         request to control the platform layer
112    */
113   bool updatePlatformRequest(bool forceUpdate = false);
114 
115   /**
116    * Invoked as a result of a requestStateResync() callback from the GNSS PAL.
117    * Runs in the context of the CHRE thread.
118    */
119   void handleRequestStateResyncCallbackSync();
120 
121   /**
122    * Prints state in a string buffer. Must only be called from the context of
123    * the main CHRE thread.
124    *
125    * @param debugDump The debug dump wrapper where a string can be printed
126    *     into one of the buffers.
127    */
128   void logStateToBuffer(DebugDumpWrapper &debugDump) const;
129 
130  private:
131   /**
132    * Tracks a nanoapp that has subscribed to a session and the reporting
133    * interval.
134    */
135   struct Request {
136     //! The nanoapp instance ID that made this request.
137     uint32_t nanoappInstanceId;
138 
139     //! The interval of results requested.
140     Milliseconds minInterval;
141   };
142 
143   //! Internal struct with data needed to log last X session requests
144   struct SessionRequestLog {
SessionRequestLogSessionRequestLog145     SessionRequestLog(Nanoseconds timestampIn, uint32_t instanceIdIn,
146                       Milliseconds intervalIn, bool startIn)
147         : timestamp(timestampIn),
148           instanceId(instanceIdIn),
149           interval(intervalIn),
150           start(startIn) {}
151     Nanoseconds timestamp;
152     uint32_t instanceId;
153     Milliseconds interval;
154     bool start;
155   };
156 
157   /**
158    * Tracks the state of the GNSS engine.
159    */
160   struct StateTransition {
161     //! The nanoapp instance ID that prompted the change.
162     uint32_t nanoappInstanceId;
163 
164     //! The cookie provided to the CHRE API when the nanoapp requested a
165     //! change to the state of the GNSS engine.
166     const void *cookie;
167 
168     //! The target state of the GNSS engine.
169     bool enable;
170 
171     //! The target minimum reporting interval for the GNSS engine. This is only
172     //! valid if enable is set to true.
173     Milliseconds minInterval;
174   };
175 
176   //! The event type of the session's report data.
177   const uint16_t kReportEventType;
178 
179   //! The request type to start and stop a session.
180   uint8_t mStartRequestType;
181   uint8_t mStopRequestType;
182 
183   //! The session name, used in logging state.
184   const char *mName;
185 
186   //! The maximum number of pending state transitions allowed.
187   static constexpr size_t kMaxGnssStateTransitions = 8;
188 
189   //! The queue of state transitions for the GNSS engine. Only one asynchronous
190   //! state transition can be in flight at one time. Any further requests are
191   //! queued here.
192   ArrayQueue<StateTransition, kMaxGnssStateTransitions> mStateTransitions;
193 
194   //! The list of most recent session request logs
195   static constexpr size_t kNumSessionRequestLogs = 10;
196   ArrayQueue<SessionRequestLog, kNumSessionRequestLogs> mSessionRequestLogs;
197 
198   //! The request multiplexer for GNSS session requests.
199   DynamicVector<Request> mRequests;
200 
201   //! The current report interval being sent to the session. This is only valid
202   //! if the mRequests is non-empty.
203   Milliseconds mCurrentInterval = Milliseconds(UINT64_MAX);
204 
205   //! The state of the last successful request to the platform.
206   bool mPlatformEnabled = false;
207 
208   //! True if a request from the CHRE framework is currently pending.
209   bool mInternalRequestPending = false;
210 
211   //! True if a setting change event is pending to be processed.
212   bool mSettingChangePending = false;
213 
214   //! True if a state resync callback is pending to be processed.
215   bool mResyncPending = false;
216 
217   // Allows GnssManager to access constructor.
218   friend class GnssManager;
219 
220   /**
221    * Constructs a GnssSesson.
222    *
223    * @param reportEventType The report event type of this GNSS session.
224    *        Currently, only CHRE_EVENT_GNSS_LOCATION for a location session and
225    *        CHRE_EVENT_GNSS_LOCATION for a measurement session are supported.
226    */
227   GnssSession(uint16_t reportEventType);
228 
229   /**
230    * Configures the GNSS engine to be enabled/disabled. If enable is set to true
231    * then the minInterval and minTimeToNext values are valid.
232    *
233    * @param nanoapp The nanoapp requesting the state change for the engine.
234    * @param enable Whether to enable or disable the engine.
235    * @param minInterval The minimum reporting interval requested by the nanoapp.
236    * @param minTimeToNext The minimum time to the next report.
237    * @param cookie The cookie provided by the nanoapp to round-trip for context.
238    *
239    * @return true if the request was accepted.
240    */
241   bool configure(Nanoapp *nanoapp, bool enable, Milliseconds minInterval,
242                  Milliseconds minTimeToNext, const void *cookie);
243 
244   /**
245    * Checks if a nanoapp has an open session request.
246    *
247    * @param instanceId The nanoapp instance ID to search for.
248    * @param requestIndex A pointer to an index to populate if the nanoapp has an
249    *        open session request.
250    *
251    * @return true if the provided instanceId was found.
252    */
253   bool nanoappHasRequest(uint32_t instanceId,
254                          size_t *requestIndex = nullptr) const;
255 
256   /**
257    * Adds a request for a session to the queue of state transitions.
258    *
259    * @param instanceId The nanoapp instance ID requesting a session.
260    * @param enable Whether the session is being enabled or disabled for this
261    *        nanoapp.
262    * @param minInterval The minimum interval requested by the nanoapp.
263    * @param cookie A cookie that is round-tripped to the nanoapp for context.
264    *
265    * @return true if the state transition was added to the queue.
266    */
267   bool addRequestToQueue(uint32_t instanceId, bool enable,
268                          Milliseconds minInterval, const void *cookie);
269 
270   /**
271    * @return true if the session is currently enabled.
272    */
273   bool isEnabled() const;
274 
275   /**
276    * Determines if a change to the session state is required given a set of
277    * parameters.
278    *
279    * @param requestedState The target state requested by a nanoapp.
280    * @param minInterval The minimum reporting interval.
281    * @param nanoappHasRequest If the nanoapp already has a request.
282    * @param requestIndex The index of the request in the list of open requests
283    *        if nanoappHasRequest is set to true.
284    *
285    * @return true if a state transition is required.
286    */
287   bool stateTransitionIsRequired(bool requestedState, Milliseconds minInterval,
288                                  bool nanoappHasRequest,
289                                  size_t requestIndex) const;
290 
291   /**
292    * Updates the session requests given a nanoapp and the interval requested.
293    *
294    * @param enable true if enabling the session.
295    * @param minInterval the minimum reporting interval if enable is true.
296    * @param instanceId the nanoapp instance ID that owns the request.
297    *
298    * @return true if the session request list was updated.
299    */
300   bool updateRequests(bool enable, Milliseconds minInterval,
301                       uint32_t instanceId);
302 
303   /**
304    * Posts the result of a GNSS session add/remove request.
305    *
306    * @param instanceId The nanoapp instance ID that made the request.
307    * @param success true if the operation was successful.
308    * @param enable true if enabling the session.
309    * @param minInterval the minimum reporting interval.
310    * @param errorCode the error code as a result of this operation.
311    * @param cookie the cookie that the nanoapp is provided for context.
312    *
313    * @return true if the event was successfully posted.
314    */
315   bool postAsyncResultEvent(uint32_t instanceId, bool success, bool enable,
316                             Milliseconds minInterval, uint8_t errorCode,
317                             const void *cookie);
318 
319   /**
320    * Calls through to postAsyncResultEvent but invokes FATAL_ERROR if the
321    * event is not posted successfully. This is used in asynchronous contexts
322    * where a nanoapp could be stuck waiting for a response but CHRE failed to
323    * enqueue one. For parameter details,
324    * @see postAsyncResultEvent
325    */
326   void postAsyncResultEventFatal(uint32_t instanceId, bool success, bool enable,
327                                  Milliseconds minInterval, uint8_t errorCode,
328                                  const void *cookie);
329 
330   /**
331    * Handles the result of a request to PlatformGnss to change the state of
332    * the session. See the handleStatusChange method which may be called from
333    * any thread. This method is intended to be invoked on the CHRE event loop
334    * thread.
335    *
336    * @param enabled true if the session was enabled
337    * @param errorCode an error code that is provided to indicate success.
338    */
339   void handleStatusChangeSync(bool enabled, uint8_t errorCode);
340 
341   /**
342    * Releases a GNSS report event after nanoapps have consumed it.
343    *
344    * @param eventType the type of event being freed.
345    * @param eventData a pointer to the scan event to release.
346    */
347   static void freeReportEventCallback(uint16_t eventType, void *eventData);
348 
349   /**
350    * Configures PlatformGnss based on session settings.
351    *
352    * @return true if PlatformGnss has accepted the setting.
353    */
354   bool controlPlatform(bool enable, Milliseconds minInterval,
355                        Milliseconds minTimeToNext);
356 
357   /**
358    * Add a log to list of session logs possibly pushing out the oldest log.
359    *
360    * @param nanoappInstanceId the instance of id of nanoapp requesting
361    * @param interval the interval in milliseconds for request
362    * @param start true if the is a start request, false if a stop request
363    */
364   void addSessionRequestLog(uint32_t nanoappInstanceId, Milliseconds interval,
365                             bool start);
366 
367   /**
368    * Dispatches pending state transitions on the queue until the first one
369    * succeeds.
370    */
371   void dispatchQueuedStateTransitions();
372 };
373 
374 /**
375  * The GnssManager handles platform init, capability query, and delagates debug
376  * dump and all GNSS request management to GnssSession(s), which includes
377  * multiplexing multiple requests into one for the platform to handle.
378  *
379  * This class is effectively a singleton as there can only be one instance of
380  * the PlatformGnss instance.
381  */
382 class GnssManager : public NonCopyable {
383  public:
384   /**
385    * Constructs a GnssManager.
386    */
387   GnssManager();
388 
389   /**
390    * Initializes the underlying platform-specific GNSS module. Must be called
391    * prior to invoking any other methods in this class.
392    */
393   void init();
394 
395   /**
396    * @return the GNSS capabilities exposed by this platform.
397    */
398   uint32_t getCapabilities();
399 
getLocationSession()400   GnssSession &getLocationSession() {
401     return mLocationSession;
402   };
403 
getMeasurementSession()404   GnssSession &getMeasurementSession() {
405     return mMeasurementSession;
406   };
407 
408   /**
409    * Invoked when the host notifies CHRE of a settings change.
410    *
411    * @param setting The setting that changed.
412    * @param state The new setting state.
413    */
414   void onSettingChanged(Setting setting, SettingState state);
415 
416   /**
417    * Invoked as a result of a requestStateResync() callback from the GNSS PAL.
418    * Runs asynchronously in the context of the callback immediately.
419    */
420   void handleRequestStateResyncCallback();
421 
422   /**
423    * Invoked as a result of a requestStateResync() callback from the GNSS PAL.
424    * Runs in the context of the CHRE thread.
425    */
426   void handleRequestStateResyncCallbackSync();
427 
428   /**
429    * @param nanoapp The nanoapp invoking
430    * chreGnssConfigurePassiveLocationListener.
431    * @param enable true to enable the configuration.
432    *
433    * @return true if the configuration succeeded.
434    */
435   bool configurePassiveLocationListener(Nanoapp *nanoapp, bool enable);
436 
437   /**
438    * Prints state in a string buffer. Must only be called from the context of
439    * the main CHRE thread.
440    *
441    * @param debugDump The debug dump wrapper where a string can be printed
442    *     into one of the buffers.
443    */
444   void logStateToBuffer(DebugDumpWrapper &debugDump) const;
445 
446  private:
447   // Allows GnssSession to access mPlatformGnss.
448   friend class GnssSession;
449 
450   //! The platform GNSS interface.
451   PlatformGnss mPlatformGnss;
452 
453   //! The instance of location session.
454   GnssSession mLocationSession;
455 
456   //! The instance of measurement session.
457   GnssSession mMeasurementSession;
458 
459   //! The list of instance ID of nanoapps that has a passive location listener
460   //! request.
461   DynamicVector<uint32_t> mPassiveLocationListenerNanoapps;
462 
463   //! true if the passive location listener is enabled at the platform.
464   bool mPlatformPassiveLocationListenerEnabled;
465 
466   /**
467    * @param nanoappInstanceId The instance ID of the nanoapp to check.
468    * @param index If non-null and this function returns true, stores the index
469    * of mPassiveLocationListenerNanoapps where the instance ID is stored.
470    *
471    * @return true if the nanoapp currently has a passive location listener
472    * request.
473    */
474   bool nanoappHasPassiveLocationListener(uint32_t nanoappInstanceId,
475                                          size_t *index = nullptr);
476 
477   /**
478    * Helper function to invoke configurePassiveLocationListener at the platform
479    * and handle the result.
480    *
481    * @param enable true to enable the configuration.
482    *
483    * @return true if success.
484    */
485   bool platformConfigurePassiveLocationListener(bool enable);
486 };
487 
488 }  // namespace chre
489 
490 #endif  // CHRE_CORE_GNSS_MANAGER_H_
491