2 * Copyright (C) 2017 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #define LOG_TAG "android.hardware.usb@1.1-service.wahoo"
19 #include <android-base/logging.h>
26 #include <sys/types.h>
29 #include <unordered_map>
31 #include <cutils/uevent.h>
32 #include <sys/epoll.h>
33 #include <utils/Errors.h>
34 #include <utils/StrongPointer.h>
42 namespace implementation
{
44 const char GOOGLE_USB_VENDOR_ID_STR
[] = "18d1";
45 const char GOOGLE_USBC_35_ADAPTER_UNPLUGGED_ID_STR
[] = "5029";
47 // Set by the signal handler to destroy the thread
48 volatile bool destroyThread
;
50 static void checkUsbDeviceAutoSuspend(const std::string
& devicePath
);
52 static int32_t readFile(const std::string
&filename
, std::string
*contents
) {
58 fp
= fopen(filename
.c_str(), "r");
60 if ((read
= getline(&line
, &len
, fp
)) != -1) {
62 if ((pos
= strchr(line
, '\n')) != NULL
) *pos
= '\0';
69 ALOGE("fopen failed in readFile %s, errno=%d", filename
.c_str(), errno
);
75 static int32_t writeFile(const std::string
&filename
,
76 const std::string
&contents
) {
80 fp
= fopen(filename
.c_str(), "w");
82 ret
= fputs(contents
.c_str(), fp
);
85 ALOGE("fputs failed in writeFile %s", filename
.c_str());
90 ALOGE("fopen failed in writeFile %s, errno=%d", filename
.c_str(), errno
);
96 std::string
appendRoleNodeHelper(const std::string
&portName
,
98 std::string
node("/sys/class/typec/" + portName
);
101 case PortRoleType::DATA_ROLE
:
102 return node
+ "/data_role";
103 case PortRoleType::POWER_ROLE
:
104 return node
+ "/power_role";
105 case PortRoleType::MODE
:
106 return node
+ "/port_type";
112 std::string
convertRoletoString(PortRole role
) {
113 if (role
.type
== PortRoleType::POWER_ROLE
) {
114 if (role
.role
== static_cast<uint32_t>(PortPowerRole::SOURCE
))
116 else if (role
.role
== static_cast<uint32_t>(PortPowerRole::SINK
))
118 } else if (role
.type
== PortRoleType::DATA_ROLE
) {
119 if (role
.role
== static_cast<uint32_t>(PortDataRole::HOST
)) return "host";
120 if (role
.role
== static_cast<uint32_t>(PortDataRole::DEVICE
))
122 } else if (role
.type
== PortRoleType::MODE
) {
123 if (role
.role
== static_cast<uint32_t>(PortMode_1_1::UFP
)) return "sink";
124 if (role
.role
== static_cast<uint32_t>(PortMode_1_1::DFP
)) return "source";
129 void extractRole(std::string
*roleName
) {
130 std::size_t first
, last
;
132 first
= roleName
->find("[");
133 last
= roleName
->find("]");
135 if (first
!= std::string::npos
&& last
!= std::string::npos
) {
136 *roleName
= roleName
->substr(first
+ 1, last
- first
- 1);
140 void switchToDrp(const std::string
&portName
) {
141 std::string filename
=
142 appendRoleNodeHelper(std::string(portName
.c_str()), PortRoleType::MODE
);
145 if (filename
!= "") {
146 fp
= fopen(filename
.c_str(), "w");
148 int ret
= fputs("dual", fp
);
151 ALOGE("Fatal: Error while switching back to drp");
153 ALOGE("Fatal: Cannot open file to switch back to drp");
156 ALOGE("Fatal: invalid node type");
160 bool switchMode(const hidl_string
&portName
,
161 const PortRole
&newRole
, struct Usb
*usb
) {
162 std::string filename
=
163 appendRoleNodeHelper(std::string(portName
.c_str()), newRole
.type
);
166 bool roleSwitch
= false;
168 if (filename
== "") {
169 ALOGE("Fatal: invalid node type");
173 fp
= fopen(filename
.c_str(), "w");
175 // Hold the lock here to prevent loosing connected signals
176 // as once the file is written the partner added signal
177 // can arrive anytime.
178 pthread_mutex_lock(&usb
->mPartnerLock
);
179 usb
->mPartnerUp
= false;
180 int ret
= fputs(convertRoletoString(newRole
).c_str(), fp
);
188 clock_gettime(CLOCK_MONOTONIC
, &now
);
189 to
.tv_sec
= now
.tv_sec
+ PORT_TYPE_TIMEOUT
;
190 to
.tv_nsec
= now
.tv_nsec
;
192 int err
= pthread_cond_timedwait(&usb
->mPartnerCV
, &usb
->mPartnerLock
, &to
);
193 // There are no uevent signals which implies role swap timed out.
194 if (err
== ETIMEDOUT
) {
195 ALOGI("uevents wait timedout");
197 } else if (!usb
->mPartnerUp
) {
199 // Role switch succeeded since usb->mPartnerUp is true.
204 ALOGI("Role switch failed while wrting to file");
206 pthread_mutex_unlock(&usb
->mPartnerLock
);
210 switchToDrp(std::string(portName
.c_str()));
216 : mLock(PTHREAD_MUTEX_INITIALIZER
),
217 mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER
),
218 mPartnerLock(PTHREAD_MUTEX_INITIALIZER
),
220 pthread_condattr_t attr
;
221 if (pthread_condattr_init(&attr
)) {
222 ALOGE("pthread_condattr_init failed: %s", strerror(errno
));
225 if (pthread_condattr_setclock(&attr
, CLOCK_MONOTONIC
)) {
226 ALOGE("pthread_condattr_setclock failed: %s", strerror(errno
));
229 if (pthread_cond_init(&mPartnerCV
, &attr
)) {
230 ALOGE("pthread_cond_init failed: %s", strerror(errno
));
233 if (pthread_condattr_destroy(&attr
)) {
234 ALOGE("pthread_condattr_destroy failed: %s", strerror(errno
));
240 Return
<void> Usb::switchRole(const hidl_string
&portName
,
241 const PortRole
&newRole
) {
242 std::string filename
=
243 appendRoleNodeHelper(std::string(portName
.c_str()), newRole
.type
);
246 bool roleSwitch
= false;
248 if (filename
== "") {
249 ALOGE("Fatal: invalid node type");
253 pthread_mutex_lock(&mRoleSwitchLock
);
255 ALOGI("filename write: %s role:%s", filename
.c_str(),
256 convertRoletoString(newRole
).c_str());
258 if (newRole
.type
== PortRoleType::MODE
) {
259 roleSwitch
= switchMode(portName
, newRole
, this);
261 fp
= fopen(filename
.c_str(), "w");
263 int ret
= fputs(convertRoletoString(newRole
).c_str(), fp
);
265 if ((ret
!= EOF
) && !readFile(filename
, &written
)) {
266 extractRole(&written
);
267 ALOGI("written: %s", written
.c_str());
268 if (written
== convertRoletoString(newRole
)) {
271 ALOGE("Role switch failed");
274 ALOGE("failed to update the new role");
277 ALOGE("fopen failed");
281 pthread_mutex_lock(&mLock
);
282 if (mCallback_1_0
!= NULL
) {
284 mCallback_1_0
->notifyRoleSwitchStatus(portName
, newRole
,
285 roleSwitch
? Status::SUCCESS
: Status::ERROR
);
287 ALOGE("RoleSwitchStatus error %s", ret
.description().c_str());
289 ALOGE("Not notifying the userspace. Callback is not set");
291 pthread_mutex_unlock(&mLock
);
292 pthread_mutex_unlock(&mRoleSwitchLock
);
297 Status
getAccessoryConnected(const std::string
&portName
, std::string
*accessory
) {
298 std::string filename
=
299 "/sys/class/typec/" + portName
+ "-partner/accessory_mode";
301 if (readFile(filename
, accessory
)) {
302 ALOGE("getAccessoryConnected: Failed to open filesystem node: %s",
304 return Status::ERROR
;
307 return Status::SUCCESS
;
310 Status
getCurrentRoleHelper(const std::string
&portName
, bool connected
,
311 PortRoleType type
, uint32_t *currentRole
) {
312 std::string filename
;
313 std::string roleName
;
314 std::string accessory
;
318 if (type
== PortRoleType::POWER_ROLE
) {
319 filename
= "/sys/class/typec/" + portName
+ "/power_role";
320 *currentRole
= static_cast<uint32_t>(PortPowerRole::NONE
);
321 } else if (type
== PortRoleType::DATA_ROLE
) {
322 filename
= "/sys/class/typec/" + portName
+ "/data_role";
323 *currentRole
= static_cast<uint32_t>(PortDataRole::NONE
);
324 } else if (type
== PortRoleType::MODE
) {
325 filename
= "/sys/class/typec/" + portName
+ "/data_role";
326 *currentRole
= static_cast<uint32_t>(PortMode_1_1::NONE
);
328 return Status::ERROR
;
331 if (!connected
) return Status::SUCCESS
;
333 if (type
== PortRoleType::MODE
) {
334 if (getAccessoryConnected(portName
, &accessory
) != Status::SUCCESS
) {
335 return Status::ERROR
;
337 if (accessory
== "analog_audio") {
338 *currentRole
= static_cast<uint32_t>(PortMode_1_1::AUDIO_ACCESSORY
);
339 return Status::SUCCESS
;
340 } else if (accessory
== "debug") {
341 *currentRole
= static_cast<uint32_t>(PortMode_1_1::DEBUG_ACCESSORY
);
342 return Status::SUCCESS
;
346 if (readFile(filename
, &roleName
)) {
347 ALOGE("getCurrentRole: Failed to open filesystem node: %s",
349 return Status::ERROR
;
352 extractRole(&roleName
);
354 if (roleName
== "source") {
355 *currentRole
= static_cast<uint32_t>(PortPowerRole::SOURCE
);
356 } else if (roleName
== "sink") {
357 *currentRole
= static_cast<uint32_t>(PortPowerRole::SINK
);
358 } else if (roleName
== "host") {
359 if (type
== PortRoleType::DATA_ROLE
)
360 *currentRole
= static_cast<uint32_t>(PortDataRole::HOST
);
362 *currentRole
= static_cast<uint32_t>(PortMode_1_1::DFP
);
363 } else if (roleName
== "device") {
364 if (type
== PortRoleType::DATA_ROLE
)
365 *currentRole
= static_cast<uint32_t>(PortDataRole::DEVICE
);
367 *currentRole
= static_cast<uint32_t>(PortMode_1_1::UFP
);
368 } else if (roleName
!= "none") {
369 /* case for none has already been addressed.
370 * so we check if the role isnt none.
372 return Status::UNRECOGNIZED_ROLE
;
375 return Status::SUCCESS
;
378 Status
getTypeCPortNamesHelper(std::unordered_map
<std::string
, bool> *names
) {
381 dp
= opendir("/sys/class/typec");
385 while ((ep
= readdir(dp
))) {
386 if (ep
->d_type
== DT_LNK
) {
387 if (std::string::npos
== std::string(ep
->d_name
).find("-partner")) {
388 std::unordered_map
<std::string
, bool>::const_iterator portName
=
389 names
->find(ep
->d_name
);
390 if (portName
== names
->end()) {
391 names
->insert({ep
->d_name
, false});
394 (*names
)[std::strtok(ep
->d_name
, "-")] = true;
399 return Status::SUCCESS
;
402 ALOGE("Failed to open /sys/class/typec");
403 return Status::ERROR
;
406 bool canSwitchRoleHelper(const std::string
&portName
, PortRoleType
/*type*/) {
407 std::string filename
=
408 "/sys/class/typec/" + portName
+ "-partner/supports_usb_power_delivery";
409 std::string supportsPD
;
411 if (!readFile(filename
, &supportsPD
)) {
412 if (supportsPD
== "yes") {
421 * Reuse the same method for both V1_0 and V1_1 callback objects.
422 * The caller of this method would reconstruct the V1_0::PortStatus
423 * object if required.
425 Status
getPortStatusHelper(hidl_vec
<PortStatus_1_1
> *currentPortStatus_1_1
,
427 std::unordered_map
<std::string
, bool> names
;
428 Status result
= getTypeCPortNamesHelper(&names
);
431 if (result
== Status::SUCCESS
) {
432 currentPortStatus_1_1
->resize(names
.size());
433 for (std::pair
<std::string
, bool> port
: names
) {
435 ALOGI("%s", port
.first
.c_str());
436 (*currentPortStatus_1_1
)[i
].status
.portName
= port
.first
;
438 uint32_t currentRole
;
439 if (getCurrentRoleHelper(port
.first
, port
.second
,
440 PortRoleType::POWER_ROLE
,
441 ¤tRole
) == Status::SUCCESS
) {
442 (*currentPortStatus_1_1
)[i
].status
.currentPowerRole
=
443 static_cast<PortPowerRole
>(currentRole
);
445 ALOGE("Error while retreiving portNames");
449 if (getCurrentRoleHelper(port
.first
, port
.second
, PortRoleType::DATA_ROLE
,
450 ¤tRole
) == Status::SUCCESS
) {
451 (*currentPortStatus_1_1
)[i
].status
.currentDataRole
=
452 static_cast<PortDataRole
>(currentRole
);
454 ALOGE("Error while retreiving current port role");
458 if (getCurrentRoleHelper(port
.first
, port
.second
, PortRoleType::MODE
,
459 ¤tRole
) == Status::SUCCESS
) {
460 (*currentPortStatus_1_1
)[i
].currentMode
=
461 static_cast<PortMode_1_1
>(currentRole
);
462 (*currentPortStatus_1_1
)[i
].status
.currentMode
=
463 static_cast<V1_0::PortMode
>(currentRole
);
465 ALOGE("Error while retreiving current data role");
469 (*currentPortStatus_1_1
)[i
].status
.canChangeMode
= true;
470 (*currentPortStatus_1_1
)[i
].status
.canChangeDataRole
=
471 port
.second
? canSwitchRoleHelper(port
.first
, PortRoleType::DATA_ROLE
)
473 (*currentPortStatus_1_1
)[i
].status
.canChangePowerRole
=
475 ? canSwitchRoleHelper(port
.first
, PortRoleType::POWER_ROLE
)
478 ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d",
479 port
.second
, (*currentPortStatus_1_1
)[i
].status
.canChangeMode
,
480 (*currentPortStatus_1_1
)[i
].status
.canChangeDataRole
,
481 (*currentPortStatus_1_1
)[i
].status
.canChangePowerRole
);
484 (*currentPortStatus_1_1
)[i
].status
.supportedModes
= V1_0::PortMode::DFP
;
486 (*currentPortStatus_1_1
)[i
].supportedModes
= PortMode_1_1::UFP
| PortMode_1_1::DFP
;
487 (*currentPortStatus_1_1
)[i
].status
.supportedModes
= V1_0::PortMode::NONE
;
488 (*currentPortStatus_1_1
)[i
].status
.currentMode
= V1_0::PortMode::NONE
;
491 return Status::SUCCESS
;
494 return Status::ERROR
;
497 Return
<void> Usb::queryPortStatus() {
498 hidl_vec
<PortStatus_1_1
> currentPortStatus_1_1
;
499 hidl_vec
<V1_0::PortStatus
> currentPortStatus
;
501 sp
<IUsbCallback
> callback_V1_1
= IUsbCallback::castFrom(mCallback_1_0
);
503 pthread_mutex_lock(&mLock
);
504 if (mCallback_1_0
!= NULL
) {
505 if (callback_V1_1
!= NULL
) {
506 status
= getPortStatusHelper(¤tPortStatus_1_1
, false);
508 status
= getPortStatusHelper(¤tPortStatus_1_1
, true);
509 currentPortStatus
.resize(currentPortStatus_1_1
.size());
510 for (unsigned long i
= 0; i
< currentPortStatus_1_1
.size(); i
++)
511 currentPortStatus
[i
] = currentPortStatus_1_1
[i
].status
;
516 if (callback_V1_1
!= NULL
)
517 ret
= callback_V1_1
->notifyPortStatusChange_1_1(currentPortStatus_1_1
, status
);
519 ret
= mCallback_1_0
->notifyPortStatusChange(currentPortStatus
, status
);
522 ALOGE("queryPortStatus_1_1 error %s", ret
.description().c_str());
524 ALOGI("Notifying userspace skipped. Callback is NULL");
526 pthread_mutex_unlock(&mLock
);
533 android::hardware::usb::V1_1::implementation::Usb
*usb
;
536 static void uevent_event(uint32_t /*epevents*/, struct data
*payload
) {
537 char msg
[UEVENT_MSG_LEN
+ 2];
541 n
= uevent_kernel_multicast_recv(payload
->uevent_fd
, msg
, UEVENT_MSG_LEN
);
543 if (n
>= UEVENT_MSG_LEN
) /* overflow -- discard */
552 if (std::regex_match(cp
, std::regex("(add)(.*)(-partner)"))) {
553 ALOGI("partner added");
554 pthread_mutex_lock(&payload
->usb
->mPartnerLock
);
555 payload
->usb
->mPartnerUp
= true;
556 pthread_cond_signal(&payload
->usb
->mPartnerCV
);
557 pthread_mutex_unlock(&payload
->usb
->mPartnerLock
);
558 } else if (!strncmp(cp
, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
559 hidl_vec
<PortStatus_1_1
> currentPortStatus_1_1
;
560 ALOGI("uevent received %s", cp
);
561 pthread_mutex_lock(&payload
->usb
->mLock
);
562 if (payload
->usb
->mCallback_1_0
!= NULL
) {
563 sp
<IUsbCallback
> callback_V1_1
= IUsbCallback::castFrom(payload
->usb
->mCallback_1_0
);
567 if (callback_V1_1
!= NULL
) {
568 Status status
= getPortStatusHelper(¤tPortStatus_1_1
, false);
569 ret
= callback_V1_1
->notifyPortStatusChange_1_1(
570 currentPortStatus_1_1
, status
);
571 } else { // V1_0 callback
572 Status status
= getPortStatusHelper(¤tPortStatus_1_1
, true);
575 * Copying the result from getPortStatusHelper
576 * into V1_0::PortStatus to pass back through
577 * the V1_0 callback object.
579 hidl_vec
<V1_0::PortStatus
> currentPortStatus
;
580 currentPortStatus
.resize(currentPortStatus_1_1
.size());
581 for (unsigned long i
= 0; i
< currentPortStatus_1_1
.size(); i
++)
582 currentPortStatus
[i
] = currentPortStatus_1_1
[i
].status
;
584 ret
= payload
->usb
->mCallback_1_0
->notifyPortStatusChange(
585 currentPortStatus
, status
);
587 if (!ret
.isOk()) ALOGE("error %s", ret
.description().c_str());
589 ALOGI("Notifying userspace skipped. Callback is NULL");
591 pthread_mutex_unlock(&payload
->usb
->mLock
);
593 //Role switch is not in progress and port is in disconnected state
594 if (!pthread_mutex_trylock(&payload
->usb
->mRoleSwitchLock
)) {
595 for (unsigned long i
= 0; i
< currentPortStatus_1_1
.size(); i
++) {
596 DIR *dp
= opendir(std::string("/sys/class/typec/"
597 + std::string(currentPortStatus_1_1
[i
].status
.portName
.c_str())
598 + "-partner").c_str());
600 //PortRole role = {.role = static_cast<uint32_t>(PortMode::UFP)};
601 switchToDrp(currentPortStatus_1_1
[i
].status
.portName
);
606 pthread_mutex_unlock(&payload
->usb
->mRoleSwitchLock
);
609 } else if (std::regex_match(cp
, match
,
610 std::regex("add@(/devices/soc/a800000\\.ssusb/a800000\\.dwc3/xhci-hcd\\.0\\.auto/"
611 "usb\\d/\\d-\\d)/.*"))) {
612 if (match
.size() == 2) {
613 std::csub_match submatch
= match
[1];
614 checkUsbDeviceAutoSuspend("/sys" + submatch
.str());
618 /* advance to after the next \0 */
623 void *work(void *param
) {
624 int epoll_fd
, uevent_fd
;
625 struct epoll_event ev
;
629 ALOGE("creating thread");
631 uevent_fd
= uevent_open_socket(64 * 1024, true);
634 ALOGE("uevent_init: uevent_open_socket failed\n");
638 payload
.uevent_fd
= uevent_fd
;
639 payload
.usb
= (android::hardware::usb::V1_1::implementation::Usb
*)param
;
641 fcntl(uevent_fd
, F_SETFL
, O_NONBLOCK
);
644 ev
.data
.ptr
= (void *)uevent_event
;
646 epoll_fd
= epoll_create(64);
647 if (epoll_fd
== -1) {
648 ALOGE("epoll_create failed; errno=%d", errno
);
652 if (epoll_ctl(epoll_fd
, EPOLL_CTL_ADD
, uevent_fd
, &ev
) == -1) {
653 ALOGE("epoll_ctl failed; errno=%d", errno
);
657 while (!destroyThread
) {
658 struct epoll_event events
[64];
660 nevents
= epoll_wait(epoll_fd
, events
, 64, -1);
662 if (errno
== EINTR
) continue;
663 ALOGE("usb epoll_wait failed; errno=%d", errno
);
667 for (int n
= 0; n
< nevents
; ++n
) {
668 if (events
[n
].data
.ptr
)
669 (*(void (*)(int, struct data
*payload
))events
[n
].data
.ptr
)(
670 events
[n
].events
, &payload
);
674 ALOGI("exiting worker thread");
678 if (epoll_fd
>= 0) close(epoll_fd
);
683 void sighandler(int sig
) {
684 if (sig
== SIGUSR1
) {
685 destroyThread
= true;
686 ALOGI("destroy set");
689 signal(SIGUSR1
, sighandler
);
692 Return
<void> Usb::setCallback(const sp
<V1_0::IUsbCallback
> &callback
) {
694 sp
<IUsbCallback
> callback_V1_1
= IUsbCallback::castFrom(callback
);
696 if (callback
!= NULL
)
697 if (callback_V1_1
== NULL
)
698 ALOGI("Registering 1.0 callback");
700 pthread_mutex_lock(&mLock
);
702 * When both the old callback and new callback values are NULL,
703 * there is no need to spin off the worker thread.
704 * When both the values are not NULL, we would already have a
705 * worker thread running, so updating the callback object would
708 if ((mCallback_1_0
== NULL
&& callback
== NULL
) ||
709 (mCallback_1_0
!= NULL
&& callback
!= NULL
)) {
711 * Always store as V1_0 callback object. Type cast to V1_1
712 * when the callback is actually invoked.
714 mCallback_1_0
= callback
;
715 pthread_mutex_unlock(&mLock
);
719 mCallback_1_0
= callback
;
720 ALOGI("registering callback");
722 // Kill the worker thread if the new callback is NULL.
723 if (mCallback_1_0
== NULL
) {
724 pthread_mutex_unlock(&mLock
);
725 if (!pthread_kill(mPoll
, SIGUSR1
)) {
726 pthread_join(mPoll
, NULL
);
727 ALOGI("pthread destroyed");
732 destroyThread
= false;
733 signal(SIGUSR1
, sighandler
);
736 * Create a background thread if the old callback value is NULL
737 * and being updated with a new value.
739 if (pthread_create(&mPoll
, NULL
, work
, this)) {
740 ALOGE("pthread creation failed %d", errno
);
741 mCallback_1_0
= NULL
;
744 pthread_mutex_unlock(&mLock
);
749 * whitelisting USB device idProduct and idVendor to allow auto suspend.
751 static bool canProductAutoSuspend(const std::string
&deviceIdVendor
,
752 const std::string
&deviceIdProduct
) {
753 if (deviceIdVendor
== GOOGLE_USB_VENDOR_ID_STR
&&
754 deviceIdProduct
== GOOGLE_USBC_35_ADAPTER_UNPLUGGED_ID_STR
) {
760 static bool canUsbDeviceAutoSuspend(const std::string
&devicePath
) {
761 std::string deviceIdVendor
;
762 std::string deviceIdProduct
;
763 readFile(devicePath
+ "/idVendor", &deviceIdVendor
);
764 readFile(devicePath
+ "/idProduct", &deviceIdProduct
);
766 // deviceIdVendor and deviceIdProduct will be empty strings if readFile fails
767 return canProductAutoSuspend(deviceIdVendor
, deviceIdProduct
);
771 * function to consume USB device plugin events (on receiving a
772 * USB device path string), and enable autosupend on the USB device if
775 void checkUsbDeviceAutoSuspend(const std::string
& devicePath
) {
777 * Currently we only actively enable devices that should be autosuspended, and leave others
780 if (canUsbDeviceAutoSuspend(devicePath
)) {
781 ALOGI("auto suspend usb device %s", devicePath
.c_str());
782 writeFile(devicePath
+ "/power/control", "auto");
786 } // namespace implementation
789 } // namespace hardware
790 } // namespace android