Home | History | Annotate | Download | only in usb
      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 (at) 1.1-service.wahoo"
     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 
     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 
     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 
     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 
    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 
    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 
    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 timeval    tp;
    160 
    161 wait_again:
    162       gettimeofday(&tp, NULL);
    163       to.tv_sec = tp.tv_sec + PORT_TYPE_TIMEOUT;
    164       to.tv_nsec = tp.tv_usec * 1000;;
    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 
    189 
    190 
    191 Return<void> Usb::switchRole(const hidl_string &portName,
    192                              const PortRole &newRole) {
    193   std::string filename =
    194       appendRoleNodeHelper(std::string(portName.c_str()), newRole.type);
    195   std::string written;
    196   FILE *fp;
    197   bool roleSwitch = false;
    198 
    199   if (filename == "") {
    200     ALOGE("Fatal: invalid node type");
    201     return Void();
    202   }
    203 
    204   pthread_mutex_lock(&mRoleSwitchLock);
    205 
    206   ALOGI("filename write: %s role:%s", filename.c_str(),
    207         convertRoletoString(newRole).c_str());
    208 
    209   if (newRole.type == PortRoleType::MODE) {
    210       roleSwitch = switchMode(portName, newRole, this);
    211   } else {
    212     fp = fopen(filename.c_str(), "w");
    213     if (fp != NULL) {
    214       int ret = fputs(convertRoletoString(newRole).c_str(), fp);
    215       fclose(fp);
    216       if ((ret != EOF) && !readFile(filename, &written)) {
    217         extractRole(&written);
    218         ALOGI("written: %s", written.c_str());
    219         if (written == convertRoletoString(newRole)) {
    220           roleSwitch = true;
    221         } else {
    222           ALOGE("Role switch failed");
    223         }
    224       } else {
    225         ALOGE("failed to update the new role");
    226       }
    227     } else {
    228       ALOGE("fopen failed");
    229     }
    230   }
    231 
    232   pthread_mutex_lock(&mLock);
    233   if (mCallback_1_0 != NULL) {
    234     Return<void> ret =
    235         mCallback_1_0->notifyRoleSwitchStatus(portName, newRole,
    236         roleSwitch ? Status::SUCCESS : Status::ERROR);
    237     if (!ret.isOk())
    238       ALOGE("RoleSwitchStatus error %s", ret.description().c_str());
    239   } else {
    240     ALOGE("Not notifying the userspace. Callback is not set");
    241   }
    242   pthread_mutex_unlock(&mLock);
    243   pthread_mutex_unlock(&mRoleSwitchLock);
    244 
    245   return Void();
    246 }
    247 
    248 Status getAccessoryConnected(const std::string &portName, std::string *accessory) {
    249   std::string filename =
    250     "/sys/class/typec/" + portName + "-partner/accessory_mode";
    251 
    252   if (readFile(filename, accessory)) {
    253     ALOGE("getAccessoryConnected: Failed to open filesystem node: %s",
    254           filename.c_str());
    255     return Status::ERROR;
    256   }
    257 
    258   return Status::SUCCESS;
    259 }
    260 
    261 Status getCurrentRoleHelper(const std::string &portName, bool connected,
    262                             PortRoleType type, uint32_t *currentRole) {
    263   std::string filename;
    264   std::string roleName;
    265   std::string accessory;
    266 
    267   // Mode
    268 
    269   if (type == PortRoleType::POWER_ROLE) {
    270     filename = "/sys/class/typec/" + portName + "/power_role";
    271     *currentRole = static_cast<uint32_t>(PortPowerRole::NONE);
    272   } else if (type == PortRoleType::DATA_ROLE) {
    273     filename = "/sys/class/typec/" + portName + "/data_role";
    274     *currentRole = static_cast<uint32_t>(PortDataRole::NONE);
    275   } else if (type == PortRoleType::MODE) {
    276     filename = "/sys/class/typec/" + portName + "/data_role";
    277     *currentRole = static_cast<uint32_t>(PortMode_1_1::NONE);
    278   } else {
    279     return Status::ERROR;
    280   }
    281 
    282   if (!connected) return Status::SUCCESS;
    283 
    284   if (type == PortRoleType::MODE) {
    285     if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) {
    286       return Status::ERROR;
    287     }
    288     if (accessory == "analog_audio") {
    289       *currentRole = static_cast<uint32_t>(PortMode_1_1::AUDIO_ACCESSORY);
    290       return Status::SUCCESS;
    291     } else if (accessory == "debug") {
    292       *currentRole = static_cast<uint32_t>(PortMode_1_1::DEBUG_ACCESSORY);
    293       return Status::SUCCESS;
    294     }
    295   }
    296 
    297   if (readFile(filename, &roleName)) {
    298     ALOGE("getCurrentRole: Failed to open filesystem node: %s",
    299           filename.c_str());
    300     return Status::ERROR;
    301   }
    302 
    303   extractRole(&roleName);
    304 
    305   if (roleName == "source") {
    306     *currentRole = static_cast<uint32_t>(PortPowerRole::SOURCE);
    307   } else if (roleName == "sink") {
    308     *currentRole = static_cast<uint32_t>(PortPowerRole::SINK);
    309   } else if (roleName == "host") {
    310     if (type == PortRoleType::DATA_ROLE)
    311       *currentRole = static_cast<uint32_t>(PortDataRole::HOST);
    312     else
    313       *currentRole = static_cast<uint32_t>(PortMode_1_1::DFP);
    314   } else if (roleName == "device") {
    315     if (type == PortRoleType::DATA_ROLE)
    316       *currentRole = static_cast<uint32_t>(PortDataRole::DEVICE);
    317     else
    318       *currentRole = static_cast<uint32_t>(PortMode_1_1::UFP);
    319   } else if (roleName != "none") {
    320     /* case for none has already been addressed.
    321      * so we check if the role isnt none.
    322      */
    323     return Status::UNRECOGNIZED_ROLE;
    324   }
    325 
    326   return Status::SUCCESS;
    327 }
    328 
    329 Status getTypeCPortNamesHelper(std::unordered_map<std::string, bool> *names) {
    330   DIR *dp;
    331 
    332   dp = opendir("/sys/class/typec");
    333   if (dp != NULL) {
    334     int32_t ports = 0;
    335     int32_t current = 0;
    336     struct dirent *ep;
    337 
    338     while ((ep = readdir(dp))) {
    339       if (ep->d_type == DT_LNK) {
    340         if (std::string::npos == std::string(ep->d_name).find("-partner")) {
    341           std::unordered_map<std::string, bool>::const_iterator portName =
    342               names->find(ep->d_name);
    343           if (portName == names->end()) {
    344             names->insert({ep->d_name, false});
    345           }
    346         } else {
    347           (*names)[std::strtok(ep->d_name, "-")] = true;
    348         }
    349       }
    350     }
    351     closedir(dp);
    352     return Status::SUCCESS;
    353   }
    354 
    355   ALOGE("Failed to open /sys/class/typec");
    356   return Status::ERROR;
    357 }
    358 
    359 bool canSwitchRoleHelper(const std::string &portName, PortRoleType /*type*/) {
    360   std::string filename =
    361       "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery";
    362   std::string supportsPD;
    363 
    364   if (!readFile(filename, &supportsPD)) {
    365     if (supportsPD == "yes") {
    366       return true;
    367     }
    368   }
    369 
    370   return false;
    371 }
    372 
    373 /*
    374  * Reuse the same method for both V1_0 and V1_1 callback objects.
    375  * The caller of this method would reconstruct the V1_0::PortStatus
    376  * object if required.
    377  */
    378 Status getPortStatusHelper(hidl_vec<PortStatus_1_1> *currentPortStatus_1_1,
    379     bool V1_0) {
    380   std::unordered_map<std::string, bool> names;
    381   Status result = getTypeCPortNamesHelper(&names);
    382   int i = -1;
    383 
    384   if (result == Status::SUCCESS) {
    385     currentPortStatus_1_1->resize(names.size());
    386     for (std::pair<std::string, bool> port : names) {
    387       i++;
    388       ALOGI("%s", port.first.c_str());
    389       (*currentPortStatus_1_1)[i].status.portName = port.first;
    390 
    391       uint32_t currentRole;
    392       if (getCurrentRoleHelper(port.first, port.second,
    393                                PortRoleType::POWER_ROLE,
    394                                &currentRole) == Status::SUCCESS) {
    395         (*currentPortStatus_1_1)[i].status.currentPowerRole =
    396             static_cast<PortPowerRole>(currentRole);
    397       } else {
    398         ALOGE("Error while retreiving portNames");
    399         goto done;
    400       }
    401 
    402       if (getCurrentRoleHelper(port.first, port.second, PortRoleType::DATA_ROLE,
    403                                &currentRole) == Status::SUCCESS) {
    404         (*currentPortStatus_1_1)[i].status.currentDataRole =
    405             static_cast<PortDataRole>(currentRole);
    406       } else {
    407         ALOGE("Error while retreiving current port role");
    408         goto done;
    409       }
    410 
    411       if (getCurrentRoleHelper(port.first, port.second, PortRoleType::MODE,
    412                                &currentRole) == Status::SUCCESS) {
    413         (*currentPortStatus_1_1)[i].currentMode =
    414             static_cast<PortMode_1_1>(currentRole);
    415         (*currentPortStatus_1_1)[i].status.currentMode =
    416             static_cast<V1_0::PortMode>(currentRole);
    417       } else {
    418         ALOGE("Error while retreiving current data role");
    419         goto done;
    420       }
    421 
    422       (*currentPortStatus_1_1)[i].status.canChangeMode = true;
    423       (*currentPortStatus_1_1)[i].status.canChangeDataRole =
    424           port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE)
    425                       : false;
    426       (*currentPortStatus_1_1)[i].status.canChangePowerRole =
    427           port.second
    428               ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE)
    429               : false;
    430 
    431       ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d",
    432             port.second, (*currentPortStatus_1_1)[i].status.canChangeMode,
    433             (*currentPortStatus_1_1)[i].status.canChangeDataRole,
    434             (*currentPortStatus_1_1)[i].status.canChangePowerRole);
    435 
    436       if (V1_0) {
    437         (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::DFP;
    438       } else {
    439         (*currentPortStatus_1_1)[i].supportedModes = PortMode_1_1::UFP | PortMode_1_1::DFP;
    440         (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::NONE;
    441         (*currentPortStatus_1_1)[i].status.currentMode = V1_0::PortMode::NONE;
    442       }
    443     }
    444     return Status::SUCCESS;
    445   }
    446 done:
    447   return Status::ERROR;
    448 }
    449 
    450 Return<void> Usb::queryPortStatus() {
    451   hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
    452   hidl_vec<V1_0::PortStatus> currentPortStatus;
    453   Status status;
    454   sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(mCallback_1_0);
    455 
    456   pthread_mutex_lock(&mLock);
    457   if (mCallback_1_0 != NULL) {
    458     if (callback_V1_1 != NULL) {
    459       status = getPortStatusHelper(&currentPortStatus_1_1, false);
    460     } else {
    461       status = getPortStatusHelper(&currentPortStatus_1_1, true);
    462       currentPortStatus.resize(currentPortStatus_1_1.size());
    463       for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++)
    464         currentPortStatus[i] = currentPortStatus_1_1[i].status;
    465     }
    466 
    467     Return<void> ret;
    468 
    469     if (callback_V1_1 != NULL)
    470       ret = callback_V1_1->notifyPortStatusChange_1_1(currentPortStatus_1_1, status);
    471     else
    472       ret = mCallback_1_0->notifyPortStatusChange(currentPortStatus, status);
    473 
    474     if (!ret.isOk())
    475       ALOGE("queryPortStatus_1_1 error %s", ret.description().c_str());
    476   } else {
    477     ALOGI("Notifying userspace skipped. Callback is NULL");
    478   }
    479   pthread_mutex_unlock(&mLock);
    480 
    481   return Void();
    482 }
    483 
    484 struct data {
    485   int uevent_fd;
    486   android::hardware::usb::V1_1::implementation::Usb *usb;
    487 };
    488 
    489 static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
    490   char msg[UEVENT_MSG_LEN + 2];
    491   char *cp;
    492   int n;
    493 
    494   n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
    495   if (n <= 0) return;
    496   if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
    497     return;
    498 
    499   msg[n] = '\0';
    500   msg[n + 1] = '\0';
    501   cp = msg;
    502 
    503   while (*cp) {
    504     if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) {
    505        ALOGI("partner added");
    506        pthread_mutex_lock(&payload->usb->mPartnerLock);
    507        payload->usb->mPartnerUp = true;
    508        pthread_cond_signal(&payload->usb->mPartnerCV);
    509        pthread_mutex_unlock(&payload->usb->mPartnerLock);
    510     } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
    511       hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
    512       ALOGI("uevent received %s", cp);
    513       pthread_mutex_lock(&payload->usb->mLock);
    514       if (payload->usb->mCallback_1_0 != NULL) {
    515         sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(payload->usb->mCallback_1_0);
    516         Return<void> ret;
    517 
    518         // V1_1 callback
    519         if (callback_V1_1 != NULL) {
    520           Status status = getPortStatusHelper(&currentPortStatus_1_1, false);
    521           ret = callback_V1_1->notifyPortStatusChange_1_1(
    522               currentPortStatus_1_1, status);
    523         } else { // V1_0 callback
    524           Status status = getPortStatusHelper(&currentPortStatus_1_1, true);
    525 
    526           /*
    527            * Copying the result from getPortStatusHelper
    528            * into V1_0::PortStatus to pass back through
    529            * the V1_0 callback object.
    530            */
    531           hidl_vec<V1_0::PortStatus> currentPortStatus;
    532           currentPortStatus.resize(currentPortStatus_1_1.size());
    533           for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++)
    534             currentPortStatus[i] = currentPortStatus_1_1[i].status;
    535 
    536           ret = payload->usb->mCallback_1_0->notifyPortStatusChange(
    537               currentPortStatus, status);
    538         }
    539         if (!ret.isOk()) ALOGE("error %s", ret.description().c_str());
    540       } else {
    541         ALOGI("Notifying userspace skipped. Callback is NULL");
    542       }
    543       pthread_mutex_unlock(&payload->usb->mLock);
    544 
    545       //Role switch is not in progress and port is in disconnected state
    546       if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) {
    547         for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) {
    548           DIR *dp = opendir(std::string("/sys/class/typec/"
    549               + std::string(currentPortStatus_1_1[i].status.portName.c_str())
    550               + "-partner").c_str());
    551           if (dp == NULL) {
    552               //PortRole role = {.role = static_cast<uint32_t>(PortMode::UFP)};
    553               switchToDrp(currentPortStatus_1_1[i].status.portName);
    554           } else {
    555               closedir(dp);
    556           }
    557         }
    558         pthread_mutex_unlock(&payload->usb->mRoleSwitchLock);
    559       }
    560       break;
    561     }
    562     /* advance to after the next \0 */
    563     while (*cp++) {}
    564   }
    565 }
    566 
    567 void *work(void *param) {
    568   int epoll_fd, uevent_fd;
    569   struct epoll_event ev;
    570   int nevents = 0;
    571   struct data payload;
    572 
    573   ALOGE("creating thread");
    574 
    575   uevent_fd = uevent_open_socket(64 * 1024, true);
    576 
    577   if (uevent_fd < 0) {
    578     ALOGE("uevent_init: uevent_open_socket failed\n");
    579     return NULL;
    580   }
    581 
    582   payload.uevent_fd = uevent_fd;
    583   payload.usb = (android::hardware::usb::V1_1::implementation::Usb *)param;
    584 
    585   fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
    586 
    587   ev.events = EPOLLIN;
    588   ev.data.ptr = (void *)uevent_event;
    589 
    590   epoll_fd = epoll_create(64);
    591   if (epoll_fd == -1) {
    592     ALOGE("epoll_create failed; errno=%d", errno);
    593     goto error;
    594   }
    595 
    596   if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
    597     ALOGE("epoll_ctl failed; errno=%d", errno);
    598     goto error;
    599   }
    600 
    601   while (!destroyThread) {
    602     struct epoll_event events[64];
    603 
    604     nevents = epoll_wait(epoll_fd, events, 64, -1);
    605     if (nevents == -1) {
    606       if (errno == EINTR) continue;
    607       ALOGE("usb epoll_wait failed; errno=%d", errno);
    608       break;
    609     }
    610 
    611     for (int n = 0; n < nevents; ++n) {
    612       if (events[n].data.ptr)
    613         (*(void (*)(int, struct data *payload))events[n].data.ptr)(
    614             events[n].events, &payload);
    615     }
    616   }
    617 
    618   ALOGI("exiting worker thread");
    619 error:
    620   close(uevent_fd);
    621 
    622   if (epoll_fd >= 0) close(epoll_fd);
    623 
    624   return NULL;
    625 }
    626 
    627 void sighandler(int sig) {
    628   if (sig == SIGUSR1) {
    629     destroyThread = true;
    630     ALOGI("destroy set");
    631     return;
    632   }
    633   signal(SIGUSR1, sighandler);
    634 }
    635 
    636 Return<void> Usb::setCallback(const sp<V1_0::IUsbCallback> &callback) {
    637 
    638   sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(callback);
    639 
    640   if (callback != NULL)
    641       if (callback_V1_1 == NULL)
    642           ALOGI("Registering 1.0 callback");
    643 
    644   pthread_mutex_lock(&mLock);
    645   /*
    646    * When both the old callback and new callback values are NULL,
    647    * there is no need to spin off the worker thread.
    648    * When both the values are not NULL, we would already have a
    649    * worker thread running, so updating the callback object would
    650    * be suffice.
    651    */
    652   if ((mCallback_1_0 == NULL && callback == NULL) ||
    653       (mCallback_1_0 != NULL && callback != NULL)) {
    654     /*
    655      * Always store as V1_0 callback object. Type cast to V1_1
    656      * when the callback is actually invoked.
    657      */
    658     mCallback_1_0 = callback;
    659     pthread_mutex_unlock(&mLock);
    660     return Void();
    661   }
    662 
    663   mCallback_1_0 = callback;
    664   ALOGI("registering callback");
    665 
    666   // Kill the worker thread if the new callback is NULL.
    667   if (mCallback_1_0 == NULL) {
    668     pthread_mutex_unlock(&mLock);
    669     if (!pthread_kill(mPoll, SIGUSR1)) {
    670       pthread_join(mPoll, NULL);
    671       ALOGI("pthread destroyed");
    672     }
    673     return Void();
    674   }
    675 
    676   destroyThread = false;
    677   signal(SIGUSR1, sighandler);
    678 
    679   /*
    680    * Create a background thread if the old callback value is NULL
    681    * and being updated with a new value.
    682    */
    683   if (pthread_create(&mPoll, NULL, work, this)) {
    684     ALOGE("pthread creation failed %d", errno);
    685     mCallback_1_0 = NULL;
    686   }
    687 
    688   pthread_mutex_unlock(&mLock);
    689   return Void();
    690 }
    691 
    692 }  // namespace implementation
    693 }  // namespace V1_0
    694 }  // namespace usb
    695 }  // namespace hardware
    696 }  // namespace android
    697