• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 #define LOG_TAG "android.hardware.usb@1.3-service.crosshatch"
18 
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/parseint.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 namespace android {
42 namespace hardware {
43 namespace usb {
44 namespace V1_3 {
45 namespace implementation {
46 
47 Return<bool> Usb::enableUsbDataSignal(bool enable) {
48     bool result = true;
49 
50     ALOGI("Userspace turn %s USB data signaling", enable ? "on" : "off");
51 
52     if (enable) {
53         if (!WriteStringToFile("1", USB_DATA_PATH)) {
54             ALOGE("Not able to turn on usb connection notification");
55             result = false;
56         }
57 
58         if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) {
59             ALOGE("Gadget cannot be pulled up");
60             result = false;
61         }
62     } else {
63         if (!WriteStringToFile("1", ID_PATH)) {
64             ALOGE("Not able to turn off host mode");
65             result = false;
66         }
67 
68         if (!WriteStringToFile("0", VBUS_PATH)) {
69             ALOGE("Not able to set Vbus state");
70             result = false;
71         }
72 
73         if (!WriteStringToFile("0", USB_DATA_PATH)) {
74             ALOGE("Not able to turn on usb connection notification");
75             result = false;
76         }
77 
78         if (!WriteStringToFile("none", PULLUP_PATH)) {
79             ALOGE("Gadget cannot be pulled down");
80             result = false;
81         }
82     }
83 
84     return result;
85 }
86 
87 // Set by the signal handler to destroy the thread
88 volatile bool destroyThread;
89 
90 int32_t readFile(const std::string &filename, std::string *contents) {
91   FILE *fp;
92   ssize_t read = 0;
93   char *line = NULL;
94   size_t len = 0;
95 
96   fp = fopen(filename.c_str(), "r");
97   if (fp != NULL) {
98     if ((read = getline(&line, &len, fp)) != -1) {
99       char *pos;
100       if ((pos = strchr(line, '\n')) != NULL) *pos = '\0';
101       *contents = line;
102     }
103     free(line);
104     fclose(fp);
105     return 0;
106   } else {
107     ALOGE("fopen failed");
108   }
109 
110   return -1;
111 }
112 
113 Status queryMoistureDetectionStatus(hidl_vec<PortStatus> *currentPortStatus_1_2) {
114   std::string enabled, status, path, DetectedPath;
115 
116   if (currentPortStatus_1_2 == NULL || currentPortStatus_1_2->size() == 0) {
117       ALOGE("currentPortStatus_1_2 is not available");
118       return Status::ERROR;
119   }
120 
121   (*currentPortStatus_1_2)[0].supportedContaminantProtectionModes = 0;
122   (*currentPortStatus_1_2)[0].supportedContaminantProtectionModes |=
123       V1_2::ContaminantProtectionMode::FORCE_SINK;
124   (*currentPortStatus_1_2)[0].contaminantProtectionStatus =
125       V1_2::ContaminantProtectionStatus::NONE;
126   (*currentPortStatus_1_2)[0].contaminantDetectionStatus =
127       V1_2::ContaminantDetectionStatus::NOT_DETECTED;
128   (*currentPortStatus_1_2)[0].supportsEnableContaminantPresenceDetection = false;
129   (*currentPortStatus_1_2)[0].supportsEnableContaminantPresenceProtection = false;
130 
131   ALOGI("ContaminantDetectionStatus:%d ContaminantProtectionStatus:%d",
132         (*currentPortStatus_1_2)[0].contaminantDetectionStatus,
133         (*currentPortStatus_1_2)[0].contaminantProtectionStatus);
134 
135   return Status::SUCCESS;
136 }
137 
138 std::string appendRoleNodeHelper(const std::string &portName,
139                                  PortRoleType type) {
140   std::string node("/sys/class/typec/" + portName);
141 
142   switch (type) {
143     case PortRoleType::DATA_ROLE:
144       return node + "/data_role";
145     case PortRoleType::POWER_ROLE:
146       return node + "/power_role";
147     case PortRoleType::MODE:
148       return node + "/port_type";
149     default:
150       return "";
151   }
152 }
153 
154 std::string convertRoletoString(PortRole role) {
155   if (role.type == PortRoleType::POWER_ROLE) {
156     if (role.role == static_cast<uint32_t>(PortPowerRole::SOURCE))
157       return "source";
158     else if (role.role == static_cast<uint32_t>(PortPowerRole::SINK))
159       return "sink";
160   } else if (role.type == PortRoleType::DATA_ROLE) {
161     if (role.role == static_cast<uint32_t>(PortDataRole::HOST)) return "host";
162     if (role.role == static_cast<uint32_t>(PortDataRole::DEVICE))
163           return "device";
164   } else if (role.type == PortRoleType::MODE) {
165     if (role.role == static_cast<uint32_t>(PortMode_1_1::UFP)) return "sink";
166     if (role.role == static_cast<uint32_t>(PortMode_1_1::DFP)) return "source";
167   }
168   return "none";
169 }
170 
171 void extractRole(std::string *roleName) {
172   std::size_t first, last;
173 
174   first = roleName->find("[");
175   last = roleName->find("]");
176 
177   if (first != std::string::npos && last != std::string::npos) {
178     *roleName = roleName->substr(first + 1, last - first - 1);
179   }
180 }
181 
182 void switchToDrp(const std::string &portName) {
183   std::string filename =
184       appendRoleNodeHelper(std::string(portName.c_str()), PortRoleType::MODE);
185   FILE *fp;
186 
187   if (filename != "") {
188     fp = fopen(filename.c_str(), "w");
189     if (fp != NULL) {
190       int ret = fputs("dual", fp);
191       fclose(fp);
192       if (ret == EOF)
193         ALOGE("Fatal: Error while switching back to drp");
194     } else {
195       ALOGE("Fatal: Cannot open file to switch back to drp");
196     }
197   } else {
198     ALOGE("Fatal: invalid node type");
199   }
200 }
201 
202 bool switchMode(const hidl_string &portName,
203                              const PortRole &newRole, struct Usb *usb) {
204   std::string filename =
205        appendRoleNodeHelper(std::string(portName.c_str()), newRole.type);
206   std::string written;
207   FILE *fp;
208   bool roleSwitch = false;
209 
210   if (filename == "") {
211     ALOGE("Fatal: invalid node type");
212     return false;
213   }
214 
215   fp = fopen(filename.c_str(), "w");
216   if (fp != NULL) {
217     // Hold the lock here to prevent loosing connected signals
218     // as once the file is written the partner added signal
219     // can arrive anytime.
220     pthread_mutex_lock(&usb->mPartnerLock);
221     usb->mPartnerUp = false;
222     int ret = fputs(convertRoletoString(newRole).c_str(), fp);
223     fclose(fp);
224 
225     if (ret != EOF) {
226       struct timespec   to;
227       struct timespec   now;
228 
229 wait_again:
230       clock_gettime(CLOCK_MONOTONIC, &now);
231       to.tv_sec = now.tv_sec + PORT_TYPE_TIMEOUT;
232       to.tv_nsec = now.tv_nsec;
233 
234       int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to);
235       // There are no uevent signals which implies role swap timed out.
236       if (err == ETIMEDOUT) {
237         ALOGI("uevents wait timedout");
238       // Partner check.
239       } else if (!usb->mPartnerUp) {
240         goto wait_again;
241       // Role switch succeeded since usb->mPartnerUp is true.
242       } else {
243         roleSwitch = true;
244       }
245     } else {
246       ALOGI("Role switch failed while wrting to file");
247     }
248     pthread_mutex_unlock(&usb->mPartnerLock);
249   }
250 
251   if (!roleSwitch)
252     switchToDrp(std::string(portName.c_str()));
253 
254   return roleSwitch;
255 }
256 
257 Usb::Usb()
258         : mLock(PTHREAD_MUTEX_INITIALIZER),
259           mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER),
260           mPartnerLock(PTHREAD_MUTEX_INITIALIZER),
261           mPartnerUp(false) {
262     pthread_condattr_t attr;
263     if (pthread_condattr_init(&attr)) {
264         ALOGE("pthread_condattr_init failed: %s", strerror(errno));
265         abort();
266     }
267     if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) {
268         ALOGE("pthread_condattr_setclock failed: %s", strerror(errno));
269         abort();
270     }
271     if (pthread_cond_init(&mPartnerCV, &attr))  {
272         ALOGE("pthread_cond_init failed: %s", strerror(errno));
273         abort();
274     }
275     if (pthread_condattr_destroy(&attr)) {
276         ALOGE("pthread_condattr_destroy failed: %s", strerror(errno));
277         abort();
278     }
279 }
280 
281 
282 Return<void> Usb::switchRole(const hidl_string &portName,
283                              const V1_0::PortRole &newRole) {
284   std::string filename =
285       appendRoleNodeHelper(std::string(portName.c_str()), newRole.type);
286   std::string written;
287   FILE *fp;
288   bool roleSwitch = false;
289 
290   if (filename == "") {
291     ALOGE("Fatal: invalid node type");
292     return Void();
293   }
294 
295   pthread_mutex_lock(&mRoleSwitchLock);
296 
297   ALOGI("filename write: %s role:%s", filename.c_str(),
298         convertRoletoString(newRole).c_str());
299 
300   if (newRole.type == PortRoleType::MODE) {
301       roleSwitch = switchMode(portName, newRole, this);
302   } else {
303     fp = fopen(filename.c_str(), "w");
304     if (fp != NULL) {
305       int ret = fputs(convertRoletoString(newRole).c_str(), fp);
306       fclose(fp);
307       if ((ret != EOF) && !readFile(filename, &written)) {
308         extractRole(&written);
309         ALOGI("written: %s", written.c_str());
310         if (written == convertRoletoString(newRole)) {
311           roleSwitch = true;
312         } else {
313           ALOGE("Role switch failed");
314         }
315       } else {
316         ALOGE("failed to update the new role");
317       }
318     } else {
319       ALOGE("fopen failed");
320     }
321   }
322 
323   pthread_mutex_lock(&mLock);
324   if (mCallback_1_0 != NULL) {
325     Return<void> ret =
326         mCallback_1_0->notifyRoleSwitchStatus(portName, newRole,
327         roleSwitch ? Status::SUCCESS : Status::ERROR);
328     if (!ret.isOk())
329       ALOGE("RoleSwitchStatus error %s", ret.description().c_str());
330   } else {
331     ALOGE("Not notifying the userspace. Callback is not set");
332   }
333   pthread_mutex_unlock(&mLock);
334   pthread_mutex_unlock(&mRoleSwitchLock);
335 
336   return Void();
337 }
338 
339 Status getAccessoryConnected(const std::string &portName, std::string *accessory) {
340   std::string filename =
341     "/sys/class/typec/" + portName + "-partner/accessory_mode";
342 
343   if (readFile(filename, accessory)) {
344     ALOGE("getAccessoryConnected: Failed to open filesystem node: %s",
345           filename.c_str());
346     return Status::ERROR;
347   }
348 
349   return Status::SUCCESS;
350 }
351 
352 Status getCurrentRoleHelper(const std::string &portName, bool connected,
353                             PortRoleType type, uint32_t *currentRole) {
354   std::string filename;
355   std::string roleName;
356   std::string accessory;
357 
358   // Mode
359 
360   if (type == PortRoleType::POWER_ROLE) {
361     filename = "/sys/class/typec/" + portName + "/power_role";
362     *currentRole = static_cast<uint32_t>(PortPowerRole::NONE);
363   } else if (type == PortRoleType::DATA_ROLE) {
364     filename = "/sys/class/typec/" + portName + "/data_role";
365     *currentRole = static_cast<uint32_t>(PortDataRole::NONE);
366   } else if (type == PortRoleType::MODE) {
367     filename = "/sys/class/typec/" + portName + "/data_role";
368     *currentRole = static_cast<uint32_t>(PortMode_1_1::NONE);
369   } else {
370     return Status::ERROR;
371   }
372 
373   if (!connected) return Status::SUCCESS;
374 
375   if (type == PortRoleType::MODE) {
376     if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) {
377       return Status::ERROR;
378     }
379     if (accessory == "analog_audio") {
380       *currentRole = static_cast<uint32_t>(PortMode_1_1::AUDIO_ACCESSORY);
381       return Status::SUCCESS;
382     } else if (accessory == "debug") {
383       *currentRole = static_cast<uint32_t>(PortMode_1_1::DEBUG_ACCESSORY);
384       return Status::SUCCESS;
385     }
386   }
387 
388   if (readFile(filename, &roleName)) {
389     ALOGE("getCurrentRole: Failed to open filesystem node: %s",
390           filename.c_str());
391     return Status::ERROR;
392   }
393 
394   extractRole(&roleName);
395 
396   if (roleName == "source") {
397     *currentRole = static_cast<uint32_t>(PortPowerRole::SOURCE);
398   } else if (roleName == "sink") {
399     *currentRole = static_cast<uint32_t>(PortPowerRole::SINK);
400   } else if (roleName == "host") {
401     if (type == PortRoleType::DATA_ROLE)
402       *currentRole = static_cast<uint32_t>(PortDataRole::HOST);
403     else
404       *currentRole = static_cast<uint32_t>(PortMode_1_1::DFP);
405   } else if (roleName == "device") {
406     if (type == PortRoleType::DATA_ROLE)
407       *currentRole = static_cast<uint32_t>(PortDataRole::DEVICE);
408     else
409       *currentRole = static_cast<uint32_t>(PortMode_1_1::UFP);
410   } else if (roleName != "none") {
411     /* case for none has already been addressed.
412      * so we check if the role isnt none.
413      */
414     return Status::UNRECOGNIZED_ROLE;
415   }
416 
417   return Status::SUCCESS;
418 }
419 
420 Status getTypeCPortNamesHelper(std::unordered_map<std::string, bool> *names) {
421   DIR *dp;
422 
423   dp = opendir("/sys/class/typec");
424   if (dp != NULL) {
425     struct dirent *ep;
426 
427     while ((ep = readdir(dp))) {
428       if (ep->d_type == DT_LNK) {
429         if (std::string::npos == std::string(ep->d_name).find("-partner")) {
430           std::unordered_map<std::string, bool>::const_iterator portName =
431               names->find(ep->d_name);
432           if (portName == names->end()) {
433             names->insert({ep->d_name, false});
434           }
435         } else {
436           (*names)[std::strtok(ep->d_name, "-")] = true;
437         }
438       }
439     }
440     closedir(dp);
441     return Status::SUCCESS;
442   }
443 
444   ALOGE("Failed to open /sys/class/typec");
445   return Status::ERROR;
446 }
447 
448 bool canSwitchRoleHelper(const std::string &portName, PortRoleType /*type*/) {
449   std::string filename =
450       "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery";
451   std::string supportsPD;
452 
453   if (!readFile(filename, &supportsPD)) {
454     if (supportsPD == "yes") {
455       return true;
456     }
457   }
458 
459   return false;
460 }
461 
462 /*
463  * Reuse the same method for both V1_0 and V1_1 callback objects.
464  * The caller of this method would reconstruct the V1_0::PortStatus
465  * object if required.
466  */
467 Status getPortStatusHelper(hidl_vec<PortStatus> *currentPortStatus_1_2, HALVersion version) {
468     std::unordered_map<std::string, bool> names;
469     Status result = getTypeCPortNamesHelper(&names);
470     int i = -1;
471 
472     if (result == Status::SUCCESS) {
473         currentPortStatus_1_2->resize(names.size());
474         for (std::pair<std::string, bool> port : names) {
475             i++;
476             ALOGI("%s", port.first.c_str());
477             (*currentPortStatus_1_2)[i].status_1_1.status.portName = port.first;
478 
479             uint32_t currentRole;
480             if (getCurrentRoleHelper(port.first, port.second, PortRoleType::POWER_ROLE,
481                                      &currentRole) == Status::SUCCESS) {
482                 (*currentPortStatus_1_2)[i].status_1_1.status.currentPowerRole =
483                     static_cast<PortPowerRole>(currentRole);
484             } else {
485                 ALOGE("Error while retrieving portNames");
486                 goto done;
487             }
488 
489             if (getCurrentRoleHelper(port.first, port.second, PortRoleType::DATA_ROLE,
490                                      &currentRole) == Status::SUCCESS) {
491                 (*currentPortStatus_1_2)[i].status_1_1.status.currentDataRole =
492                     static_cast<PortDataRole>(currentRole);
493             } else {
494                 ALOGE("Error while retrieving current port role");
495                 goto done;
496             }
497 
498             if (getCurrentRoleHelper(port.first, port.second, PortRoleType::MODE, &currentRole) ==
499                 Status::SUCCESS) {
500                 (*currentPortStatus_1_2)[i].status_1_1.currentMode =
501                     static_cast<PortMode_1_1>(currentRole);
502                 (*currentPortStatus_1_2)[i].status_1_1.status.currentMode =
503                     static_cast<V1_0::PortMode>(currentRole);
504             } else {
505                 ALOGE("Error while retrieving current data role");
506                 goto done;
507             }
508 
509             (*currentPortStatus_1_2)[i].status_1_1.status.canChangeMode = true;
510             (*currentPortStatus_1_2)[i].status_1_1.status.canChangeDataRole =
511                 port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE) : false;
512             (*currentPortStatus_1_2)[i].status_1_1.status.canChangePowerRole =
513                 port.second ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE) : false;
514 
515             if (version == HALVersion::V1_0) {
516                 ALOGI("HAL version V1_0");
517                 (*currentPortStatus_1_2)[i].status_1_1.status.supportedModes = V1_0::PortMode::DRP;
518             } else {
519                 if (version == HALVersion::V1_1)
520                     ALOGI("HAL version V1_1");
521                 else
522                     ALOGI("HAL version V1_2");
523                 (*currentPortStatus_1_2)[i].status_1_1.supportedModes = 0 | PortMode_1_1::DRP;
524                 (*currentPortStatus_1_2)[i].status_1_1.status.supportedModes = V1_0::PortMode::NONE;
525                 (*currentPortStatus_1_2)[i].status_1_1.status.currentMode = V1_0::PortMode::NONE;
526             }
527 
528             ALOGI(
529                 "%d:%s connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d "
530                 "supportedModes:%d",
531                 i, port.first.c_str(), port.second,
532                 (*currentPortStatus_1_2)[i].status_1_1.status.canChangeMode,
533                 (*currentPortStatus_1_2)[i].status_1_1.status.canChangeDataRole,
534                 (*currentPortStatus_1_2)[i].status_1_1.status.canChangePowerRole,
535                 (*currentPortStatus_1_2)[i].status_1_1.supportedModes);
536         }
537         return Status::SUCCESS;
538     }
539 done:
540     return Status::ERROR;
541 }
542 
543 void queryVersionHelper(android::hardware::usb::V1_3::implementation::Usb *usb,
544                         hidl_vec<PortStatus> *currentPortStatus_1_2) {
545     hidl_vec<V1_1::PortStatus_1_1> currentPortStatus_1_1;
546     hidl_vec<V1_0::PortStatus> currentPortStatus;
547     Status status;
548     sp<V1_1::IUsbCallback> callback_V1_1 = V1_1::IUsbCallback::castFrom(usb->mCallback_1_0);
549     sp<IUsbCallback> callback_V1_2 = IUsbCallback::castFrom(usb->mCallback_1_0);
550 
551     pthread_mutex_lock(&usb->mLock);
552     if (usb->mCallback_1_0 != NULL) {
553         if (callback_V1_2 != NULL) {
554             status = getPortStatusHelper(currentPortStatus_1_2, HALVersion::V1_2);
555             if (status == Status::SUCCESS)
556                 queryMoistureDetectionStatus(currentPortStatus_1_2);
557         } else if (callback_V1_1 != NULL) {
558             status = getPortStatusHelper(currentPortStatus_1_2, HALVersion::V1_1);
559             currentPortStatus_1_1.resize(currentPortStatus_1_2->size());
560             for (unsigned long i = 0; i < currentPortStatus_1_2->size(); i++)
561                 currentPortStatus_1_1[i] = (*currentPortStatus_1_2)[i].status_1_1;
562         } else {
563             status = getPortStatusHelper(currentPortStatus_1_2, HALVersion::V1_0);
564             currentPortStatus.resize(currentPortStatus_1_2->size());
565             for (unsigned long i = 0; i < currentPortStatus_1_2->size(); i++)
566                 currentPortStatus[i] = (*currentPortStatus_1_2)[i].status_1_1.status;
567         }
568 
569         Return<void> ret;
570 
571         if (callback_V1_2 != NULL)
572             ret = callback_V1_2->notifyPortStatusChange_1_2(*currentPortStatus_1_2, status);
573         else if (callback_V1_1 != NULL)
574             ret = callback_V1_1->notifyPortStatusChange_1_1(currentPortStatus_1_1, status);
575         else
576             ret = usb->mCallback_1_0->notifyPortStatusChange(currentPortStatus, status);
577 
578         if (!ret.isOk())
579             ALOGE("queryPortStatus_1_2 error %s", ret.description().c_str());
580     } else {
581         ALOGI("Notifying userspace skipped. Callback is NULL");
582     }
583     pthread_mutex_unlock(&usb->mLock);
584 }
585 
586 Return<void> Usb::queryPortStatus() {
587     hidl_vec<PortStatus> currentPortStatus_1_2;
588 
589     queryVersionHelper(this, &currentPortStatus_1_2);
590     return Void();
591 }
592 
593 Return<void> Usb::enableContaminantPresenceDetection(const hidl_string & /*portName*/,
594                                                      bool /*enable*/) {
595     hidl_vec<PortStatus> currentPortStatus_1_2;
596 
597     queryVersionHelper(this, &currentPortStatus_1_2);
598     return Void();
599 }
600 
601 Return<void> Usb::enableContaminantPresenceProtection(const hidl_string & /*portName*/,
602                                                       bool /*enable*/) {
603     hidl_vec<PortStatus> currentPortStatus_1_2;
604 
605     queryVersionHelper(this, &currentPortStatus_1_2);
606     return Void();
607 }
608 
609 /* uevent_event() data that is persistent across uevents. */
610 struct data {
611   int uevent_fd;
612   android::hardware::usb::V1_3::implementation::Usb *usb;
613 };
614 
615 // Report connection & disconnection of devices into the USB-C connector.
616 static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
617     char msg[UEVENT_MSG_LEN + 2];
618     char *cp;
619     int n;
620 
621     n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
622     if (n <= 0)
623         return;
624     if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
625         return;
626 
627     msg[n] = '\0';
628     msg[n + 1] = '\0';
629     cp = msg;
630 
631     while (*cp) {
632         if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) {
633             ALOGI("partner added");
634             pthread_mutex_lock(&payload->usb->mPartnerLock);
635             payload->usb->mPartnerUp = true;
636             pthread_cond_signal(&payload->usb->mPartnerCV);
637             pthread_mutex_unlock(&payload->usb->mPartnerLock);
638         } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
639             hidl_vec<PortStatus> currentPortStatus_1_2;
640             queryVersionHelper(payload->usb, &currentPortStatus_1_2);
641 
642             // Role switch is not in progress and port is in disconnected state
643             if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) {
644                 for (unsigned long i = 0; i < currentPortStatus_1_2.size(); i++) {
645                     DIR *dp =
646                         opendir(std::string("/sys/class/typec/" +
647                                             std::string(currentPortStatus_1_2[i]
648                                                             .status_1_1.status.portName.c_str()) +
649                                             "-partner")
650                                     .c_str());
651                     if (dp == NULL) {
652                         // PortRole role = {.role = static_cast<uint32_t>(PortMode::UFP)};
653                         switchToDrp(currentPortStatus_1_2[i].status_1_1.status.portName);
654                     } else {
655                         closedir(dp);
656                     }
657                 }
658                 pthread_mutex_unlock(&payload->usb->mRoleSwitchLock);
659             }
660             break;
661         }
662         /* advance to after the next \0 */
663         while (*cp++) {
664         }
665     }
666 }
667 
668 void *work(void *param) {
669   int epoll_fd, uevent_fd;
670   struct epoll_event ev;
671   int nevents = 0;
672   struct data payload;
673 
674   ALOGE("creating thread");
675 
676   uevent_fd = uevent_open_socket(64 * 1024, true);
677 
678   if (uevent_fd < 0) {
679     ALOGE("uevent_init: uevent_open_socket failed\n");
680     return NULL;
681   }
682 
683   payload.uevent_fd = uevent_fd;
684   payload.usb = (android::hardware::usb::V1_3::implementation::Usb *)param;
685 
686   fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
687 
688   ev.events = EPOLLIN;
689   ev.data.ptr = (void *)uevent_event;
690 
691   epoll_fd = epoll_create1(EPOLL_CLOEXEC);
692   if (epoll_fd == -1) {
693     ALOGE("epoll_create1 failed; errno=%d", errno);
694     goto error;
695   }
696 
697   if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
698     ALOGE("epoll_ctl failed; errno=%d", errno);
699     goto error;
700   }
701 
702   while (!destroyThread) {
703     struct epoll_event events[64];
704 
705     nevents = epoll_wait(epoll_fd, events, 64, -1);
706     if (nevents == -1) {
707       if (errno == EINTR) continue;
708       ALOGE("usb epoll_wait failed; errno=%d", errno);
709       break;
710     }
711 
712     for (int n = 0; n < nevents; ++n) {
713       if (events[n].data.ptr)
714         (*(void (*)(int, struct data *payload))events[n].data.ptr)(
715             events[n].events, &payload);
716     }
717   }
718 
719   ALOGI("exiting worker thread");
720 error:
721   close(uevent_fd);
722 
723   if (epoll_fd >= 0) close(epoll_fd);
724 
725   return NULL;
726 }
727 
728 void sighandler(int sig) {
729   if (sig == SIGUSR1) {
730     destroyThread = true;
731     ALOGI("destroy set");
732     return;
733   }
734   signal(SIGUSR1, sighandler);
735 }
736 
737 Return<void> Usb::setCallback(const sp<V1_0::IUsbCallback> &callback) {
738     sp<V1_1::IUsbCallback> callback_V1_1 = V1_1::IUsbCallback::castFrom(callback);
739     sp<IUsbCallback> callback_V1_2 = IUsbCallback::castFrom(callback);
740 
741     if (callback != NULL) {
742         if (callback_V1_2 != NULL)
743             ALOGI("Registering 1.2 callback");
744         else if (callback_V1_1 != NULL)
745             ALOGI("Registering 1.1 callback");
746     }
747 
748     pthread_mutex_lock(&mLock);
749     /*
750      * When both the old callback and new callback values are NULL,
751      * there is no need to spin off the worker thread.
752      * When both the values are not NULL, we would already have a
753      * worker thread running, so updating the callback object would
754      * be suffice.
755      */
756     if ((mCallback_1_0 == NULL && callback == NULL) ||
757         (mCallback_1_0 != NULL && callback != NULL)) {
758         /*
759          * Always store as V1_0 callback object. Type cast to V1_1
760          * when the callback is actually invoked.
761          */
762         mCallback_1_0 = callback;
763         pthread_mutex_unlock(&mLock);
764         return Void();
765     }
766 
767     mCallback_1_0 = callback;
768     ALOGI("registering callback");
769 
770     // Kill the worker thread if the new callback is NULL.
771     if (mCallback_1_0 == NULL) {
772         pthread_mutex_unlock(&mLock);
773         if (!pthread_kill(mPoll, SIGUSR1)) {
774             pthread_join(mPoll, NULL);
775             ALOGI("pthread destroyed");
776         }
777         return Void();
778     }
779 
780     destroyThread = false;
781     signal(SIGUSR1, sighandler);
782 
783     /*
784      * Create a background thread if the old callback value is NULL
785      * and being updated with a new value.
786      */
787     if (pthread_create(&mPoll, NULL, work, this)) {
788         ALOGE("pthread creation failed %d", errno);
789         mCallback_1_0 = NULL;
790     }
791 
792     pthread_mutex_unlock(&mLock);
793     return Void();
794 }
795 
796 }  // namespace implementation
797 }  // namespace V1_3
798 }  // namespace usb
799 }  // namespace hardware
800 }  // namespace android
801