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         std::unique_lock<std::mutex> lock(mMtx);
71         if (!mRunning) {
72             return;
73         }
74 
75         Clock::time_point wakeupT = Clock::now() + interval;
76         for (;; wakeupT += interval) {
77             mThreadNotification.wait_until(lock, wakeupT);
78             if (!mRunning) {
79                 return;
80             }
81 
82             if (mLocation.has_value()) {
83                 batchLocationLocked(mLocation.value(), wakeUpOnFifoFull);
84             }
85         }
86     });
87 
88     return ndk::ScopedAStatus::ok();
89 }
90 
flush()91 ndk::ScopedAStatus GnssBatching::flush() {
92     std::lock_guard<std::mutex> lock(mMtx);
93     if (flushLocked()) {
94         return ndk::ScopedAStatus::ok();
95     } else {
96         return ndk::ScopedAStatus::fromServiceSpecificError(IGnss::ERROR_GENERIC);
97     }
98 }
99 
stop()100 ndk::ScopedAStatus GnssBatching::stop() {
101     stopImpl();
102     return ndk::ScopedAStatus::ok();
103 }
104 
cleanup()105 ndk::ScopedAStatus GnssBatching::cleanup() {
106     stopImpl();
107 
108     std::lock_guard<std::mutex> lock(mMtx);
109     flushLocked();
110     mCallback.reset();
111     return ndk::ScopedAStatus::ok();
112 }
113 
onGnssLocationCb(GnssLocation location)114 void GnssBatching::onGnssLocationCb(GnssLocation location) {
115     std::lock_guard<std::mutex> lock(mMtx);
116     mLocation = std::move(location);
117 }
118 
stopImpl()119 void GnssBatching::stopImpl() {
120     bool needJoin;
121 
122     {
123         std::lock_guard<std::mutex> lock(mMtx);
124         if (mThread.joinable()) {
125             mRunning = false;
126             mThreadNotification.notify_all();
127             needJoin = true;
128         } else {
129             needJoin = false;
130         }
131     }
132 
133     if (needJoin) {
134         mThread.join();
135     }
136 }
137 
batchLocationLocked(GnssLocation location,const bool wakeUpOnFifoFull)138 void GnssBatching::batchLocationLocked(GnssLocation location,
139                                        const bool wakeUpOnFifoFull) {
140     while (mBatchedLocations.size() >= kBatchSize) {
141         mBatchedLocations.pop_front();
142     }
143 
144     mBatchedLocations.push_back(location);
145 
146     if (wakeUpOnFifoFull && (mBatchedLocations.size() >= kBatchSize)) {
147         flushLocked();
148     }
149 }
150 
flushLocked()151 bool GnssBatching::flushLocked() {
152     if (mCallback) {
153         mCallback->gnssLocationBatchCb({mBatchedLocations.begin(),
154                                         mBatchedLocations.end()});
155         mBatchedLocations.clear();
156         return true;
157     } else {
158         return false;
159     }
160 }
161 
162 }  // namespace implementation
163 }  // namespace gnss
164 }  // namespace hardware
165 }  // namespace android
166 }  // namespace aidl
167