1 /*
2 * Copyright (C) 2024 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 <libminradio/ResponseTracker.h>
18
19 #include <libminradio/debug.h>
20
21 #include <random>
22
23 namespace android::hardware::radio::minimal {
24
25 using namespace ::android::hardware::radio::minimal::binder_printing;
26 using ::aidl::android::hardware::radio::RadioError;
27 using ::aidl::android::hardware::radio::RadioResponseInfo;
28 using ::ndk::ScopedAStatus;
29
toError(const ScopedAStatus & status)30 RadioError ResponseTrackerResultBase::toError(const ScopedAStatus& status) {
31 CHECK(!status.isOk()) << "statusToError called with no error";
32 return RadioError::GENERIC_FAILURE;
33 }
34
ResponseTrackerResultBase(const char * descriptor)35 ResponseTrackerResultBase::ResponseTrackerResultBase(const char* descriptor)
36 : ResponseTrackerResultBase(descriptor, RadioError::RADIO_NOT_AVAILABLE) {}
37
ResponseTrackerResultBase(const char * descriptor,RadioError error)38 ResponseTrackerResultBase::ResponseTrackerResultBase(const char* descriptor, RadioError error)
39 : mDescriptor(descriptor), mError(error) {}
40
ResponseTrackerResultBase(const char * descriptor,ScopedAStatus st)41 ResponseTrackerResultBase::ResponseTrackerResultBase(const char* descriptor, ScopedAStatus st)
42 : ResponseTrackerResultBase(descriptor, toError(st)) {}
43
isOk() const44 bool ResponseTrackerResultBase::isOk() const {
45 return mError == RadioError::NONE;
46 }
47
expectOk() const48 bool ResponseTrackerResultBase::expectOk() const {
49 if (isOk()) return true;
50 LOG(ERROR) << "Request for " << mDescriptor << " failed: " << mError;
51 return false;
52 }
53
getError() const54 RadioError ResponseTrackerResultBase::getError() const {
55 return mError;
56 }
57
getDescriptor() const58 const char* ResponseTrackerResultBase::getDescriptor() const {
59 return mDescriptor;
60 }
61
ScopedSerial(int32_t serial,ResponseTrackerBase * tracker)62 ResponseTrackerBase::ScopedSerial::ScopedSerial(int32_t serial, ResponseTrackerBase* tracker)
63 : mSerial(serial), mTracker(tracker) {}
64
~ScopedSerial()65 ResponseTrackerBase::ScopedSerial::~ScopedSerial() {
66 if (mIsReleased) return;
67 mTracker->cancelTracking(*this);
68 }
69
operator int32_t() const70 ResponseTrackerBase::ScopedSerial::operator int32_t() const {
71 CHECK(!mIsReleased) << "ScopedSerial " << mSerial << " is not valid anymore";
72 return mSerial;
73 }
74
release()75 void ResponseTrackerBase::ScopedSerial::release() {
76 mIsReleased = true;
77 }
78
initialSerial()79 int32_t ResponseTrackerBase::initialSerial() {
80 /* Android framework tends to start request serial numbers from 0, so let's pick something from
81 * the second quarter of int32_t negative range. This way the chance of having a conflict is
82 * closer to zero. */
83 static const int32_t rangeSize = std::abs(std::numeric_limits<int32_t>::min() / 4);
84 static const int32_t rangeStart = std::numeric_limits<int32_t>::min() + rangeSize;
85
86 static std::random_device generator;
87 static std::uniform_int_distribution<int32_t> distribution(rangeStart, rangeStart + rangeSize);
88
89 return distribution(generator);
90 }
91
newSerial()92 ResponseTrackerBase::ScopedSerial ResponseTrackerBase::newSerial() {
93 std::unique_lock lck(mSerialsGuard);
94
95 auto serial = mSerial++;
96 if (serial == 0) [[unlikely]] {
97 serial = mSerial++;
98 }
99 if constexpr (debug::kSuperCrazyVerbose) {
100 LOG(VERBOSE) << "Tracking " << serial << " internally";
101 }
102
103 auto inserted = mTrackedSerials.emplace(serial, nullptr).second;
104 CHECK(inserted) << "Detected tracked serials conflict at " << serial;
105
106 return {serial, this};
107 }
108
isTracked(int32_t serial) const109 bool ResponseTrackerBase::isTracked(int32_t serial) const {
110 std::unique_lock lck(mSerialsGuard);
111 return mTrackedSerials.contains(serial);
112 }
113
cancelTracking(ResponseTrackerBase::ScopedSerial & serial)114 void ResponseTrackerBase::cancelTracking(ResponseTrackerBase::ScopedSerial& serial) {
115 std::unique_lock lck(mSerialsGuard);
116 auto erased = mTrackedSerials.erase(serial);
117 CHECK(erased == 1) << "Couldn't cancel tracking " << serial;
118 LOG(VERBOSE) << "Cancelled tracking " << serial << " internally";
119 serial.release();
120 }
121
handle(const RadioResponseInfo & info,std::unique_ptr<ResponseTrackerResultBase> result)122 ScopedAStatus ResponseTrackerBase::handle(const RadioResponseInfo& info,
123 std::unique_ptr<ResponseTrackerResultBase> result) {
124 std::unique_lock lck(mSerialsGuard);
125 if constexpr (debug::kSuperCrazyVerbose) {
126 LOG(VERBOSE) << "Handling " << info.serial << " internally (not sending to the framework)";
127 }
128
129 auto it = mTrackedSerials.find(info.serial);
130 CHECK(it != mTrackedSerials.end()) << "Request not tracked: " << info;
131 CHECK(it->second == nullptr) << "Request already handled: " << info;
132 it->second = std::move(result);
133
134 return ScopedAStatus::ok();
135 }
136
getResultBase(ResponseTrackerBase::ScopedSerial & serial)137 std::unique_ptr<ResponseTrackerResultBase> ResponseTrackerBase::getResultBase(
138 ResponseTrackerBase::ScopedSerial& serial) {
139 std::unique_lock lck(mSerialsGuard);
140 auto node = mTrackedSerials.extract(serial);
141 CHECK(node.key()) << "Request " << serial << " is not tracked";
142 if (!node.mapped()) {
143 LOG(WARNING) << "Didn't get result for " << serial
144 << ". It may either mean setResponseFunctions has reset the callbacks or"
145 " the callback wasn't called synchronously from the scope of "
146 "request method implementation.";
147 serial.release();
148 return nullptr;
149 }
150 if constexpr (debug::kSuperCrazyVerbose) {
151 LOG(VERBOSE) << "Finished tracking " << serial << " internally";
152 }
153 serial.release();
154 return std::move(node.mapped());
155 }
156
157 // This symbol silences "Mismatched versions of delegator and implementation" errors from Delegator
158 // implementation. In this specific case, Delegators are used to encapsulate incoming callbacks, not
159 // outgoing interfaces - so clamping delegator interface version to lower than implementation's
160 // version wouldn't make any difference - the local binary wouldn't know what to do with a newer
161 // interface anyways. This happens when Radio HAL (which includes callback interfaces) defined on
162 // system partition is newer than one used to build local binary (usually on vendor partition).
assert2_no_op(const char *,int,const char *,const char *)163 extern "C" void assert2_no_op(const char*, int, const char*, const char*) {}
164
165 } // namespace android::hardware::radio::minimal
166