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