diff --git a/usb/usb/Android.bp b/usb/usb/Android.bp index a8842385..ef6cdcfc 100644 --- a/usb/usb/Android.bp +++ b/usb/usb/Android.bp @@ -52,7 +52,7 @@ cc_binary { "android.frameworks.stats-V2-ndk", "pixelatoms-cpp", "libbinder_ndk", - + "libprotobuf-cpp-lite", ], static_libs: [ "libpixelusb-aidl", diff --git a/usb/usb/Usb.cpp b/usb/usb/Usb.cpp index 40307af3..3fb1f0df 100644 --- a/usb/usb/Usb.cpp +++ b/usb/usb/Usb.cpp @@ -42,6 +42,7 @@ #include "Usb.h" #include +#include #include #include @@ -54,6 +55,9 @@ using android::base::Trim; using android::hardware::google::pixel::getStatsService; using android::hardware::google::pixel::PixelAtoms::VendorUsbPortOverheat; using android::hardware::google::pixel::reportUsbPortOverheat; +using android::hardware::google::pixel::PixelAtoms::VendorUsbDataSessionEvent; +using android::hardware::google::pixel::reportUsbDataSessionEvent; +using android::hardware::google::pixel::usb::BuildVendorUsbDataSessionEvent; namespace aidl { namespace android { @@ -98,6 +102,8 @@ void queryVersionHelper(android::hardware::usb::Usb *usb, std::vector *currentPortStatus); AltModeData::DisplayPortAltModeData constructAltModeData(string hpd, string pin_assignment, string link_status, string vdo); +void queryUsbDataSession(android::hardware::usb::Usb *usb, + std::vector *currentPortStatus); #define USB_STATE_MAX_LEN 20 @@ -957,6 +963,7 @@ void queryVersionHelper(android::hardware::usb::Usb *usb, queryMoistureDetectionStatus(currentPortStatus); queryPowerTransferStatus(currentPortStatus); queryNonCompliantChargerStatus(currentPortStatus); + queryUsbDataSession(usb, currentPortStatus); pthread_mutex_lock(&usb->mDisplayPortLock); if (!usb->mDisplayPortFirstSetupDone && usb->getDisplayPortUsbPathHelper(&displayPortUsbPath) == Status::SUCCESS) { @@ -1052,6 +1059,54 @@ void report_overheat_event(android::hardware::usb::Usb *usb) { } } +void report_usb_data_session_event(android::hardware::usb::Usb *usb) { + std::vector events; + + if (usb->mDataRole == PortDataRole::DEVICE) { + VendorUsbDataSessionEvent event; + BuildVendorUsbDataSessionEvent(false /* is_host */, std::chrono::steady_clock::now(), + usb->mDataSessionStart, &usb->mDeviceState.states, + &usb->mDeviceState.timestamps, &event); + events.push_back(event); + } else if (usb->mDataRole == PortDataRole::HOST) { + bool empty = true; + for (auto &entry : usb->mHostStateMap) { + // Host port will at least get an not_attached event after enablement, + // skip upload if no additional state is added. + if (entry.second.states.size() > 1) { + VendorUsbDataSessionEvent event; + BuildVendorUsbDataSessionEvent(true /* is_host */, std::chrono::steady_clock::now(), + usb->mDataSessionStart, &entry.second.states, + &entry.second.timestamps, &event); + events.push_back(event); + empty = false; + } + } + // All host ports have no state update, upload an event to reflect it + if (empty && usb->mHostStateMap.size() > 0) { + VendorUsbDataSessionEvent event; + BuildVendorUsbDataSessionEvent(true /* is_host */, std::chrono::steady_clock::now(), + usb->mDataSessionStart, + &usb->mHostStateMap.begin()->second.states, + &usb->mHostStateMap.begin()->second.timestamps, + &event); + events.push_back(event); + } + } else { + return; + } + + const shared_ptr stats_client = getStatsService(); + if (!stats_client) { + ALOGE("Unable to get AIDL Stats service"); + return; + } + + for (auto &event : events) { + reportUsbDataSessionEvent(stats_client, event); + } +} + struct data { int uevent_fd; ::aidl::android::hardware::usb::Usb *usb; @@ -1141,14 +1196,16 @@ static int registerEpollEntryByFile(Usb *usb, std::string name, int flags, } static void clearUsbDeviceState(struct Usb::usbDeviceState *device) { - device->latestState.clear(); + device->states.clear(); + device->timestamps.clear(); device->portResetCount = 0; } static void updateUsbDeviceState(struct Usb::usbDeviceState *device, char *state) { ALOGI("Update USB device state: %s", state); - device->latestState = state; + device->states.push_back(state); + device->timestamps.push_back(std::chrono::steady_clock::now()); if (!std::strcmp(state, "configured\n")) { device->portResetCount = 0; @@ -1168,6 +1225,37 @@ static void host_event(uint32_t /*epevents*/, struct Usb::payload *payload) { updateUsbDeviceState(&payload->usb->mHostStateMap[payload->name], state); } +void queryUsbDataSession(android::hardware::usb::Usb *usb, + std::vector *currentPortStatus) { + PortDataRole newDataRole = (*currentPortStatus)[0].currentDataRole; + PowerBrickStatus newPowerBrickStatus = (*currentPortStatus)[0].powerBrickStatus; + + if (newDataRole != usb->mDataRole) { + // Upload metrics for the last non-powerbrick data session that has ended + if (usb->mDataRole != PortDataRole::NONE && !usb->mIsPowerBrickConnected) { + report_usb_data_session_event(usb); + } + + // Set up for the new data session + usb->mDataRole = newDataRole; + usb->mDataSessionStart = std::chrono::steady_clock::now(); + usb->mIsPowerBrickConnected = (newPowerBrickStatus == PowerBrickStatus::CONNECTED); + if (newDataRole == PortDataRole::DEVICE) { + clearUsbDeviceState(&usb->mDeviceState); + } else if (newDataRole == PortDataRole::HOST) { + for (auto &entry : usb->mHostStateMap) { + clearUsbDeviceState(&entry.second); + } + } + } + + // PowerBrickStatus could flip from DISCONNECTED to CONNECTED during the same data + // session when BC1.2 SDP times out and falls back to DCP + if (newPowerBrickStatus == PowerBrickStatus::CONNECTED) { + usb->mIsPowerBrickConnected = true; + } +} + static void uevent_event(uint32_t /*epevents*/, struct Usb::payload *payload) { char msg[UEVENT_MSG_LEN + 2]; char *cp; @@ -1263,7 +1351,6 @@ static void uevent_event(uint32_t /*epevents*/, struct Usb::payload *payload) { registerEpollEntryByFile(payload->usb, path, EPOLLPRI, host_event); } else if (action == "unbind") { unregisterEpollEntry(payload->usb, path); - clearUsbDeviceState(&payload->usb->mHostStateMap[path]); } } } diff --git a/usb/usb/Usb.h b/usb/usb/Usb.h index 566fc73c..2cb8f2df 100644 --- a/usb/usb/Usb.h +++ b/usb/usb/Usb.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -132,12 +133,20 @@ struct Usb : public BnUsb { // USB device state monitoring struct usbDeviceState { - std::string latestState; + // Usb device state raw strings read from sysfs + std::vector states; + // Timestamps of when the usb device states were captured + std::vector timestamps; int portResetCount; }; struct usbDeviceState mDeviceState; // Map host device path name to usbDeviceState std::map mHostStateMap; + // Cache relevant info for USB data session metrics collection when a session starts, including + // the data role, power brick status and the time when the session starts. + PortDataRole mDataRole; + bool mIsPowerBrickConnected; + std::chrono::steady_clock::time_point mDataSessionStart; // File monitoring through epoll int mEpollFd;