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 #define LOG_TAG "FakeUserHal"
17
18 #include "FakeUserHal.h"
19
20 #include "UserHalHelper.h"
21
22 #include <VehicleUtils.h>
23 #include <utils/Log.h>
24 #include <utils/SystemClock.h>
25
26 namespace android {
27 namespace hardware {
28 namespace automotive {
29 namespace vehicle {
30 namespace fake {
31
32 namespace {
33
34 using ::aidl::android::hardware::automotive::vehicle::CreateUserResponse;
35 using ::aidl::android::hardware::automotive::vehicle::CreateUserStatus;
36 using ::aidl::android::hardware::automotive::vehicle::InitialUserInfoResponse;
37 using ::aidl::android::hardware::automotive::vehicle::InitialUserInfoResponseAction;
38 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
39 using ::aidl::android::hardware::automotive::vehicle::SwitchUserMessageType;
40 using ::aidl::android::hardware::automotive::vehicle::SwitchUserResponse;
41 using ::aidl::android::hardware::automotive::vehicle::SwitchUserStatus;
42 using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
43 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
44 using ::android::base::Error;
45 using ::android::base::Result;
46
47 constexpr int32_t INITIAL_USER_INFO = toInt(VehicleProperty::INITIAL_USER_INFO);
48 constexpr int32_t SWITCH_USER = toInt(VehicleProperty::SWITCH_USER);
49 constexpr int32_t CREATE_USER = toInt(VehicleProperty::CREATE_USER);
50 constexpr int32_t REMOVE_USER = toInt(VehicleProperty::REMOVE_USER);
51 constexpr int32_t USER_IDENTIFICATION_ASSOCIATION =
52 toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
53
getRequestId(const VehiclePropValue & value)54 VhalResult<int32_t> getRequestId(const VehiclePropValue& value) {
55 if (value.value.int32Values.size() < 1) {
56 return StatusError(StatusCode::INVALID_ARG)
57 << "no int32Values on property: " << value.toString();
58 }
59 return value.value.int32Values[0];
60 }
61
getSwitchUserMessageType(const VehiclePropValue & value)62 VhalResult<SwitchUserMessageType> getSwitchUserMessageType(const VehiclePropValue& value) {
63 if (value.value.int32Values.size() < 2) {
64 return StatusError(StatusCode::INVALID_ARG)
65 << "missing switch user message type on property: " << value.toString();
66 }
67 auto result = user_hal_helper::verifyAndCast<SwitchUserMessageType>(value.value.int32Values[1]);
68 if (!result.ok()) {
69 return StatusError(StatusCode::INVALID_ARG) << result.error().message();
70 }
71 return result.value();
72 }
73
74 } // namespace
75
isSupported(int32_t prop)76 bool FakeUserHal::isSupported(int32_t prop) {
77 switch (prop) {
78 case INITIAL_USER_INFO:
79 case SWITCH_USER:
80 case CREATE_USER:
81 case REMOVE_USER:
82 case USER_IDENTIFICATION_ASSOCIATION:
83 return true;
84 default:
85 return false;
86 }
87 }
88
onSetProperty(const VehiclePropValue & value)89 FakeUserHal::ValueResultType FakeUserHal::onSetProperty(const VehiclePropValue& value) {
90 ALOGV("onSetProperty(): %s", value.toString().c_str());
91
92 switch (value.prop) {
93 case INITIAL_USER_INFO:
94 return onSetInitialUserInfoResponse(value);
95 case SWITCH_USER:
96 return onSetSwitchUserResponse(value);
97 case CREATE_USER:
98 return onSetCreateUserResponse(value);
99 case REMOVE_USER:
100 ALOGI("REMOVE_USER is FYI only, nothing to do...");
101 return nullptr;
102 case USER_IDENTIFICATION_ASSOCIATION:
103 return onSetUserIdentificationAssociation(value);
104 default:
105 return StatusError(StatusCode::INVALID_ARG)
106 << "Unsupported property: " << value.toString();
107 }
108 }
109
onGetProperty(const VehiclePropValue & value) const110 FakeUserHal::ValueResultType FakeUserHal::onGetProperty(const VehiclePropValue& value) const {
111 ALOGV("onGetProperty(%s)", value.toString().c_str());
112 switch (value.prop) {
113 case INITIAL_USER_INFO:
114 case SWITCH_USER:
115 case CREATE_USER:
116 case REMOVE_USER:
117 ALOGE("onGetProperty(): %d is only supported on SET", value.prop);
118 return StatusError(StatusCode::INVALID_ARG) << "only supported on SET";
119 case USER_IDENTIFICATION_ASSOCIATION:
120 return onGetUserIdentificationAssociation(value);
121 default:
122 ALOGE("onGetProperty(): %d is not supported", value.prop);
123 return StatusError(StatusCode::INVALID_ARG) << "not supported by User HAL";
124 }
125 }
126
onGetUserIdentificationAssociation(const VehiclePropValue & value) const127 FakeUserHal::ValueResultType FakeUserHal::onGetUserIdentificationAssociation(
128 const VehiclePropValue& value) const {
129 std::scoped_lock<std::mutex> lockGuard(mLock);
130
131 if (mSetUserIdentificationAssociationResponseFromCmd == nullptr) {
132 return defaultUserIdentificationAssociation(value);
133 }
134 ALOGI("get(USER_IDENTIFICATION_ASSOCIATION): returning %s",
135 mSetUserIdentificationAssociationResponseFromCmd->toString().c_str());
136 auto newValue = mValuePool->obtain(*mSetUserIdentificationAssociationResponseFromCmd);
137 auto requestId = getRequestId(value);
138 if (requestId.ok()) {
139 // Must use the same requestId
140 newValue->value.int32Values[0] = *requestId;
141 } else {
142 ALOGE("get(USER_IDENTIFICATION_ASSOCIATION): no requestId on %s", value.toString().c_str());
143 return requestId.error();
144 }
145 return newValue;
146 }
147
onSetInitialUserInfoResponse(const VehiclePropValue & value)148 FakeUserHal::ValueResultType FakeUserHal::onSetInitialUserInfoResponse(
149 const VehiclePropValue& value) {
150 std::scoped_lock<std::mutex> lockGuard(mLock);
151
152 auto requestId = getRequestId(value);
153 if (!requestId.ok()) {
154 ALOGE("Failed to get requestId on set(INITIAL_USER_INFO): %s",
155 requestId.error().message().c_str());
156 return requestId.error();
157 }
158
159 if (value.areaId != 0) {
160 ALOGD("set(INITIAL_USER_INFO) called from lshal; storing it: %s", value.toString().c_str());
161 mInitialUserResponseFromCmd = mValuePool->obtain(value);
162 return nullptr;
163 }
164
165 ALOGD("set(INITIAL_USER_INFO) called from Android: %s", value.toString().c_str());
166 if (mInitialUserResponseFromCmd != nullptr) {
167 ALOGI("replying INITIAL_USER_INFO with lshal value: %s",
168 mInitialUserResponseFromCmd->toString().c_str());
169 return sendUserHalResponse(std::move(mInitialUserResponseFromCmd), *requestId);
170 }
171
172 // Returns default response
173 auto updatedValue = user_hal_helper::toVehiclePropValue(
174 *mValuePool, InitialUserInfoResponse{
175 .requestId = *requestId,
176 .action = InitialUserInfoResponseAction::DEFAULT,
177 });
178 ALOGI("no lshal response; replying with InitialUserInfoResponseAction::DEFAULT: %s",
179 updatedValue->toString().c_str());
180 return updatedValue;
181 }
182
onSetSwitchUserResponse(const VehiclePropValue & value)183 FakeUserHal::ValueResultType FakeUserHal::onSetSwitchUserResponse(const VehiclePropValue& value) {
184 std::scoped_lock<std::mutex> lockGuard(mLock);
185
186 auto requestId = getRequestId(value);
187 if (!requestId.ok()) {
188 ALOGE("Failed to get requestId on set(SWITCH_USER): %s",
189 requestId.error().message().c_str());
190 return requestId.error();
191 }
192
193 auto messageType = getSwitchUserMessageType(value);
194 if (!messageType.ok()) {
195 ALOGE("Failed to get messageType on set(SWITCH_USER): %s",
196 messageType.error().message().c_str());
197 return messageType.error();
198 }
199
200 if (value.areaId != 0) {
201 if (*messageType == SwitchUserMessageType::VEHICLE_REQUEST) {
202 // User HAL can also request a user switch, so we need to check it first
203 ALOGD("set(SWITCH_USER) called from lshal to emulate a vehicle request: %s",
204 value.toString().c_str());
205 return mValuePool->obtain(value);
206 }
207 // Otherwise, we store it
208 ALOGD("set(SWITCH_USER) called from lshal; storing it: %s", value.toString().c_str());
209 mSwitchUserResponseFromCmd = mValuePool->obtain(value);
210 return nullptr;
211 }
212 ALOGD("set(SWITCH_USER) called from Android: %s", value.toString().c_str());
213
214 if (mSwitchUserResponseFromCmd != nullptr) {
215 ALOGI("replying SWITCH_USER with lshal value: %s",
216 mSwitchUserResponseFromCmd->toString().c_str());
217 return sendUserHalResponse(std::move(mSwitchUserResponseFromCmd), *requestId);
218 }
219
220 if (*messageType == SwitchUserMessageType::LEGACY_ANDROID_SWITCH ||
221 *messageType == SwitchUserMessageType::ANDROID_POST_SWITCH) {
222 ALOGI("request is %s; ignoring it", toString(*messageType).c_str());
223 return nullptr;
224 }
225
226 // Returns default response
227 auto updatedValue = user_hal_helper::toVehiclePropValue(
228 *mValuePool, SwitchUserResponse{
229 .requestId = *requestId,
230 .messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
231 .status = SwitchUserStatus::SUCCESS,
232 });
233 ALOGI("no lshal response; replying with VEHICLE_RESPONSE / SUCCESS: %s",
234 updatedValue->toString().c_str());
235 return updatedValue;
236 }
237
onSetCreateUserResponse(const VehiclePropValue & value)238 FakeUserHal::ValueResultType FakeUserHal::onSetCreateUserResponse(const VehiclePropValue& value) {
239 std::scoped_lock<std::mutex> lockGuard(mLock);
240
241 auto requestId = getRequestId(value);
242 if (!requestId.ok()) {
243 ALOGE("Failed to get requestId on set(CREATE_USER): %s",
244 requestId.error().message().c_str());
245 return requestId.error();
246 }
247
248 if (value.areaId != 0) {
249 ALOGD("set(CREATE_USER) called from lshal; storing it: %s", value.toString().c_str());
250 mCreateUserResponseFromCmd = mValuePool->obtain(value);
251 return nullptr;
252 }
253 ALOGD("set(CREATE_USER) called from Android: %s", value.toString().c_str());
254
255 if (mCreateUserResponseFromCmd != nullptr) {
256 ALOGI("replying CREATE_USER with lshal value: %s",
257 mCreateUserResponseFromCmd->toString().c_str());
258 return sendUserHalResponse(std::move(mCreateUserResponseFromCmd), *requestId);
259 }
260
261 // Returns default response
262 auto updatedValue = user_hal_helper::toVehiclePropValue(
263 *mValuePool, CreateUserResponse{
264 .requestId = *requestId,
265 .status = CreateUserStatus::SUCCESS,
266 });
267 ALOGI("no lshal response; replying with SUCCESS: %s", updatedValue->toString().c_str());
268 return updatedValue;
269 }
270
onSetUserIdentificationAssociation(const VehiclePropValue & value)271 FakeUserHal::ValueResultType FakeUserHal::onSetUserIdentificationAssociation(
272 const VehiclePropValue& value) {
273 std::scoped_lock<std::mutex> lockGuard(mLock);
274
275 auto requestId = getRequestId(value);
276 if (!requestId.ok()) {
277 ALOGE("Failed to get requestId on set(USER_IDENTIFICATION_ASSOCIATION): %s",
278 requestId.error().message().c_str());
279 return requestId.error();
280 }
281
282 if (value.areaId != 0) {
283 ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from lshal; storing it: %s",
284 value.toString().c_str());
285 mSetUserIdentificationAssociationResponseFromCmd = mValuePool->obtain(value);
286 return nullptr;
287 }
288 ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from Android: %s", value.toString().c_str());
289
290 if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
291 ALOGI("replying USER_IDENTIFICATION_ASSOCIATION with lshal value: %s",
292 mSetUserIdentificationAssociationResponseFromCmd->toString().c_str());
293 // Not moving response so it can be used on GET requests
294 auto copy = mValuePool->obtain(*mSetUserIdentificationAssociationResponseFromCmd);
295 return sendUserHalResponse(std::move(copy), *requestId);
296 }
297 // Returns default response
298 return defaultUserIdentificationAssociation(value);
299 }
300
defaultUserIdentificationAssociation(const VehiclePropValue & request)301 FakeUserHal::ValueResultType FakeUserHal::defaultUserIdentificationAssociation(
302 const VehiclePropValue& request) {
303 // TODO(b/159498909): return a response with NOT_ASSOCIATED_ANY_USER for all requested types
304 ALOGE("no lshal response for %s; replying with NOT_AVAILABLE", request.toString().c_str());
305 return StatusError(StatusCode::NOT_AVAILABLE) << "not set by lshal";
306 }
307
sendUserHalResponse(VehiclePropValuePool::RecyclableType response,int32_t requestId)308 FakeUserHal::ValueResultType FakeUserHal::sendUserHalResponse(
309 VehiclePropValuePool::RecyclableType response, int32_t requestId) {
310 switch (response->areaId) {
311 case 1:
312 ALOGD("returning response with right request id");
313 response->value.int32Values[0] = requestId;
314 break;
315 case 2:
316 ALOGD("returning response with wrong request id");
317 response->value.int32Values[0] = -requestId;
318 break;
319 case 3:
320 ALOGD("not generating a property change event because of lshal prop: %s",
321 response->toString().c_str());
322 return StatusError(StatusCode::NOT_AVAILABLE)
323 << "not generating a property change event because of lshal prop: "
324 << response->toString();
325 default:
326 ALOGE("invalid action on lshal response: %s", response->toString().c_str());
327 return StatusError(StatusCode::INTERNAL_ERROR)
328 << "invalid action on lshal response: " << response->toString();
329 }
330
331 // Update area ID to 0 since this is a global property (and the area ID was only set to emulate
332 // the request id behavior).
333 response->areaId = 0;
334 ALOGD("updating property to: %s", response->toString().c_str());
335 return response;
336 }
337
showDumpHelp() const338 std::string FakeUserHal::showDumpHelp() const {
339 return fmt::format("{}: dumps state used for user management\n", kUserHalDumpOption);
340 }
341
dump() const342 std::string FakeUserHal::dump() const {
343 std::scoped_lock<std::mutex> lockGuard(mLock);
344
345 std::string info;
346 if (mInitialUserResponseFromCmd != nullptr) {
347 info += fmt::format("InitialUserInfo response: {}\n",
348 mInitialUserResponseFromCmd->toString());
349 } else {
350 info += "No InitialUserInfo response\n";
351 }
352 if (mSwitchUserResponseFromCmd != nullptr) {
353 info += fmt::format("SwitchUser response: {}\n", mSwitchUserResponseFromCmd->toString());
354 } else {
355 info += "No SwitchUser response\n";
356 }
357 if (mCreateUserResponseFromCmd != nullptr) {
358 info += fmt::format("CreateUser response: {}\n", mCreateUserResponseFromCmd->toString());
359 } else {
360 info += "No CreateUser response\n";
361 }
362 if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
363 info += fmt::format("SetUserIdentificationAssociation response: {}\n",
364 mSetUserIdentificationAssociationResponseFromCmd->toString());
365 } else {
366 info += "No SetUserIdentificationAssociation response\n";
367 }
368 return info;
369 }
370
371 } // namespace fake
372 } // namespace vehicle
373 } // namespace automotive
374 } // namespace hardware
375 } // namespace android
376