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, &currentPortStatus);
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, &currentRole) == 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, &currentRole) == 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, &currentRole) == 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, &currentPortStatus);
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, &currentPortStatus);
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, &currentPortStatus);
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