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