1 /*
2  * Copyright (C) 2020 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 #include <chrono>
18 #include <aidl/android/hardware/gnss/IGnss.h>
19 #include <debug.h>
20 
21 #include "GnssBatching.h"
22 
23 namespace aidl {
24 namespace android {
25 namespace hardware {
26 namespace gnss {
27 namespace implementation {
28 namespace {
29 using Clock = std::chrono::steady_clock;
30 
31 constexpr size_t kBatchSize = 4;
32 }  // namsepace
33 
~GnssBatching()34 GnssBatching::~GnssBatching() {
35     stopImpl();
36 }
37 
init(const std::shared_ptr<IGnssBatchingCallback> & callback)38 ndk::ScopedAStatus GnssBatching::init(const std::shared_ptr<IGnssBatchingCallback>& callback) {
39     if (callback == nullptr) {
40         return ndk::ScopedAStatus::fromExceptionCode(FAILURE(IGnss::ERROR_INVALID_ARGUMENT));
41     }
42 
43     stopImpl();
44 
45     std::lock_guard<std::mutex> lock(mMtx);
46     mBatchedLocations.clear();
47     mCallback = callback;
48     return ndk::ScopedAStatus::ok();
49 }
50 
getBatchSize(int * size)51 ndk::ScopedAStatus GnssBatching::getBatchSize(int* size) {
52     *size = kBatchSize;
53     return ndk::ScopedAStatus::ok();
54 }
55 
start(const Options & options)56 ndk::ScopedAStatus GnssBatching::start(const Options& options) {
57     if (options.periodNanos < 0) {
58         return ndk::ScopedAStatus::fromExceptionCode(FAILURE(IGnss::ERROR_INVALID_ARGUMENT));
59     }
60 
61     const Clock::duration interval = std::chrono::nanoseconds(options.periodNanos);
62     const bool wakeUpOnFifoFull =
63         (options.flags & IGnssBatching::WAKEUP_ON_FIFO_FULL) ? true : false;
64 
65     stopImpl();
66 
67     std::lock_guard<std::mutex> lock(mMtx);
68     mRunning = true;
69     mThread = std::thread([this, interval, wakeUpOnFifoFull](){
70         Clock::time_point wakeupT = Clock::now() + interval;
71 
72         for (;; wakeupT += interval) {
73             std::unique_lock<std::mutex> lock(mMtx);
74             if ((mThreadNotification.wait_until(lock, wakeupT) == std::cv_status::no_timeout) &&
75                     !mRunning) {
76                 return;
77             }
78 
79             if (mLocation.has_value()) {
80                 batchLocationLocked(mLocation.value(), wakeUpOnFifoFull);
81             }
82         }
83     });
84 
85     return ndk::ScopedAStatus::ok();
86 }
87 
flush()88 ndk::ScopedAStatus GnssBatching::flush() {
89     std::lock_guard<std::mutex> lock(mMtx);
90     if (flushLocked()) {
91         return ndk::ScopedAStatus::ok();
92     } else {
93         return ndk::ScopedAStatus::fromServiceSpecificError(IGnss::ERROR_GENERIC);
94     }
95 }
96 
stop()97 ndk::ScopedAStatus GnssBatching::stop() {
98     stopImpl();
99     return ndk::ScopedAStatus::ok();
100 }
101 
cleanup()102 ndk::ScopedAStatus GnssBatching::cleanup() {
103     stopImpl();
104 
105     std::lock_guard<std::mutex> lock(mMtx);
106     flushLocked();
107     mCallback.reset();
108     return ndk::ScopedAStatus::ok();
109 }
110 
onGnssLocationCb(GnssLocation location)111 void GnssBatching::onGnssLocationCb(GnssLocation location) {
112     std::lock_guard<std::mutex> lock(mMtx);
113     mLocation = std::move(location);
114 }
115 
stopImpl()116 void GnssBatching::stopImpl() {
117     bool needJoin;
118 
119     {
120         std::lock_guard<std::mutex> lock(mMtx);
121         if (mThread.joinable()) {
122             mRunning = false;
123             mThreadNotification.notify_all();
124             needJoin = true;
125         } else {
126             needJoin = false;
127         }
128     }
129 
130     if (needJoin) {
131         mThread.join();
132     }
133 }
134 
batchLocationLocked(GnssLocation location,const bool wakeUpOnFifoFull)135 void GnssBatching::batchLocationLocked(GnssLocation location,
136                                        const bool wakeUpOnFifoFull) {
137     while (mBatchedLocations.size() >= kBatchSize) {
138         mBatchedLocations.pop_front();
139     }
140 
141     mBatchedLocations.push_back(location);
142 
143     if (wakeUpOnFifoFull && (mBatchedLocations.size() >= kBatchSize)) {
144         flushLocked();
145     }
146 }
147 
flushLocked()148 bool GnssBatching::flushLocked() {
149     if (mCallback) {
150         mCallback->gnssLocationBatchCb({mBatchedLocations.begin(),
151                                         mBatchedLocations.end()});
152         mBatchedLocations.clear();
153         return true;
154     } else {
155         return false;
156     }
157 }
158 
159 }  // namespace implementation
160 }  // namespace gnss
161 }  // namespace hardware
162 }  // namespace android
163 }  // namespace aidl
164