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