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