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");
387 while ((ep
= readdir(dp
))) {
388 if (ep
->d_type
== DT_LNK
) {
389 if (std::string::npos
== std::string(ep
->d_name
).find("-partner")) {
390 std::unordered_map
<std::string
, bool>::const_iterator portName
=
391 names
->find(ep
->d_name
);
392 if (portName
== names
->end()) {
393 names
->insert({ep
->d_name
, false});
396 (*names
)[std::strtok(ep
->d_name
, "-")] = true;
401 return Status::SUCCESS
;
404 ALOGE("Failed to open /sys/class/typec");
405 return Status::ERROR
;
408 bool canSwitchRoleHelper(const std::string
&portName
, PortRoleType
/*type*/) {
409 std::string filename
=
410 "/sys/class/typec/" + portName
+ "-partner/supports_usb_power_delivery";
411 std::string supportsPD
;
413 if (!readFile(filename
, &supportsPD
)) {
414 if (supportsPD
== "yes") {
423 * Reuse the same method for both V1_0 and V1_1 callback objects.
424 * The caller of this method would reconstruct the V1_0::PortStatus
425 * object if required.
427 Status
getPortStatusHelper(hidl_vec
<PortStatus_1_1
> *currentPortStatus_1_1
,
429 std::unordered_map
<std::string
, bool> names
;
430 Status result
= getTypeCPortNamesHelper(&names
);
433 if (result
== Status::SUCCESS
) {
434 currentPortStatus_1_1
->resize(names
.size());
435 for (std::pair
<std::string
, bool> port
: names
) {
437 ALOGI("%s", port
.first
.c_str());
438 (*currentPortStatus_1_1
)[i
].status
.portName
= port
.first
;
440 uint32_t currentRole
;
441 if (getCurrentRoleHelper(port
.first
, port
.second
,
442 PortRoleType::POWER_ROLE
,
443 ¤tRole
) == Status::SUCCESS
) {
444 (*currentPortStatus_1_1
)[i
].status
.currentPowerRole
=
445 static_cast<PortPowerRole
>(currentRole
);
447 ALOGE("Error while retreiving portNames");
451 if (getCurrentRoleHelper(port
.first
, port
.second
, PortRoleType::DATA_ROLE
,
452 ¤tRole
) == Status::SUCCESS
) {
453 (*currentPortStatus_1_1
)[i
].status
.currentDataRole
=
454 static_cast<PortDataRole
>(currentRole
);
456 ALOGE("Error while retreiving current port role");
460 if (getCurrentRoleHelper(port
.first
, port
.second
, PortRoleType::MODE
,
461 ¤tRole
) == Status::SUCCESS
) {
462 (*currentPortStatus_1_1
)[i
].currentMode
=
463 static_cast<PortMode_1_1
>(currentRole
);
464 (*currentPortStatus_1_1
)[i
].status
.currentMode
=
465 static_cast<V1_0::PortMode
>(currentRole
);
467 ALOGE("Error while retreiving current data role");
471 (*currentPortStatus_1_1
)[i
].status
.canChangeMode
= true;
472 (*currentPortStatus_1_1
)[i
].status
.canChangeDataRole
=
473 port
.second
? canSwitchRoleHelper(port
.first
, PortRoleType::DATA_ROLE
)
475 (*currentPortStatus_1_1
)[i
].status
.canChangePowerRole
=
477 ? canSwitchRoleHelper(port
.first
, PortRoleType::POWER_ROLE
)
480 ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d",
481 port
.second
, (*currentPortStatus_1_1
)[i
].status
.canChangeMode
,
482 (*currentPortStatus_1_1
)[i
].status
.canChangeDataRole
,
483 (*currentPortStatus_1_1
)[i
].status
.canChangePowerRole
);
486 (*currentPortStatus_1_1
)[i
].status
.supportedModes
= V1_0::PortMode::DFP
;
488 (*currentPortStatus_1_1
)[i
].supportedModes
= PortMode_1_1::UFP
| PortMode_1_1::DFP
;
489 (*currentPortStatus_1_1
)[i
].status
.supportedModes
= V1_0::PortMode::NONE
;
490 (*currentPortStatus_1_1
)[i
].status
.currentMode
= V1_0::PortMode::NONE
;
493 return Status::SUCCESS
;
496 return Status::ERROR
;
499 Return
<void> Usb::queryPortStatus() {
500 hidl_vec
<PortStatus_1_1
> currentPortStatus_1_1
;
501 hidl_vec
<V1_0::PortStatus
> currentPortStatus
;
503 sp
<IUsbCallback
> callback_V1_1
= IUsbCallback::castFrom(mCallback_1_0
);
505 pthread_mutex_lock(&mLock
);
506 if (mCallback_1_0
!= NULL
) {
507 if (callback_V1_1
!= NULL
) {
508 status
= getPortStatusHelper(¤tPortStatus_1_1
, false);
510 status
= getPortStatusHelper(¤tPortStatus_1_1
, true);
511 currentPortStatus
.resize(currentPortStatus_1_1
.size());
512 for (unsigned long i
= 0; i
< currentPortStatus_1_1
.size(); i
++)
513 currentPortStatus
[i
] = currentPortStatus_1_1
[i
].status
;
518 if (callback_V1_1
!= NULL
)
519 ret
= callback_V1_1
->notifyPortStatusChange_1_1(currentPortStatus_1_1
, status
);
521 ret
= mCallback_1_0
->notifyPortStatusChange(currentPortStatus
, status
);
524 ALOGE("queryPortStatus_1_1 error %s", ret
.description().c_str());
526 ALOGI("Notifying userspace skipped. Callback is NULL");
528 pthread_mutex_unlock(&mLock
);
535 android::hardware::usb::V1_1::implementation::Usb
*usb
;
538 static void uevent_event(uint32_t /*epevents*/, struct data
*payload
) {
539 char msg
[UEVENT_MSG_LEN
+ 2];
543 n
= uevent_kernel_multicast_recv(payload
->uevent_fd
, msg
, UEVENT_MSG_LEN
);
545 if (n
>= UEVENT_MSG_LEN
) /* overflow -- discard */
554 if (std::regex_match(cp
, std::regex("(add)(.*)(-partner)"))) {
555 ALOGI("partner added");
556 pthread_mutex_lock(&payload
->usb
->mPartnerLock
);
557 payload
->usb
->mPartnerUp
= true;
558 pthread_cond_signal(&payload
->usb
->mPartnerCV
);
559 pthread_mutex_unlock(&payload
->usb
->mPartnerLock
);
560 } else if (!strncmp(cp
, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
561 hidl_vec
<PortStatus_1_1
> currentPortStatus_1_1
;
562 ALOGI("uevent received %s", cp
);
563 pthread_mutex_lock(&payload
->usb
->mLock
);
564 if (payload
->usb
->mCallback_1_0
!= NULL
) {
565 sp
<IUsbCallback
> callback_V1_1
= IUsbCallback::castFrom(payload
->usb
->mCallback_1_0
);
569 if (callback_V1_1
!= NULL
) {
570 Status status
= getPortStatusHelper(¤tPortStatus_1_1
, false);
571 ret
= callback_V1_1
->notifyPortStatusChange_1_1(
572 currentPortStatus_1_1
, status
);
573 } else { // V1_0 callback
574 Status status
= getPortStatusHelper(¤tPortStatus_1_1
, true);
577 * Copying the result from getPortStatusHelper
578 * into V1_0::PortStatus to pass back through
579 * the V1_0 callback object.
581 hidl_vec
<V1_0::PortStatus
> currentPortStatus
;
582 currentPortStatus
.resize(currentPortStatus_1_1
.size());
583 for (unsigned long i
= 0; i
< currentPortStatus_1_1
.size(); i
++)
584 currentPortStatus
[i
] = currentPortStatus_1_1
[i
].status
;
586 ret
= payload
->usb
->mCallback_1_0
->notifyPortStatusChange(
587 currentPortStatus
, status
);
589 if (!ret
.isOk()) ALOGE("error %s", ret
.description().c_str());
591 ALOGI("Notifying userspace skipped. Callback is NULL");
593 pthread_mutex_unlock(&payload
->usb
->mLock
);
595 //Role switch is not in progress and port is in disconnected state
596 if (!pthread_mutex_trylock(&payload
->usb
->mRoleSwitchLock
)) {
597 for (unsigned long i
= 0; i
< currentPortStatus_1_1
.size(); i
++) {
598 DIR *dp
= opendir(std::string("/sys/class/typec/"
599 + std::string(currentPortStatus_1_1
[i
].status
.portName
.c_str())
600 + "-partner").c_str());
602 //PortRole role = {.role = static_cast<uint32_t>(PortMode::UFP)};
603 switchToDrp(currentPortStatus_1_1
[i
].status
.portName
);
608 pthread_mutex_unlock(&payload
->usb
->mRoleSwitchLock
);
611 } else if (std::regex_match(cp
, match
,
612 std::regex("add@(/devices/soc/a800000\\.ssusb/a800000\\.dwc3/xhci-hcd\\.0\\.auto/"
613 "usb\\d/\\d-\\d)/.*"))) {
614 if (match
.size() == 2) {
615 std::csub_match submatch
= match
[1];
616 checkUsbDeviceAutoSuspend("/sys" + submatch
.str());
620 /* advance to after the next \0 */
625 void *work(void *param
) {
626 int epoll_fd
, uevent_fd
;
627 struct epoll_event ev
;
631 ALOGE("creating thread");
633 uevent_fd
= uevent_open_socket(64 * 1024, true);
636 ALOGE("uevent_init: uevent_open_socket failed\n");
640 payload
.uevent_fd
= uevent_fd
;
641 payload
.usb
= (android::hardware::usb::V1_1::implementation::Usb
*)param
;
643 fcntl(uevent_fd
, F_SETFL
, O_NONBLOCK
);
646 ev
.data
.ptr
= (void *)uevent_event
;
648 epoll_fd
= epoll_create(64);
649 if (epoll_fd
== -1) {
650 ALOGE("epoll_create failed; errno=%d", errno
);
654 if (epoll_ctl(epoll_fd
, EPOLL_CTL_ADD
, uevent_fd
, &ev
) == -1) {
655 ALOGE("epoll_ctl failed; errno=%d", errno
);
659 while (!destroyThread
) {
660 struct epoll_event events
[64];
662 nevents
= epoll_wait(epoll_fd
, events
, 64, -1);
664 if (errno
== EINTR
) continue;
665 ALOGE("usb epoll_wait failed; errno=%d", errno
);
669 for (int n
= 0; n
< nevents
; ++n
) {
670 if (events
[n
].data
.ptr
)
671 (*(void (*)(int, struct data
*payload
))events
[n
].data
.ptr
)(
672 events
[n
].events
, &payload
);
676 ALOGI("exiting worker thread");
680 if (epoll_fd
>= 0) close(epoll_fd
);
685 void sighandler(int sig
) {
686 if (sig
== SIGUSR1
) {
687 destroyThread
= true;
688 ALOGI("destroy set");
691 signal(SIGUSR1
, sighandler
);
694 Return
<void> Usb::setCallback(const sp
<V1_0::IUsbCallback
> &callback
) {
696 sp
<IUsbCallback
> callback_V1_1
= IUsbCallback::castFrom(callback
);
698 if (callback
!= NULL
)
699 if (callback_V1_1
== NULL
)
700 ALOGI("Registering 1.0 callback");
702 pthread_mutex_lock(&mLock
);
704 * When both the old callback and new callback values are NULL,
705 * there is no need to spin off the worker thread.
706 * When both the values are not NULL, we would already have a
707 * worker thread running, so updating the callback object would
710 if ((mCallback_1_0
== NULL
&& callback
== NULL
) ||
711 (mCallback_1_0
!= NULL
&& callback
!= NULL
)) {
713 * Always store as V1_0 callback object. Type cast to V1_1
714 * when the callback is actually invoked.
716 mCallback_1_0
= callback
;
717 pthread_mutex_unlock(&mLock
);
721 mCallback_1_0
= callback
;
722 ALOGI("registering callback");
724 // Kill the worker thread if the new callback is NULL.
725 if (mCallback_1_0
== NULL
) {
726 pthread_mutex_unlock(&mLock
);
727 if (!pthread_kill(mPoll
, SIGUSR1
)) {
728 pthread_join(mPoll
, NULL
);
729 ALOGI("pthread destroyed");
734 destroyThread
= false;
735 signal(SIGUSR1
, sighandler
);
738 * Create a background thread if the old callback value is NULL
739 * and being updated with a new value.
741 if (pthread_create(&mPoll
, NULL
, work
, this)) {
742 ALOGE("pthread creation failed %d", errno
);
743 mCallback_1_0
= NULL
;
746 pthread_mutex_unlock(&mLock
);
751 * whitelisting USB device idProduct and idVendor to allow auto suspend.
753 static bool canProductAutoSuspend(const std::string
&deviceIdVendor
,
754 const std::string
&deviceIdProduct
) {
755 if (deviceIdVendor
== GOOGLE_USB_VENDOR_ID_STR
&&
756 deviceIdProduct
== GOOGLE_USBC_35_ADAPTER_UNPLUGGED_ID_STR
) {
762 static bool canUsbDeviceAutoSuspend(const std::string
&devicePath
) {
763 std::string deviceIdVendor
;
764 std::string deviceIdProduct
;
765 readFile(devicePath
+ "/idVendor", &deviceIdVendor
);
766 readFile(devicePath
+ "/idProduct", &deviceIdProduct
);
768 // deviceIdVendor and deviceIdProduct will be empty strings if readFile fails
769 return canProductAutoSuspend(deviceIdVendor
, deviceIdProduct
);
773 * function to consume USB device plugin events (on receiving a
774 * USB device path string), and enable autosupend on the USB device if
777 void checkUsbDeviceAutoSuspend(const std::string
& devicePath
) {
779 * Currently we only actively enable devices that should be autosuspended, and leave others
782 if (canUsbDeviceAutoSuspend(devicePath
)) {
783 ALOGI("auto suspend usb device %s", devicePath
.c_str());
784 writeFile(devicePath
+ "/power/control", "auto");
788 } // namespace implementation
791 } // namespace hardware
792 } // namespace android