1 /*
2 * Copyright (C) 2021 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 #define LOG_TAG "android.hardware.usb.aidl-service"
18
19 #include <android-base/logging.h>
20 #include <android-base/properties.h>
21 #include <android-base/strings.h>
22 #include <assert.h>
23 #include <dirent.h>
24 #include <pthread.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <chrono>
29 #include <regex>
30 #include <thread>
31 #include <unordered_map>
32
33 #include <cutils/uevent.h>
34 #include <sys/epoll.h>
35 #include <utils/Errors.h>
36 #include <utils/StrongPointer.h>
37
38 #include "Usb.h"
39
40 using android::base::GetProperty;
41 using android::base::Trim;
42
43 namespace aidl {
44 namespace android {
45 namespace hardware {
46 namespace usb {
47
48 // Set by the signal handler to destroy the thread
49 volatile bool destroyThread;
50
51 constexpr char kConsole[] = "init.svc.console";
52 constexpr char kDetectedPath[] = "/sys/class/power_supply/usb/moisture_detected";
53 constexpr char kDisableContatminantDetection[] = "vendor.usb.contaminantdisable";
54 constexpr char kEnabledPath[] = "/sys/class/power_supply/usb/moisture_detection_enabled";
55 constexpr char kTypecPath[] = "/sys/class/typec";
56
57 void queryVersionHelper(android::hardware::usb::Usb *usb,
58 std::vector<PortStatus> *currentPortStatus);
59
enableUsbData(const string & in_portName,bool in_enable,int64_t in_transactionId)60 ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable,
61 int64_t in_transactionId) {
62 bool result = true;
63 std::vector<PortStatus> currentPortStatus;
64 string pullup;
65
66 ALOGI("Userspace turn %s USB data signaling. opID:%ld", in_enable ? "on" : "off",
67 in_transactionId);
68
69 if (in_enable) {
70 if (ReadFileToString(PULLUP_PATH, &pullup)) {
71 pullup = Trim(pullup);
72 if (pullup != kGadgetName) {
73 if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) {
74 ALOGE("Gadget cannot be pulled up");
75 result = false;
76 }
77 }
78 }
79
80 if (!WriteStringToFile("1", USB_DATA_PATH)) {
81 ALOGE("Not able to turn on usb connection notification");
82 result = false;
83 }
84 } else {
85 if (ReadFileToString(PULLUP_PATH, &pullup)) {
86 pullup = Trim(pullup);
87 if (pullup == kGadgetName) {
88 if (!WriteStringToFile("none", PULLUP_PATH)) {
89 ALOGE("Gadget cannot be pulled down");
90 result = false;
91 }
92 }
93 }
94
95 if (!WriteStringToFile("0", USB_DATA_PATH)) {
96 ALOGE("Not able to turn off usb connection notification");
97 result = false;
98 }
99 }
100
101 if (result) {
102 mUsbDataEnabled = in_enable;
103 }
104 pthread_mutex_lock(&mLock);
105 if (mCallback != NULL) {
106 ScopedAStatus ret = mCallback->notifyEnableUsbDataStatus(
107 in_portName, in_enable, result ? Status::SUCCESS : Status::ERROR, in_transactionId);
108 if (!ret.isOk())
109 ALOGE("notifyEnableUsbDataStatus error %s", ret.getDescription().c_str());
110 } else {
111 ALOGE("Not notifying the userspace. Callback is not set");
112 }
113 pthread_mutex_unlock(&mLock);
114 queryVersionHelper(this, ¤tPortStatus);
115
116 return ScopedAStatus::ok();
117 }
118
enableUsbDataWhileDocked(const string & in_portName,int64_t in_transactionId)119 ScopedAStatus Usb::enableUsbDataWhileDocked(const string& in_portName,
120 int64_t in_transactionId) {
121 std::vector<PortStatus> currentPortStatus;
122
123 ALOGI("Userspace enableUsbDataWhileDocked opID:%ld", in_transactionId);
124
125 pthread_mutex_lock(&mLock);
126 if (mCallback != NULL) {
127 ScopedAStatus ret = mCallback->notifyEnableUsbDataWhileDockedStatus(
128 in_portName, Status::NOT_SUPPORTED, in_transactionId);
129 if (!ret.isOk())
130 ALOGE("notifyEnableUsbDataStatus error %s", ret.getDescription().c_str());
131 } else {
132 ALOGE("Not notifying the userspace. Callback is not set");
133 }
134 pthread_mutex_unlock(&mLock);
135 queryVersionHelper(this, ¤tPortStatus);
136
137 return ScopedAStatus::ok();
138 }
139
resetUsbPort(const std::string & in_portName,int64_t in_transactionId)140 ScopedAStatus Usb::resetUsbPort(const std::string& in_portName, int64_t in_transactionId) {
141 bool result = true;
142 std::vector<PortStatus> currentPortStatus;
143
144 ALOGI("Userspace reset USB Port. opID:%ld", in_transactionId);
145
146 if (!WriteStringToFile("none", PULLUP_PATH)) {
147 ALOGI("Gadget cannot be pulled down");
148 result = false;
149 }
150
151 pthread_mutex_lock(&mLock);
152 if (mCallback != NULL) {
153 ::ndk::ScopedAStatus ret = mCallback->notifyResetUsbPortStatus(
154 in_portName, result ? Status::SUCCESS : Status::ERROR, in_transactionId);
155 if (!ret.isOk())
156 ALOGE("notifyTransactionStatus error %s", ret.getDescription().c_str());
157 } else {
158 ALOGE("Not notifying the userspace. Callback is not set");
159 }
160 pthread_mutex_unlock(&mLock);
161
162 return ::ndk::ScopedAStatus::ok();
163 }
164
limitPowerTransfer(const string & in_portName,bool in_limit,int64_t in_transactionId)165 ScopedAStatus Usb::limitPowerTransfer(const string& in_portName, bool in_limit,
166 int64_t in_transactionId) {
167 std::vector<PortStatus> currentPortStatus;
168 bool sessionFail = false, success;
169
170 pthread_mutex_lock(&mLock);
171 ALOGI("limitPowerTransfer limit:%c opId:%ld", in_limit ? 'y' : 'n', in_transactionId);
172
173 if (in_limit) {
174 success = WriteStringToFile("0", SINK_CURRENT_LIMIT_PATH);
175 if (!success) {
176 ALOGE("Failed to set sink current limit");
177 sessionFail = true;
178 }
179 }
180 success = WriteStringToFile(in_limit ? "1" : "0", SINK_LIMIT_ENABLE_PATH);
181 if (!success) {
182 ALOGE("Failed to %s sink current limit: %s", in_limit ? "enable" : "disable",
183 SINK_LIMIT_ENABLE_PATH);
184 sessionFail = true;
185 }
186 success = WriteStringToFile(in_limit ? "1" : "0", SOURCE_LIMIT_ENABLE_PATH);
187 if (!success) {
188 ALOGE("Failed to %s source current limit: %s", in_limit ? "enable" : "disable",
189 SOURCE_LIMIT_ENABLE_PATH);
190 sessionFail = true;
191 }
192
193 if (mCallback != NULL && in_transactionId >= 0) {
194 ScopedAStatus ret = mCallback->notifyLimitPowerTransferStatus(
195 in_portName, in_limit, sessionFail ? Status::ERROR : Status::SUCCESS,
196 in_transactionId);
197 if (!ret.isOk())
198 ALOGE("limitPowerTransfer error %s", ret.getDescription().c_str());
199 } else {
200 ALOGE("Not notifying the userspace. Callback is not set");
201 }
202
203 pthread_mutex_unlock(&mLock);
204 queryVersionHelper(this, ¤tPortStatus);
205
206 return ScopedAStatus::ok();
207 }
208
queryMoistureDetectionStatus(std::vector<PortStatus> * currentPortStatus)209 Status queryMoistureDetectionStatus(std::vector<PortStatus> *currentPortStatus) {
210 string enabled, status, path, DetectedPath;
211
212 (*currentPortStatus)[0].supportedContaminantProtectionModes
213 .push_back(ContaminantProtectionMode::FORCE_DISABLE);
214 (*currentPortStatus)[0].contaminantProtectionStatus = ContaminantProtectionStatus::NONE;
215 (*currentPortStatus)[0].contaminantDetectionStatus = ContaminantDetectionStatus::DISABLED;
216 (*currentPortStatus)[0].supportsEnableContaminantPresenceDetection = true;
217 (*currentPortStatus)[0].supportsEnableContaminantPresenceProtection = false;
218
219 if (!ReadFileToString(kEnabledPath, &enabled)) {
220 ALOGE("Failed to open moisture_detection_enabled");
221 return Status::ERROR;
222 }
223
224 enabled = Trim(enabled);
225 if (enabled == "1") {
226 if (!ReadFileToString(kDetectedPath, &status)) {
227 ALOGE("Failed to open moisture_detected");
228 return Status::ERROR;
229 }
230 status = Trim(status);
231 if (status == "1") {
232 (*currentPortStatus)[0].contaminantDetectionStatus =
233 ContaminantDetectionStatus::DETECTED;
234 (*currentPortStatus)[0].contaminantProtectionStatus =
235 ContaminantProtectionStatus::FORCE_DISABLE;
236 } else {
237 (*currentPortStatus)[0].contaminantDetectionStatus =
238 ContaminantDetectionStatus::NOT_DETECTED;
239 }
240 }
241
242 ALOGI("ContaminantDetectionStatus:%d ContaminantProtectionStatus:%d",
243 (*currentPortStatus)[0].contaminantDetectionStatus,
244 (*currentPortStatus)[0].contaminantProtectionStatus);
245
246 return Status::SUCCESS;
247 }
248
appendRoleNodeHelper(const string & portName,PortRole::Tag tag)249 string appendRoleNodeHelper(const string &portName, PortRole::Tag tag) {
250 string node("/sys/class/typec/" + portName);
251
252 switch (tag) {
253 case PortRole::dataRole:
254 return node + "/data_role";
255 case PortRole::powerRole:
256 return node + "/power_role";
257 case PortRole::mode:
258 return node + "/port_type";
259 default:
260 return "";
261 }
262 }
263
convertRoletoString(PortRole role)264 string convertRoletoString(PortRole role) {
265 if (role.getTag() == PortRole::powerRole) {
266 if (role.get<PortRole::powerRole>() == PortPowerRole::SOURCE)
267 return "source";
268 else if (role.get<PortRole::powerRole>() == PortPowerRole::SINK)
269 return "sink";
270 } else if (role.getTag() == PortRole::dataRole) {
271 if (role.get<PortRole::dataRole>() == PortDataRole::HOST)
272 return "host";
273 if (role.get<PortRole::dataRole>() == PortDataRole::DEVICE)
274 return "device";
275 } else if (role.getTag() == PortRole::mode) {
276 if (role.get<PortRole::mode>() == PortMode::UFP)
277 return "sink";
278 if (role.get<PortRole::mode>() == PortMode::DFP)
279 return "source";
280 }
281 return "none";
282 }
283
extractRole(string * roleName)284 void extractRole(string *roleName) {
285 std::size_t first, last;
286
287 first = roleName->find("[");
288 last = roleName->find("]");
289
290 if (first != string::npos && last != string::npos) {
291 *roleName = roleName->substr(first + 1, last - first - 1);
292 }
293 }
294
switchToDrp(const string & portName)295 void switchToDrp(const string &portName) {
296 string filename = appendRoleNodeHelper(string(portName.c_str()), PortRole::mode);
297 FILE *fp;
298
299 if (filename != "") {
300 fp = fopen(filename.c_str(), "w");
301 if (fp != NULL) {
302 int ret = fputs("dual", fp);
303 fclose(fp);
304 if (ret == EOF)
305 ALOGE("Fatal: Error while switching back to drp");
306 } else {
307 ALOGE("Fatal: Cannot open file to switch back to drp");
308 }
309 } else {
310 ALOGE("Fatal: invalid node type");
311 }
312 }
313
switchMode(const string & portName,const PortRole & in_role,struct Usb * usb)314 bool switchMode(const string &portName, const PortRole &in_role, struct Usb *usb) {
315 string filename = appendRoleNodeHelper(string(portName.c_str()), in_role.getTag());
316 string written;
317 FILE *fp;
318 bool roleSwitch = false;
319
320 if (filename == "") {
321 ALOGE("Fatal: invalid node type");
322 return false;
323 }
324
325 fp = fopen(filename.c_str(), "w");
326 if (fp != NULL) {
327 // Hold the lock here to prevent loosing connected signals
328 // as once the file is written the partner added signal
329 // can arrive anytime.
330 pthread_mutex_lock(&usb->mPartnerLock);
331 usb->mPartnerUp = false;
332 int ret = fputs(convertRoletoString(in_role).c_str(), fp);
333 fclose(fp);
334
335 if (ret != EOF) {
336 struct timespec to;
337 struct timespec now;
338
339 wait_again:
340 clock_gettime(CLOCK_MONOTONIC, &now);
341 to.tv_sec = now.tv_sec + PORT_TYPE_TIMEOUT;
342 to.tv_nsec = now.tv_nsec;
343
344 int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to);
345 // There are no uevent signals which implies role swap timed out.
346 if (err == ETIMEDOUT) {
347 ALOGI("uevents wait timedout");
348 // Validity check.
349 } else if (!usb->mPartnerUp) {
350 goto wait_again;
351 // Role switch succeeded since usb->mPartnerUp is true.
352 } else {
353 roleSwitch = true;
354 }
355 } else {
356 ALOGI("Role switch failed while wrting to file");
357 }
358 pthread_mutex_unlock(&usb->mPartnerLock);
359 }
360
361 if (!roleSwitch)
362 switchToDrp(string(portName.c_str()));
363
364 return roleSwitch;
365 }
366
Usb()367 Usb::Usb()
368 : mLock(PTHREAD_MUTEX_INITIALIZER),
369 mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER),
370 mPartnerLock(PTHREAD_MUTEX_INITIALIZER),
371 mPartnerUp(false),
372 mUsbDataEnabled(true) {
373 pthread_condattr_t attr;
374 if (pthread_condattr_init(&attr)) {
375 ALOGE("pthread_condattr_init failed: %s", strerror(errno));
376 abort();
377 }
378 if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) {
379 ALOGE("pthread_condattr_setclock failed: %s", strerror(errno));
380 abort();
381 }
382 if (pthread_cond_init(&mPartnerCV, &attr)) {
383 ALOGE("pthread_cond_init failed: %s", strerror(errno));
384 abort();
385 }
386 if (pthread_condattr_destroy(&attr)) {
387 ALOGE("pthread_condattr_destroy failed: %s", strerror(errno));
388 abort();
389 }
390 }
391
switchRole(const string & in_portName,const PortRole & in_role,int64_t in_transactionId)392 ScopedAStatus Usb::switchRole(const string& in_portName, const PortRole& in_role,
393 int64_t in_transactionId) {
394 string filename = appendRoleNodeHelper(string(in_portName.c_str()), in_role.getTag());
395 string written;
396 FILE *fp;
397 bool roleSwitch = false;
398
399 if (filename == "") {
400 ALOGE("Fatal: invalid node type");
401 return ScopedAStatus::ok();
402 }
403
404 pthread_mutex_lock(&mRoleSwitchLock);
405
406 ALOGI("filename write: %s role:%s", filename.c_str(), convertRoletoString(in_role).c_str());
407
408 if (in_role.getTag() == PortRole::mode) {
409 roleSwitch = switchMode(in_portName, in_role, this);
410 } else {
411 fp = fopen(filename.c_str(), "w");
412 if (fp != NULL) {
413 int ret = fputs(convertRoletoString(in_role).c_str(), fp);
414 fclose(fp);
415 if ((ret != EOF) && ReadFileToString(filename, &written)) {
416 written = Trim(written);
417 extractRole(&written);
418 ALOGI("written: %s", written.c_str());
419 if (written == convertRoletoString(in_role)) {
420 roleSwitch = true;
421 } else {
422 ALOGE("Role switch failed");
423 }
424 } else {
425 ALOGE("failed to update the new role");
426 }
427 } else {
428 ALOGE("fopen failed");
429 }
430 }
431
432 pthread_mutex_lock(&mLock);
433 if (mCallback != NULL) {
434 ScopedAStatus ret = mCallback->notifyRoleSwitchStatus(
435 in_portName, in_role, roleSwitch ? Status::SUCCESS : Status::ERROR, in_transactionId);
436 if (!ret.isOk())
437 ALOGE("RoleSwitchStatus error %s", ret.getDescription().c_str());
438 } else {
439 ALOGE("Not notifying the userspace. Callback is not set");
440 }
441 pthread_mutex_unlock(&mLock);
442 pthread_mutex_unlock(&mRoleSwitchLock);
443
444 return ScopedAStatus::ok();
445 }
446
getAccessoryConnected(const string & portName,string * accessory)447 Status getAccessoryConnected(const string &portName, string *accessory) {
448 string filename = "/sys/class/typec/" + portName + "-partner/accessory_mode";
449
450 if (!ReadFileToString(filename, accessory)) {
451 ALOGE("getAccessoryConnected: Failed to open filesystem node: %s", filename.c_str());
452 return Status::ERROR;
453 }
454 *accessory = Trim(*accessory);
455
456 return Status::SUCCESS;
457 }
458
getCurrentRoleHelper(const string & portName,bool connected,PortRole * currentRole)459 Status getCurrentRoleHelper(const string &portName, bool connected, PortRole *currentRole) {
460 string filename;
461 string roleName;
462 string accessory;
463
464 if (currentRole->getTag() == PortRole::powerRole) {
465 filename = "/sys/class/typec/" + portName + "/power_role";
466 currentRole->set<PortRole::powerRole>(PortPowerRole::NONE);
467 } else if (currentRole->getTag() == PortRole::dataRole) {
468 filename = "/sys/class/typec/" + portName + "/data_role";
469 currentRole->set<PortRole::dataRole>(PortDataRole::NONE);
470 } else if (currentRole->getTag() == PortRole::mode) {
471 filename = "/sys/class/typec/" + portName + "/data_role";
472 currentRole->set<PortRole::mode>(PortMode::NONE);
473 } else {
474 return Status::ERROR;
475 }
476
477 if (!connected)
478 return Status::SUCCESS;
479
480 if (currentRole->getTag() == PortRole::mode) {
481 if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) {
482 return Status::ERROR;
483 }
484 if (accessory == "analog_audio") {
485 currentRole->set<PortRole::mode>(PortMode::AUDIO_ACCESSORY);
486 return Status::SUCCESS;
487 } else if (accessory == "debug") {
488 currentRole->set<PortRole::mode>(PortMode::DEBUG_ACCESSORY);
489 return Status::SUCCESS;
490 }
491 }
492
493 if (!ReadFileToString(filename, &roleName)) {
494 ALOGE("getCurrentRole: Failed to open filesystem node: %s", filename.c_str());
495 return Status::ERROR;
496 }
497
498 roleName = Trim(roleName);
499 extractRole(&roleName);
500
501 if (roleName == "source") {
502 currentRole->set<PortRole::powerRole>(PortPowerRole::SOURCE);
503 } else if (roleName == "sink") {
504 currentRole->set<PortRole::powerRole>(PortPowerRole::SINK);
505 } else if (roleName == "host") {
506 if (currentRole->getTag() == PortRole::dataRole)
507 currentRole->set<PortRole::dataRole>(PortDataRole::HOST);
508 else
509 currentRole->set<PortRole::mode>(PortMode::DFP);
510 } else if (roleName == "device") {
511 if (currentRole->getTag() == PortRole::dataRole)
512 currentRole->set<PortRole::dataRole>(PortDataRole::DEVICE);
513 else
514 currentRole->set<PortRole::mode>(PortMode::UFP);
515 } else if (roleName != "none") {
516 /* case for none has already been addressed.
517 * so we check if the role isn't none.
518 */
519 return Status::UNRECOGNIZED_ROLE;
520 }
521 return Status::SUCCESS;
522 }
523
getTypeCPortNamesHelper(std::unordered_map<string,bool> * names)524 Status getTypeCPortNamesHelper(std::unordered_map<string, bool> *names) {
525 DIR *dp;
526
527 dp = opendir(kTypecPath);
528 if (dp != NULL) {
529 struct dirent *ep;
530
531 while ((ep = readdir(dp))) {
532 if (ep->d_type == DT_LNK) {
533 if (string::npos == string(ep->d_name).find("-partner")) {
534 std::unordered_map<string, bool>::const_iterator portName =
535 names->find(ep->d_name);
536 if (portName == names->end()) {
537 names->insert({ep->d_name, false});
538 }
539 } else {
540 (*names)[std::strtok(ep->d_name, "-")] = true;
541 }
542 }
543 }
544 closedir(dp);
545 return Status::SUCCESS;
546 }
547
548 ALOGE("Failed to open /sys/class/typec");
549 return Status::ERROR;
550 }
551
canSwitchRoleHelper(const string & portName)552 bool canSwitchRoleHelper(const string &portName) {
553 string filename = "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery";
554 string supportsPD;
555
556 if (ReadFileToString(filename, &supportsPD)) {
557 supportsPD = Trim(supportsPD);
558 if (supportsPD == "yes") {
559 return true;
560 }
561 }
562
563 return false;
564 }
565
getPortStatusHelper(android::hardware::usb::Usb * usb,std::vector<PortStatus> * currentPortStatus)566 Status getPortStatusHelper(android::hardware::usb::Usb *usb,
567 std::vector<PortStatus> *currentPortStatus) {
568 std::unordered_map<string, bool> names;
569 Status result = getTypeCPortNamesHelper(&names);
570 int i = -1;
571
572 if (result == Status::SUCCESS) {
573 currentPortStatus->resize(names.size());
574 for (std::pair<string, bool> port : names) {
575 i++;
576 ALOGI("%s", port.first.c_str());
577 (*currentPortStatus)[i].portName = port.first;
578
579 PortRole currentRole;
580 currentRole.set<PortRole::powerRole>(PortPowerRole::NONE);
581 if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS){
582 (*currentPortStatus)[i].currentPowerRole = currentRole.get<PortRole::powerRole>();
583 } else {
584 ALOGE("Error while retrieving portNames");
585 goto done;
586 }
587
588 currentRole.set<PortRole::dataRole>(PortDataRole::NONE);
589 if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) {
590 (*currentPortStatus)[i].currentDataRole = currentRole.get<PortRole::dataRole>();
591 } else {
592 ALOGE("Error while retrieving current port role");
593 goto done;
594 }
595
596 currentRole.set<PortRole::mode>(PortMode::NONE);
597 if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) {
598 (*currentPortStatus)[i].currentMode = currentRole.get<PortRole::mode>();
599 } else {
600 ALOGE("Error while retrieving current data role");
601 goto done;
602 }
603
604 (*currentPortStatus)[i].canChangeMode = true;
605 (*currentPortStatus)[i].canChangeDataRole =
606 port.second ? canSwitchRoleHelper(port.first) : false;
607 (*currentPortStatus)[i].canChangePowerRole =
608 port.second ? canSwitchRoleHelper(port.first) : false;
609
610 (*currentPortStatus)[i].supportedModes.push_back(PortMode::DRP);
611
612 if (!usb->mUsbDataEnabled) {
613 (*currentPortStatus)[i].usbDataStatus.push_back(UsbDataStatus::DISABLED_FORCE);
614 } else {
615 (*currentPortStatus)[i].usbDataStatus.push_back(UsbDataStatus::ENABLED);
616 }
617 (*currentPortStatus)[i].powerBrickStatus = PowerBrickStatus::UNKNOWN;
618
619 ALOGI("%d:%s connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d "
620 "usbDataEnabled:%d",
621 i, port.first.c_str(), port.second,
622 (*currentPortStatus)[i].canChangeMode,
623 (*currentPortStatus)[i].canChangeDataRole,
624 (*currentPortStatus)[i].canChangePowerRole,
625 usb->mUsbDataEnabled ? 1 : 0);
626 }
627 return Status::SUCCESS;
628 }
629 done:
630 return Status::ERROR;
631 }
632
queryPowerTransferStatus(std::vector<PortStatus> * currentPortStatus)633 Status queryPowerTransferStatus(std::vector<PortStatus> *currentPortStatus) {
634 string enabled;
635
636 if (!ReadFileToString(SINK_LIMIT_ENABLE_PATH, &enabled)) {
637 ALOGE("Failed to open limit_sink_enable");
638 return Status::ERROR;
639 }
640
641 enabled = Trim(enabled);
642 (*currentPortStatus)[0].powerTransferLimited = enabled == "1";
643
644 ALOGI("powerTransferLimited:%d", (*currentPortStatus)[0].powerTransferLimited ? 1 : 0);
645 return Status::SUCCESS;
646 }
647
queryVersionHelper(android::hardware::usb::Usb * usb,std::vector<PortStatus> * currentPortStatus)648 void queryVersionHelper(android::hardware::usb::Usb *usb,
649 std::vector<PortStatus> *currentPortStatus) {
650 Status status;
651 pthread_mutex_lock(&usb->mLock);
652 status = getPortStatusHelper(usb, currentPortStatus);
653 queryMoistureDetectionStatus(currentPortStatus);
654 queryPowerTransferStatus(currentPortStatus);
655 if (usb->mCallback != NULL) {
656 ScopedAStatus ret = usb->mCallback->notifyPortStatusChange(*currentPortStatus,
657 status);
658 if (!ret.isOk())
659 ALOGE("queryPortStatus error %s", ret.getDescription().c_str());
660 } else {
661 ALOGI("Notifying userspace skipped. Callback is NULL");
662 }
663 pthread_mutex_unlock(&usb->mLock);
664 }
665
queryPortStatus(int64_t in_transactionId)666 ScopedAStatus Usb::queryPortStatus(int64_t in_transactionId) {
667 std::vector<PortStatus> currentPortStatus;
668
669 queryVersionHelper(this, ¤tPortStatus);
670 pthread_mutex_lock(&mLock);
671 if (mCallback != NULL) {
672 ScopedAStatus ret = mCallback->notifyQueryPortStatus(
673 "all", Status::SUCCESS, in_transactionId);
674 if (!ret.isOk())
675 ALOGE("notifyQueryPortStatus error %s", ret.getDescription().c_str());
676 } else {
677 ALOGE("Not notifying the userspace. Callback is not set");
678 }
679 pthread_mutex_unlock(&mLock);
680
681 return ScopedAStatus::ok();
682 }
683
enableContaminantPresenceDetection(const string & in_portName,bool in_enable,int64_t in_transactionId)684 ScopedAStatus Usb::enableContaminantPresenceDetection(const string& in_portName,
685 bool in_enable, int64_t in_transactionId) {
686 string disable = GetProperty(kDisableContatminantDetection, "");
687 std::string status = GetProperty(kConsole, "");
688 std::vector<PortStatus> currentPortStatus;
689 bool success = true;
690
691 if (status != "running" && disable != "true")
692 success = WriteStringToFile(in_enable ? "1" : "0", kEnabledPath);
693
694 pthread_mutex_lock(&mLock);
695 if (mCallback != NULL) {
696 ScopedAStatus ret = mCallback->notifyContaminantEnabledStatus(
697 in_portName, in_enable, success ? Status::SUCCESS : Status::ERROR, in_transactionId);
698 if (!ret.isOk())
699 ALOGE("notifyContaminantEnabledStatus error %s", ret.getDescription().c_str());
700 } else {
701 ALOGE("Not notifying the userspace. Callback is not set");
702 }
703 pthread_mutex_unlock(&mLock);
704
705 queryVersionHelper(this, ¤tPortStatus);
706 return ScopedAStatus::ok();
707 }
708
709 struct data {
710 int uevent_fd;
711 ::aidl::android::hardware::usb::Usb *usb;
712 };
713
uevent_event(uint32_t,struct data * payload)714 static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
715 char msg[UEVENT_MSG_LEN + 2];
716 char *cp;
717 int n;
718
719 n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
720 if (n <= 0)
721 return;
722 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
723 return;
724
725 msg[n] = '\0';
726 msg[n + 1] = '\0';
727 cp = msg;
728
729 while (*cp) {
730 if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) {
731 ALOGI("partner added");
732 pthread_mutex_lock(&payload->usb->mPartnerLock);
733 payload->usb->mPartnerUp = true;
734 pthread_cond_signal(&payload->usb->mPartnerCV);
735 pthread_mutex_unlock(&payload->usb->mPartnerLock);
736 } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_")) ||
737 !strncmp(cp, "POWER_SUPPLY_MOISTURE_DETECTED",
738 strlen("POWER_SUPPLY_MOISTURE_DETECTED"))) {
739 std::vector<PortStatus> currentPortStatus;
740 queryVersionHelper(payload->usb, ¤tPortStatus);
741
742 // Role switch is not in progress and port is in disconnected state
743 if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) {
744 for (unsigned long i = 0; i < currentPortStatus.size(); i++) {
745 DIR *dp =
746 opendir(string("/sys/class/typec/" +
747 string(currentPortStatus[i].portName.c_str()) +
748 "-partner").c_str());
749 if (dp == NULL) {
750 switchToDrp(currentPortStatus[i].portName);
751 } else {
752 closedir(dp);
753 }
754 }
755 pthread_mutex_unlock(&payload->usb->mRoleSwitchLock);
756 }
757 break;
758 }
759 /* advance to after the next \0 */
760 while (*cp++) {
761 }
762 }
763 }
764
work(void * param)765 void *work(void *param) {
766 int epoll_fd, uevent_fd;
767 struct epoll_event ev;
768 int nevents = 0;
769 struct data payload;
770
771 ALOGE("creating thread");
772
773 uevent_fd = uevent_open_socket(64 * 1024, true);
774
775 if (uevent_fd < 0) {
776 ALOGE("uevent_init: uevent_open_socket failed\n");
777 return NULL;
778 }
779
780 payload.uevent_fd = uevent_fd;
781 payload.usb = (::aidl::android::hardware::usb::Usb *)param;
782
783 fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
784
785 ev.events = EPOLLIN;
786 ev.data.ptr = (void *)uevent_event;
787
788 epoll_fd = epoll_create(64);
789 if (epoll_fd == -1) {
790 ALOGE("epoll_create failed; errno=%d", errno);
791 goto error;
792 }
793
794 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
795 ALOGE("epoll_ctl failed; errno=%d", errno);
796 goto error;
797 }
798
799 while (!destroyThread) {
800 struct epoll_event events[64];
801
802 nevents = epoll_wait(epoll_fd, events, 64, -1);
803 if (nevents == -1) {
804 if (errno == EINTR)
805 continue;
806 ALOGE("usb epoll_wait failed; errno=%d", errno);
807 break;
808 }
809
810 for (int n = 0; n < nevents; ++n) {
811 if (events[n].data.ptr)
812 (*(void (*)(int, struct data *payload))events[n].data.ptr)(events[n].events,
813 &payload);
814 }
815 }
816
817 ALOGI("exiting worker thread");
818 error:
819 close(uevent_fd);
820
821 if (epoll_fd >= 0)
822 close(epoll_fd);
823
824 return NULL;
825 }
826
sighandler(int sig)827 void sighandler(int sig) {
828 if (sig == SIGUSR1) {
829 destroyThread = true;
830 ALOGI("destroy set");
831 return;
832 }
833 signal(SIGUSR1, sighandler);
834 }
835
setCallback(const shared_ptr<IUsbCallback> & in_callback)836 ScopedAStatus Usb::setCallback(const shared_ptr<IUsbCallback>& in_callback) {
837 pthread_mutex_lock(&mLock);
838 if ((mCallback == NULL && in_callback == NULL) ||
839 (mCallback != NULL && in_callback != NULL)) {
840 mCallback = in_callback;
841 pthread_mutex_unlock(&mLock);
842 return ScopedAStatus::ok();
843 }
844
845 mCallback = in_callback;
846 ALOGI("registering callback");
847
848 if (mCallback == NULL) {
849 if (!pthread_kill(mPoll, SIGUSR1)) {
850 pthread_join(mPoll, NULL);
851 ALOGI("pthread destroyed");
852 }
853 pthread_mutex_unlock(&mLock);
854 return ScopedAStatus::ok();
855 }
856
857 destroyThread = false;
858 signal(SIGUSR1, sighandler);
859
860 /*
861 * Create a background thread if the old callback value is NULL
862 * and being updated with a new value.
863 */
864 if (pthread_create(&mPoll, NULL, work, this)) {
865 ALOGE("pthread creation failed %d", errno);
866 mCallback = NULL;
867 }
868
869 pthread_mutex_unlock(&mLock);
870 return ScopedAStatus::ok();
871 }
872
873 } // namespace usb
874 } // namespace hardware
875 } // namespace android
876 } // aidl
877