diff options
-rw-r--r-- | usb/usb/Android.bp | 1 | ||||
-rw-r--r-- | usb/usb/Usb.cpp | 93 | ||||
-rw-r--r-- | usb/usb/Usb.h | 11 |
3 files changed, 101 insertions, 4 deletions
diff --git a/usb/usb/Android.bp b/usb/usb/Android.bp index c35666a..e28667f 100644 --- a/usb/usb/Android.bp +++ b/usb/usb/Android.bp @@ -53,6 +53,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 bae5463..9aa09da 100644 --- a/usb/usb/Usb.cpp +++ b/usb/usb/Usb.cpp @@ -43,6 +43,7 @@ #include "Usb.h" #include <aidl/android/frameworks/stats/IStats.h> +#include <pixelusb/CommonUtils.h> #include <pixelusb/UsbGadgetAidlCommon.h> #include <pixelstats/StatsHelper.h> @@ -53,6 +54,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; using android::String8; using android::Vector; @@ -94,6 +98,8 @@ constexpr char kHostUeventRegex[] = "^(bind|unbind)@(/devices/platform/11210000\ constexpr int kSamplingIntervalSec = 5; void queryVersionHelper(android::hardware::usb::Usb *usb, std::vector<PortStatus> *currentPortStatus); +void queryUsbDataSession(android::hardware::usb::Usb *usb, + std::vector<PortStatus> *currentPortStatus); #define USB_STATE_MAX_LEN 20 #define CTRL_TRANSFER_TIMEOUT_MSEC 1000 @@ -909,6 +915,7 @@ void queryVersionHelper(android::hardware::usb::Usb *usb, queryMoistureDetectionStatus(currentPortStatus); queryPowerTransferStatus(currentPortStatus); queryNonCompliantChargerStatus(currentPortStatus); + queryUsbDataSession(usb, currentPortStatus); if (usb->mCallback != NULL) { ScopedAStatus ret = usb->mCallback->notifyPortStatusChange(*currentPortStatus, status); @@ -995,6 +1002,54 @@ void report_overheat_event(android::hardware::usb::Usb *usb) { } } +void report_usb_data_session_event(android::hardware::usb::Usb *usb) { + std::vector<VendorUsbDataSessionEvent> 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<IStats> stats_client = getStatsService(); + if (!stats_client) { + ALOGE("Unable to get AIDL Stats service"); + return; + } + + for (auto &event : events) { + reportUsbDataSessionEvent(stats_client, event); + } +} + static void unregisterEpollEntry(Usb *usb, std::string name) { std::map<std::string, struct Usb::epollEntry> *map; int fd; @@ -1068,14 +1123,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; @@ -1095,6 +1152,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<PortStatus> *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; @@ -1167,7 +1255,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 faaa270..83bae88 100644 --- a/usb/usb/Usb.h +++ b/usb/usb/Usb.h @@ -19,6 +19,7 @@ #include <android-base/file.h> #include <aidl/android/hardware/usb/BnUsb.h> #include <aidl/android/hardware/usb/BnUsbCallback.h> +#include <chrono> #include <pixelusb/UsbOverheatEvent.h> #include <utils/Log.h> @@ -99,12 +100,20 @@ struct Usb : public BnUsb { // USB device state monitoring struct usbDeviceState { - std::string latestState; + // Usb device state raw strings read from sysfs + std::vector<std::string> states; + // Timestamps of when the usb device states were captured + std::vector<std::chrono::steady_clock::time_point> timestamps; int portResetCount; }; struct usbDeviceState mDeviceState; // Map host device path name to usbDeviceState std::map<std::string, struct usbDeviceState> 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; |