summaryrefslogtreecommitdiff
path: root/sensorservice/utils/direct_channel_buffer_reader.h
diff options
context:
space:
mode:
Diffstat (limited to 'sensorservice/utils/direct_channel_buffer_reader.h')
-rw-r--r--sensorservice/utils/direct_channel_buffer_reader.h138
1 files changed, 138 insertions, 0 deletions
diff --git a/sensorservice/utils/direct_channel_buffer_reader.h b/sensorservice/utils/direct_channel_buffer_reader.h
new file mode 100644
index 0000000..5c91ea5
--- /dev/null
+++ b/sensorservice/utils/direct_channel_buffer_reader.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hardware/sensors.h>
+
+#include <algorithm>
+#include <deque>
+
+// A utility class that reads sensor samples from a direct channel buffer.
+// Direct channel operates in a lockless manner and uses an atomic counter for
+// synchronization. This class implements the counter based synchronization
+// protocol and therefore guarantees data consistency. See
+// https://developer.android.com/reference/android/hardware/SensorDirectChannel
+// for more details on the atomic counter.
+//
+// Besides reading samples, the reader also supports keeping track of recently
+// obtained samples.
+//
+// DirectChannelBufferReader is not thread safe. It's the caller's responsibility
+// to serialize the calls, including the access to the returned sample
+// container.
+//
+// Example usage:
+// DirectChannelBufferReader reader(buf, 100);
+//
+// int num_samples = reader.Read();
+// const std::deque<sensors_event_t>& samples = reader.GetSampleContainer();
+// for (auto it = samples.end() - num_samples; it != samples.end(); it++) {
+// HandleNewSamples(*it);
+// }
+//
+// int num_samples_skipped;
+// reader.Read(&num_samples_skipped);
+// if (num_samples_skipped > 0) {
+// ReportMissedSamples(num_samples_skipped);
+// }
+//
+//
+// Another example:
+//
+// DirectChannelBufferReader reader(buf, 100);
+//
+// std::vector<sensors_event_t> Query(int start_time, int end_time) {
+// reader.Read();
+// std::vector<sensors_event_t> output;
+// for (auto& sample : reader_.GetSampleContainer()) {
+// if (sample.timestamp >= start_time && sample.timestamp < end_time) {
+// output.push_back(sample);
+// }
+// }
+// return output;
+// }
+
+class DirectChannelBufferReader {
+ public:
+ static constexpr int kErrorHeadOfBufferNotFound = -1;
+
+ // Constructor
+ // direct_channel_buffer: Pointer to the shared buffer where sensor samples
+ // are written into.
+ // buffer_size_samples: The size of direct_channel_buffer in number of
+ // samples.
+ DirectChannelBufferReader(const sensors_event_t* direct_channel_buffer,
+ int buffer_size_samples);
+
+ virtual ~DirectChannelBufferReader() {}
+
+ // Attempts to read samples from the direct channel buffer. Returns
+ // the number of samples read, or kErrorHeadOfBufferNotFound if the reader
+ // can not find the write head e.g. due to corrupted data in the buffer.
+ // The function is non-blocking and returns 0 if new samples are not available.
+ // The caller should control its polling based on external factors like
+ // events in a different subsystem (e.g. camera frame ready)
+ // After the call completes, the caller can use GetSampleContainer() to
+ // access the samples. Sometimes it may be possible for one or more samples
+ // in the direct channel buffer to be overwritten by the writter before the
+ // reader has a chance to read it, e.g. when the reader does not keep up
+ // with the writer. The number of samples that were lost / skipped is
+ // written to <num_samples_skipped>, if the argument is not null.
+ int Read(int* num_samples_skipped = nullptr);
+
+ // Returns the container that holds recent samples. New samples are appended
+ // to the end of the container when Read() is called. Samples from previous
+ // rounds of Read() are kept around in the container, except when the total
+ // samples exceeds <buffer_size_samples> - 1, in which case older samples
+ // would be truncated. The caller is free to remove samples from the
+ // container, e.g. after the samples are consumed.
+ //
+ // Calls to the returned container must be synchronized with calls to this
+ // instance of DirectChannelBufferReader.
+ std::deque<sensors_event_t>& GetSampleContainer() { return buffer_; }
+
+ protected:
+ // For test only.
+ virtual const sensors_event_t ReadOneSample(int index);
+
+ private:
+ // Truncates the head of <buffer_> until its size <= buffer_size_samples - 1.
+ void TruncateBuffer();
+
+ // Points to the direct channel buffer where the sensor writes samples into.
+ const volatile sensors_event_t* direct_channel_buffer_;
+
+ // The number of samples that <direct_channel_buffer_> is able to hold.
+ const int buffer_size_samples_;
+
+ // The atomic counter value of the last valid sample.
+ int64_t last_atomic_counter_ = 0;
+
+ // The index into <direct_channel_buffer_> that should be read next time.
+ int index_ = 0;
+
+ // The number of successive sensors_event_t reads with consecutive atomic
+ // counters values.
+ // E.g. 1 => streak_ = 1
+ // 5 6 7 => streak_ = 3
+ // 1 2 3 14 => streak_ = 1
+ // 1 2 3 14 15 => streak_ = 2
+ int streak_ = 0;
+
+ // The buffer holding recent samples.
+ std::deque<sensors_event_t> buffer_;
+};