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