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