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