Merge from Chromium at DEPS revision 284076

This commit was generated by merge_to_master.py.

Change-Id: I9a279485b02fe7ceddcd32d992a714ff132e99ae
diff --git a/device/hid/hid.gyp b/device/hid/hid.gyp
index c12845a..2db80fd 100644
--- a/device/hid/hid.gyp
+++ b/device/hid/hid.gyp
@@ -16,6 +16,8 @@
       'sources': [
         'device_monitor_linux.cc',
         'device_monitor_linux.h',
+        'hid_collection_info.cc',
+        'hid_collection_info.h',
         'hid_connection.cc',
         'hid_connection.h',
         'hid_connection_linux.cc',
@@ -40,8 +42,6 @@
         'hid_service_win.h',
         'hid_usage_and_page.cc',
         'hid_usage_and_page.h',
-        'hid_utils_mac.cc',
-        'hid_utils_mac.h',
         'input_service_linux.cc',
         'input_service_linux.h',
       ],
diff --git a/device/hid/hid_collection_info.cc b/device/hid/hid_collection_info.cc
new file mode 100644
index 0000000..4c55d08
--- /dev/null
+++ b/device/hid/hid_collection_info.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_collection_info.h"
+
+namespace device {
+
+HidCollectionInfo::HidCollectionInfo()
+    : usage(HidUsageAndPage::kGenericDesktopUndefined,
+            HidUsageAndPage::kPageUndefined) {
+}
+
+HidCollectionInfo::~HidCollectionInfo() {
+}
+
+}  // namespace device
diff --git a/device/hid/hid_collection_info.h b/device/hid/hid_collection_info.h
new file mode 100644
index 0000000..3b35e5e
--- /dev/null
+++ b/device/hid/hid_collection_info.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_COLLECTION_INFO_H_
+#define DEVICE_HID_HID_COLLECTION_INFO_H_
+
+#include <set>
+
+#include "device/hid/hid_usage_and_page.h"
+
+namespace device {
+
+struct HidCollectionInfo {
+  HidCollectionInfo();
+  ~HidCollectionInfo();
+
+  // Collection's usage ID.
+  HidUsageAndPage usage;
+
+  // HID report IDs which belong
+  // to this collection or to its
+  // embedded collections.
+  std::set<int> report_ids;
+};
+
+}  // namespace device"
+
+#endif  // DEVICE_HID_HID_COLLECTION_INFO_H_
diff --git a/device/hid/hid_connection.cc b/device/hid/hid_connection.cc
index c134bf2..76809d3 100644
--- a/device/hid/hid_connection.cc
+++ b/device/hid/hid_connection.cc
@@ -4,8 +4,183 @@
 
 #include "device/hid/hid_connection.h"
 
+#include <algorithm>
+
 namespace device {
 
+namespace {
+
+// Functor used to filter collections by report ID.
+struct CollectionHasReportId {
+  explicit CollectionHasReportId(const uint8_t report_id)
+      : report_id_(report_id) {}
+
+  bool operator()(const HidCollectionInfo& info) const {
+    if (info.report_ids.size() == 0 ||
+        report_id_ == HidConnection::kNullReportId)
+      return false;
+
+    if (report_id_ == HidConnection::kAnyReportId)
+      return true;
+
+    return std::find(info.report_ids.begin(),
+                     info.report_ids.end(),
+                     report_id_) != info.report_ids.end();
+  }
+
+ private:
+  const uint8_t report_id_;
+};
+
+// Functor returning true if collection has a protected usage.
+struct CollectionIsProtected {
+  bool operator()(const HidCollectionInfo& info) const {
+    return info.usage.IsProtected();
+  }
+};
+
+bool FindCollectionByReportId(const HidDeviceInfo& device_info,
+                              const uint8_t report_id,
+                              HidCollectionInfo* collection_info) {
+  std::vector<HidCollectionInfo>::const_iterator collection_iter =
+      std::find_if(device_info.collections.begin(),
+                   device_info.collections.end(),
+                   CollectionHasReportId(report_id));
+  if (collection_iter != device_info.collections.end()) {
+    if (collection_info) {
+      *collection_info = *collection_iter;
+    }
+    return true;
+  }
+
+  return false;
+}
+
+bool HasReportId(const HidDeviceInfo& device_info) {
+  return FindCollectionByReportId(
+      device_info, HidConnection::kAnyReportId, NULL);
+}
+
+bool HasProtectedCollection(const HidDeviceInfo& device_info) {
+  return std::find_if(device_info.collections.begin(),
+                      device_info.collections.end(),
+                      CollectionIsProtected()) != device_info.collections.end();
+}
+
+}  // namespace
+
+HidConnection::HidConnection(const HidDeviceInfo& device_info)
+    : device_info_(device_info) {
+  has_protected_collection_ = HasProtectedCollection(device_info);
+  has_report_id_ = HasReportId(device_info);
+}
+
+HidConnection::~HidConnection() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void HidConnection::Read(scoped_refptr<net::IOBufferWithSize> buffer,
+                         const IOCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (device_info_.max_input_report_size == 0) {
+    // The device does not support input reports.
+    callback.Run(false, 0);
+    return;
+  }
+  int expected_buffer_size = device_info_.max_input_report_size;
+  if (!has_report_id()) {
+    expected_buffer_size--;
+  }
+  if (buffer->size() < expected_buffer_size) {
+    // Receive buffer is too small.
+    callback.Run(false, 0);
+    return;
+  }
+
+  PlatformRead(buffer, callback);
+}
+
+void HidConnection::Write(uint8_t report_id,
+                          scoped_refptr<net::IOBufferWithSize> buffer,
+                          const IOCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (device_info_.max_output_report_size == 0) {
+    // The device does not support output reports.
+    callback.Run(false, 0);
+    return;
+  }
+  if (IsReportIdProtected(report_id)) {
+    callback.Run(false, 0);
+    return;
+  }
+
+  PlatformWrite(report_id, buffer, callback);
+}
+
+void HidConnection::GetFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (device_info_.max_feature_report_size == 0) {
+    // The device does not support feature reports.
+    callback.Run(false, 0);
+    return;
+  }
+  if (IsReportIdProtected(report_id)) {
+    callback.Run(false, 0);
+    return;
+  }
+  int expected_buffer_size = device_info_.max_feature_report_size;
+  if (!has_report_id()) {
+    expected_buffer_size--;
+  }
+  if (buffer->size() < expected_buffer_size) {
+    // Receive buffer is too small.
+    callback.Run(false, 0);
+    return;
+  }
+
+  PlatformGetFeatureReport(report_id, buffer, callback);
+}
+
+void HidConnection::SendFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (device_info_.max_feature_report_size == 0) {
+    // The device does not support feature reports.
+    callback.Run(false, 0);
+    return;
+  }
+  if (IsReportIdProtected(report_id)) {
+    callback.Run(false, 0);
+    return;
+  }
+
+  PlatformSendFeatureReport(report_id, buffer, callback);
+}
+
+bool HidConnection::CompleteRead(scoped_refptr<net::IOBufferWithSize> buffer,
+                                 const IOCallback& callback) {
+  if (buffer->size() == 0 || IsReportIdProtected(buffer->data()[0])) {
+    return false;
+  }
+
+  callback.Run(true, buffer->size());
+  return true;
+}
+
+bool HidConnection::IsReportIdProtected(const uint8_t report_id) {
+  HidCollectionInfo collection_info;
+  if (FindCollectionByReportId(device_info_, report_id, &collection_info)) {
+    return collection_info.usage.IsProtected();
+  }
+
+  return has_protected_collection();
+}
+
 PendingHidReport::PendingHidReport() {}
 
 PendingHidReport::~PendingHidReport() {}
@@ -14,9 +189,4 @@
 
 PendingHidRead::~PendingHidRead() {}
 
-HidConnection::HidConnection(const HidDeviceInfo& device_info)
-    : device_info_(device_info) {}
-
-HidConnection::~HidConnection() {}
-
 }  // namespace device
diff --git a/device/hid/hid_connection.h b/device/hid/hid_connection.h
index 5c08fbc..963b89f 100644
--- a/device/hid/hid_connection.h
+++ b/device/hid/hid_connection.h
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
 #include "device/hid/hid_device_info.h"
 #include "net/base/io_buffer.h"
 
@@ -16,31 +17,65 @@
 
 class HidConnection : public base::RefCountedThreadSafe<HidConnection> {
  public:
+  enum SpecialReportIds {
+    kNullReportId = 0x00,
+    kAnyReportId = 0xFF,
+  };
+
   typedef base::Callback<void(bool success, size_t size)> IOCallback;
 
-  virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
-                    const IOCallback& callback) = 0;
-  virtual void Write(uint8_t report_id,
-                     scoped_refptr<net::IOBufferWithSize> buffer,
-                     const IOCallback& callback) = 0;
-  virtual void GetFeatureReport(uint8_t report_id,
-                                scoped_refptr<net::IOBufferWithSize> buffer,
-                                const IOCallback& callback) = 0;
-  virtual void SendFeatureReport(uint8_t report_id,
-                                 scoped_refptr<net::IOBufferWithSize> buffer,
-                                 const IOCallback& callback) = 0;
-
   const HidDeviceInfo& device_info() const { return device_info_; }
+  bool has_protected_collection() const { return has_protected_collection_; }
+  bool has_report_id() const { return has_report_id_; }
+  const base::ThreadChecker& thread_checker() const { return thread_checker_; }
+
+  void Read(scoped_refptr<net::IOBufferWithSize> buffer,
+            const IOCallback& callback);
+  void Write(uint8_t report_id,
+             scoped_refptr<net::IOBufferWithSize> buffer,
+             const IOCallback& callback);
+  void GetFeatureReport(uint8_t report_id,
+                        scoped_refptr<net::IOBufferWithSize> buffer,
+                        const IOCallback& callback);
+  void SendFeatureReport(uint8_t report_id,
+                         scoped_refptr<net::IOBufferWithSize> buffer,
+                         const IOCallback& callback);
 
  protected:
   friend class base::RefCountedThreadSafe<HidConnection>;
-  friend struct HidDeviceInfo;
 
   explicit HidConnection(const HidDeviceInfo& device_info);
   virtual ~HidConnection();
 
+  virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
+                            const IOCallback& callback) = 0;
+  virtual void PlatformWrite(uint8_t report_id,
+                             scoped_refptr<net::IOBufferWithSize> buffer,
+                             const IOCallback& callback) = 0;
+  virtual void PlatformGetFeatureReport(
+      uint8_t report_id,
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const IOCallback& callback) = 0;
+  virtual void PlatformSendFeatureReport(
+      uint8_t report_id,
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const IOCallback& callback) = 0;
+
+  // PlatformRead implementation must call this method on read
+  // success, rather than directly running the callback.
+  // In case incoming buffer is empty or protected, it is filtered
+  // and this method returns false. Otherwise it runs the callback
+  // and returns true.
+  bool CompleteRead(scoped_refptr<net::IOBufferWithSize> buffer,
+                    const IOCallback& callback);
+
  private:
+  bool IsReportIdProtected(const uint8_t report_id);
+
   const HidDeviceInfo device_info_;
+  bool has_report_id_;
+  bool has_protected_collection_;
+  base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(HidConnection);
 };
diff --git a/device/hid/hid_connection_linux.cc b/device/hid/hid_connection_linux.cc
index 425d88f..0220c2e 100644
--- a/device/hid/hid_connection_linux.cc
+++ b/device/hid/hid_connection_linux.cc
@@ -46,8 +46,6 @@
 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
                                        std::string dev_node)
     : HidConnection(device_info) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
   int flags = base::File::FLAG_OPEN |
               base::File::FLAG_READ |
               base::File::FLAG_WRITE;
@@ -72,7 +70,7 @@
 
   if (fcntl(device_file.GetPlatformFile(), F_SETFL,
             fcntl(device_file.GetPlatformFile(), F_GETFL) | O_NONBLOCK)) {
-    PLOG(ERROR) << "Failed to set non-blocking flag to device file.";
+    PLOG(ERROR) << "Failed to set non-blocking flag to device file";
     return;
   }
   device_file_ = device_file.Pass();
@@ -88,48 +86,13 @@
 }
 
 HidConnectionLinux::~HidConnectionLinux() {
-  DCHECK(thread_checker_.CalledOnValidThread());
   Disconnect();
+  Flush();
 }
 
-void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(fd, device_file_.GetPlatformFile());
-
-  uint8 buffer[1024] = {0};
-  int bytes_read =
-      HANDLE_EINTR(read(device_file_.GetPlatformFile(), buffer, 1024));
-  if (bytes_read < 0) {
-    if (errno == EAGAIN) {
-      return;
-    }
-    Disconnect();
-    return;
-  }
-
-  PendingHidReport report;
-  report.buffer = new net::IOBufferWithSize(bytes_read);
-  memcpy(report.buffer->data(), buffer, bytes_read);
-  pending_reports_.push(report);
-  ProcessReadQueue();
-}
-
-void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {}
-
-void HidConnectionLinux::Disconnect() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  device_file_watcher_.StopWatchingFileDescriptor();
-  device_file_.Close();
-  while (!pending_reads_.empty()) {
-    PendingHidRead pending_read = pending_reads_.front();
-    pending_reads_.pop();
-    pending_read.callback.Run(false, 0);
-  }
-}
-
-void HidConnectionLinux::Read(scoped_refptr<net::IOBufferWithSize> buffer,
-                              const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+void HidConnectionLinux::PlatformRead(
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
   PendingHidRead pending_read;
   pending_read.buffer = buffer;
   pending_read.callback = callback;
@@ -137,16 +100,17 @@
   ProcessReadQueue();
 }
 
-void HidConnectionLinux::Write(uint8_t report_id,
-                               scoped_refptr<net::IOBufferWithSize> buffer,
-                               const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+void HidConnectionLinux::PlatformWrite(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
   // If report ID is non-zero, insert it into a new copy of the buffer.
   if (report_id != 0)
     buffer = CopyBufferWithReportId(buffer, report_id);
   int bytes_written = HANDLE_EINTR(
       write(device_file_.GetPlatformFile(), buffer->data(), buffer->size()));
   if (bytes_written < 0) {
+    VPLOG(1) << "Write failed";
     Disconnect();
     callback.Run(false, 0);
   } else {
@@ -154,12 +118,10 @@
   }
 }
 
-void HidConnectionLinux::GetFeatureReport(
+void HidConnectionLinux::PlatformGetFeatureReport(
     uint8_t report_id,
     scoped_refptr<net::IOBufferWithSize> buffer,
     const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
   if (buffer->size() == 0) {
     callback.Run(false, 0);
     return;
@@ -170,39 +132,97 @@
   int result = ioctl(device_file_.GetPlatformFile(),
                      HIDIOCGFEATURE(buffer->size()),
                      buffer->data());
-  if (result < 0)
+  if (result < 0) {
+    VPLOG(1) << "Failed to get feature report";
     callback.Run(false, 0);
-  else
+  } else {
     callback.Run(true, result);
+  }
 }
 
-void HidConnectionLinux::SendFeatureReport(
+void HidConnectionLinux::PlatformSendFeatureReport(
     uint8_t report_id,
     scoped_refptr<net::IOBufferWithSize> buffer,
     const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   if (report_id != 0)
     buffer = CopyBufferWithReportId(buffer, report_id);
   int result = ioctl(device_file_.GetPlatformFile(),
                      HIDIOCSFEATURE(buffer->size()),
                      buffer->data());
-  if (result < 0)
+  if (result < 0) {
+    VPLOG(1) << "Failed to send feature report";
     callback.Run(false, 0);
-  else
+  } else {
     callback.Run(true, result);
+  }
+}
+
+void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
+  DCHECK(thread_checker().CalledOnValidThread());
+  DCHECK_EQ(fd, device_file_.GetPlatformFile());
+
+  uint8 raw_buffer[1024] = {0};
+  int bytes_read =
+      HANDLE_EINTR(read(device_file_.GetPlatformFile(), raw_buffer, 1024));
+  if (bytes_read < 0) {
+    if (errno == EAGAIN) {
+      return;
+    }
+    VPLOG(1) << "Read failed";
+    Disconnect();
+    return;
+  }
+
+  scoped_refptr<net::IOBufferWithSize> buffer =
+      new net::IOBufferWithSize(bytes_read);
+  memcpy(buffer->data(), raw_buffer, bytes_read);
+
+  ProcessInputReport(buffer);
+}
+
+void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {
+}
+
+void HidConnectionLinux::Disconnect() {
+  DCHECK(thread_checker().CalledOnValidThread());
+  device_file_watcher_.StopWatchingFileDescriptor();
+  device_file_.Close();
+
+  Flush();
+}
+
+void HidConnectionLinux::Flush() {
+  while (!pending_reads_.empty()) {
+    pending_reads_.front().callback.Run(false, 0);
+    pending_reads_.pop();
+  }
+}
+
+void HidConnectionLinux::ProcessInputReport(
+    scoped_refptr<net::IOBufferWithSize> buffer) {
+  DCHECK(thread_checker().CalledOnValidThread());
+  PendingHidReport report;
+  report.buffer = buffer;
+  pending_reports_.push(report);
+  ProcessReadQueue();
 }
 
 void HidConnectionLinux::ProcessReadQueue() {
+  DCHECK(thread_checker().CalledOnValidThread());
   while (pending_reads_.size() && pending_reports_.size()) {
     PendingHidRead read = pending_reads_.front();
-    pending_reads_.pop();
     PendingHidReport report = pending_reports_.front();
-    if (report.buffer->size() > read.buffer->size()) {
-      read.callback.Run(false, report.buffer->size());
+
+    if (read.buffer->size() < report.buffer->size()) {
+      read.callback.Run(false, 0);
+      pending_reads_.pop();
     } else {
       memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
       pending_reports_.pop();
-      read.callback.Run(true, report.buffer->size());
+
+      if (CompleteRead(report.buffer, read.callback)) {
+        pending_reads_.pop();
+      }
     }
   }
 }
diff --git a/device/hid/hid_connection_linux.h b/device/hid/hid_connection_linux.h
index 108ad88..1f5a7a8 100644
--- a/device/hid/hid_connection_linux.h
+++ b/device/hid/hid_connection_linux.h
@@ -8,10 +8,8 @@
 #include <queue>
 
 #include "base/files/file.h"
-#include "base/memory/ref_counted.h"
 #include "base/message_loop/message_pump_libevent.h"
 #include "device/hid/hid_connection.h"
-#include "device/hid/hid_device_info.h"
 
 namespace device {
 
@@ -20,19 +18,22 @@
  public:
   HidConnectionLinux(HidDeviceInfo device_info, std::string dev_node);
 
-  virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
-                    const IOCallback& callback) OVERRIDE;
-  virtual void Write(uint8_t report_id,
-                     scoped_refptr<net::IOBufferWithSize> buffer,
-                     const IOCallback& callback) OVERRIDE;
-  virtual void GetFeatureReport(uint8_t report_id,
-                                scoped_refptr<net::IOBufferWithSize> buffer,
-                                const IOCallback& callback) OVERRIDE;
-  virtual void SendFeatureReport(uint8_t report_id,
-                                 scoped_refptr<net::IOBufferWithSize> buffer,
-                                 const IOCallback& callback) OVERRIDE;
+  // HidConnection implementation.
+  virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
+                            const IOCallback& callback) OVERRIDE;
+  virtual void PlatformWrite(uint8_t report_id,
+                             scoped_refptr<net::IOBufferWithSize> buffer,
+                             const IOCallback& callback) OVERRIDE;
+  virtual void PlatformGetFeatureReport(
+      uint8_t report_id,
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const IOCallback& callback) OVERRIDE;
+  virtual void PlatformSendFeatureReport(
+      uint8_t report_id,
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const IOCallback& callback) OVERRIDE;
 
-  // Implements base::MessagePumpLibevent::Watcher
+  // base::MessagePumpLibevent::Watcher implementation.
   virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
   virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
 
@@ -40,17 +41,18 @@
   friend class base::RefCountedThreadSafe<HidConnectionLinux>;
   virtual ~HidConnectionLinux();
 
-  void ProcessReadQueue();
   void Disconnect();
 
+  void Flush();
+  void ProcessInputReport(scoped_refptr<net::IOBufferWithSize> buffer);
+  void ProcessReadQueue();
+
   base::File device_file_;
   base::MessagePumpLibevent::FileDescriptorWatcher device_file_watcher_;
 
   std::queue<PendingHidReport> pending_reports_;
   std::queue<PendingHidRead> pending_reads_;
 
-  base::ThreadChecker thread_checker_;
-
   DISALLOW_COPY_AND_ASSIGN(HidConnectionLinux);
 };
 
diff --git a/device/hid/hid_connection_mac.cc b/device/hid/hid_connection_mac.cc
index ce17df4..521f287 100644
--- a/device/hid/hid_connection_mac.cc
+++ b/device/hid/hid_connection_mac.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/mac/foundation_util.h"
 #include "base/message_loop/message_loop.h"
-#include "base/threading/thread_restrictions.h"
 #include "device/hid/hid_connection_mac.h"
 
 namespace device {
@@ -15,85 +14,70 @@
 HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info)
     : HidConnection(device_info),
       device_(device_info.device_id, base::scoped_policy::RETAIN) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
   message_loop_ = base::MessageLoopProxy::current();
 
   DCHECK(device_.get());
-  inbound_buffer_.reset((uint8_t*)malloc(device_info.input_report_size));
+  inbound_buffer_.reset((uint8_t*)malloc(device_info.max_input_report_size));
   IOHIDDeviceRegisterInputReportCallback(device_.get(),
                                          inbound_buffer_.get(),
-                                         device_info.input_report_size,
+                                         device_info.max_input_report_size,
                                          &HidConnectionMac::InputReportCallback,
                                          this);
   IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone);
 }
 
 HidConnectionMac::~HidConnectionMac() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  while (!pending_reads_.empty()) {
-    pending_reads_.front().callback.Run(false, 0);
-    pending_reads_.pop();
-  }
-
   IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
+  Flush();
 }
 
-void HidConnectionMac::Read(scoped_refptr<net::IOBufferWithSize> buffer,
-                            const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+void HidConnectionMac::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
+                                    const IOCallback& callback) {
   if (!device_) {
     callback.Run(false, 0);
     return;
   }
-  PendingHidRead read;
-  read.buffer = buffer;
-  read.callback = callback;
-  pending_reads_.push(read);
+
+  PendingHidRead pending_read;
+  pending_read.buffer = buffer;
+  pending_read.callback = callback;
+  pending_reads_.push(pending_read);
   ProcessReadQueue();
 }
 
-void HidConnectionMac::Write(uint8_t report_id,
-                             scoped_refptr<net::IOBufferWithSize> buffer,
-                             const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback);
-}
-
-void HidConnectionMac::GetFeatureReport(
+void HidConnectionMac::PlatformWrite(
     uint8_t report_id,
     scoped_refptr<net::IOBufferWithSize> buffer,
     const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (device_info().feature_report_size == 0) {
-    callback.Run(false, 0);
-    return;
-  }
+  WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback);
+}
 
-  if (buffer->size() < device_info().feature_report_size) {
+void HidConnectionMac::PlatformGetFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
+  if (!device_) {
     callback.Run(false, 0);
     return;
   }
 
   uint8_t* feature_report_buffer = reinterpret_cast<uint8_t*>(buffer->data());
-  CFIndex feature_report_size = device_info().feature_report_size;
+  CFIndex max_feature_report_size = device_info().max_feature_report_size;
   IOReturn result = IOHIDDeviceGetReport(device_,
                                          kIOHIDReportTypeFeature,
                                          report_id,
                                          feature_report_buffer,
-                                         &feature_report_size);
+                                         &max_feature_report_size);
   if (result == kIOReturnSuccess)
-    callback.Run(true, feature_report_size);
+    callback.Run(true, max_feature_report_size);
   else
     callback.Run(false, 0);
 }
 
-void HidConnectionMac::SendFeatureReport(
+void HidConnectionMac::PlatformSendFeatureReport(
     uint8_t report_id,
     scoped_refptr<net::IOBufferWithSize> buffer,
     const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback);
 }
 
@@ -112,45 +96,18 @@
 
   connection->message_loop_->PostTask(
       FROM_HERE,
-      base::Bind(
-          &HidConnectionMac::ProcessInputReport, connection, type, buffer));
-}
-
-void HidConnectionMac::ProcessReadQueue() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  while (pending_reads_.size() && pending_reports_.size()) {
-    PendingHidRead read = pending_reads_.front();
-    pending_reads_.pop();
-    PendingHidReport report = pending_reports_.front();
-    if (read.buffer->size() < report.buffer->size()) {
-      read.callback.Run(false, report.buffer->size());
-    } else {
-      memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
-      pending_reports_.pop();
-      read.callback.Run(true, report.buffer->size());
-    }
-  }
-}
-
-void HidConnectionMac::ProcessInputReport(
-    IOHIDReportType type,
-    scoped_refptr<net::IOBufferWithSize> buffer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  PendingHidReport report;
-  report.buffer = buffer;
-  pending_reports_.push(report);
-  ProcessReadQueue();
+      base::Bind(&HidConnectionMac::ProcessInputReport, connection, buffer));
 }
 
 void HidConnectionMac::WriteReport(IOHIDReportType type,
                                    uint8_t report_id,
                                    scoped_refptr<net::IOBufferWithSize> buffer,
                                    const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   if (!device_) {
     callback.Run(false, 0);
     return;
   }
+
   scoped_refptr<net::IOBufferWithSize> output_buffer;
   if (report_id != 0) {
     output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
@@ -173,4 +130,40 @@
   }
 }
 
+void HidConnectionMac::Flush() {
+  while (!pending_reads_.empty()) {
+    pending_reads_.front().callback.Run(false, 0);
+    pending_reads_.pop();
+  }
+}
+
+void HidConnectionMac::ProcessInputReport(
+    scoped_refptr<net::IOBufferWithSize> buffer) {
+  DCHECK(thread_checker().CalledOnValidThread());
+  PendingHidReport report;
+  report.buffer = buffer;
+  pending_reports_.push(report);
+  ProcessReadQueue();
+}
+
+void HidConnectionMac::ProcessReadQueue() {
+  DCHECK(thread_checker().CalledOnValidThread());
+  while (pending_reads_.size() && pending_reports_.size()) {
+    PendingHidRead read = pending_reads_.front();
+    PendingHidReport report = pending_reports_.front();
+
+    if (read.buffer->size() < report.buffer->size()) {
+      read.callback.Run(false, 0);
+      pending_reads_.pop();
+    } else {
+      memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
+      pending_reports_.pop();
+
+      if (CompleteRead(report.buffer, read.callback)) {
+        pending_reads_.pop();
+      }
+    }
+  }
+}
+
 }  // namespace device
diff --git a/device/hid/hid_connection_mac.h b/device/hid/hid_connection_mac.h
index c307fb6..02dde04 100644
--- a/device/hid/hid_connection_mac.h
+++ b/device/hid/hid_connection_mac.h
@@ -11,11 +11,8 @@
 #include <queue>
 
 #include "base/mac/foundation_util.h"
-#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/threading/thread_checker.h"
 #include "device/hid/hid_connection.h"
-#include "device/hid/hid_device_info.h"
 
 namespace base {
 class MessageLoopProxy;
@@ -31,17 +28,20 @@
  public:
   explicit HidConnectionMac(HidDeviceInfo device_info);
 
-  virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
-                    const IOCallback& callback) OVERRIDE;
-  virtual void Write(uint8_t report_id,
-                     scoped_refptr<net::IOBufferWithSize> buffer,
-                     const IOCallback& callback) OVERRIDE;
-  virtual void GetFeatureReport(uint8_t report_id,
-                                scoped_refptr<net::IOBufferWithSize> buffer,
-                                const IOCallback& callback) OVERRIDE;
-  virtual void SendFeatureReport(uint8_t report_id,
-                                 scoped_refptr<net::IOBufferWithSize> buffer,
-                                 const IOCallback& callback) OVERRIDE;
+  // HidConnection implementation.
+  virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
+                            const IOCallback& callback) OVERRIDE;
+  virtual void PlatformWrite(uint8_t report_id,
+                             scoped_refptr<net::IOBufferWithSize> buffer,
+                             const IOCallback& callback) OVERRIDE;
+  virtual void PlatformGetFeatureReport(
+      uint8_t report_id,
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const IOCallback& callback) OVERRIDE;
+  virtual void PlatformSendFeatureReport(
+      uint8_t report_id,
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const IOCallback& callback) OVERRIDE;
 
  private:
   virtual ~HidConnectionMac();
@@ -53,29 +53,26 @@
                                   uint32_t report_id,
                                   uint8_t* report_bytes,
                                   CFIndex report_length);
-  void ProcessReadQueue();
-  void ProcessInputReport(IOHIDReportType type,
-                          scoped_refptr<net::IOBufferWithSize> buffer);
 
   void WriteReport(IOHIDReportType type,
                    uint8_t report_id,
                    scoped_refptr<net::IOBufferWithSize> buffer,
                    const IOCallback& callback);
 
-  scoped_refptr<base::MessageLoopProxy> message_loop_;
+  void Flush();
+  void ProcessInputReport(scoped_refptr<net::IOBufferWithSize> buffer);
+  void ProcessReadQueue();
 
   base::ScopedCFTypeRef<IOHIDDeviceRef> device_;
+  scoped_refptr<base::MessageLoopProxy> message_loop_;
   scoped_ptr<uint8_t, base::FreeDeleter> inbound_buffer_;
 
   std::queue<PendingHidReport> pending_reports_;
   std::queue<PendingHidRead> pending_reads_;
 
-  base::ThreadChecker thread_checker_;
-
   DISALLOW_COPY_AND_ASSIGN(HidConnectionMac);
 };
 
-
 }  // namespace device
 
 #endif  // DEVICE_HID_HID_CONNECTION_MAC_H_
diff --git a/device/hid/hid_connection_win.cc b/device/hid/hid_connection_win.cc
index 17448f0..767feac 100644
--- a/device/hid/hid_connection_win.cc
+++ b/device/hid/hid_connection_win.cc
@@ -8,12 +8,7 @@
 
 #include "base/files/file.h"
 #include "base/message_loop/message_loop.h"
-#include "base/stl_util.h"
-#include "base/threading/thread_restrictions.h"
 #include "base/win/object_watcher.h"
-#include "base/win/scoped_handle.h"
-#include "device/hid/hid_service.h"
-#include "device/hid/hid_service_win.h"
 
 #define INITGUID
 
@@ -103,7 +98,6 @@
 
 HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info)
     : HidConnection(device_info) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   file_.Set(CreateFileA(device_info.device_id.c_str(),
                         GENERIC_WRITE | GENERIC_READ,
                         FILE_SHARE_READ | FILE_SHARE_WRITE,
@@ -124,40 +118,15 @@
   }
 }
 
-bool HidConnectionWin::available() const {
-  return file_.IsValid();
-}
-
 HidConnectionWin::~HidConnectionWin() {
-  DCHECK(thread_checker_.CalledOnValidThread());
   CancelIo(file_.Get());
 }
 
-void HidConnectionWin::Read(scoped_refptr<net::IOBufferWithSize> buffer,
-                            const HidConnection::IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (device_info().input_report_size == 0) {
-    // The device does not support input reports.
-    callback.Run(false, 0);
-    return;
-  }
+void HidConnectionWin::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
+                                    const HidConnection::IOCallback& callback) {
+  scoped_refptr<net::IOBufferWithSize> receive_buffer =
+      new net::IOBufferWithSize(device_info().max_input_report_size);
 
-  // This fairly awkward logic is correct: If Windows does not expect a device
-  // to supply a report ID in its input reports, it requires the buffer to be
-  // 1 byte larger than what the device actually sends.
-  int receive_buffer_size = device_info().input_report_size;
-  int expected_buffer_size = receive_buffer_size;
-  if (!device_info().has_report_id)
-    expected_buffer_size -= 1;
-
-  if (buffer->size() < expected_buffer_size) {
-    callback.Run(false, 0);
-    return;
-  }
-
-  scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
-  if (receive_buffer_size != expected_buffer_size)
-    receive_buffer = new net::IOBufferWithSize(receive_buffer_size);
   scoped_refptr<PendingHidTransfer> transfer(
       new PendingHidTransfer(this, buffer, receive_buffer, callback));
   transfers_.insert(transfer);
@@ -169,16 +138,10 @@
                transfer->GetOverlapped()));
 }
 
-void HidConnectionWin::Write(uint8_t report_id,
-                             scoped_refptr<net::IOBufferWithSize> buffer,
-                             const HidConnection::IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (device_info().output_report_size == 0) {
-    // The device does not support output reports.
-    callback.Run(false, 0);
-    return;
-  }
-
+void HidConnectionWin::PlatformWrite(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const HidConnection::IOCallback& callback) {
   // The Windows API always wants either a report ID (if supported) or
   // zero at the front of every output report.
   scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
@@ -197,32 +160,15 @@
                 transfer->GetOverlapped()));
 }
 
-void HidConnectionWin::GetFeatureReport(
+void HidConnectionWin::PlatformGetFeatureReport(
     uint8_t report_id,
     scoped_refptr<net::IOBufferWithSize> buffer,
     const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (device_info().feature_report_size == 0) {
-    // The device does not support feature reports.
-    callback.Run(false, 0);
-    return;
-  }
-
-  int receive_buffer_size = device_info().feature_report_size;
-  int expected_buffer_size = receive_buffer_size;
-  if (!device_info().has_report_id)
-    expected_buffer_size -= 1;
-  if (buffer->size() < expected_buffer_size) {
-    callback.Run(false, 0);
-    return;
-  }
-
-  scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
-  if (receive_buffer_size != expected_buffer_size)
-    receive_buffer = new net::IOBufferWithSize(receive_buffer_size);
-
+  scoped_refptr<net::IOBufferWithSize> receive_buffer =
+      new net::IOBufferWithSize(device_info().max_feature_report_size);
   // The first byte of the destination buffer is the report ID being requested.
   receive_buffer->data()[0] = report_id;
+
   scoped_refptr<PendingHidTransfer> transfer(
       new PendingHidTransfer(this, buffer, receive_buffer, callback));
   transfers_.insert(transfer);
@@ -237,17 +183,10 @@
                       transfer->GetOverlapped()));
 }
 
-void HidConnectionWin::SendFeatureReport(
+void HidConnectionWin::PlatformSendFeatureReport(
     uint8_t report_id,
     scoped_refptr<net::IOBufferWithSize> buffer,
     const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (device_info().feature_report_size == 0) {
-    // The device does not support feature reports.
-    callback.Run(false, 0);
-    return;
-  }
-
   // The Windows API always wants either a report ID (if supported) or
   // zero at the front of every output report.
   scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
@@ -270,25 +209,42 @@
 
 void HidConnectionWin::OnTransferFinished(
     scoped_refptr<PendingHidTransfer> transfer) {
-  DWORD bytes_transferred;
   transfers_.erase(transfer);
+
+  DWORD bytes_transferred;
   if (GetOverlappedResult(
           file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
-    if (bytes_transferred == 0)
+    if (bytes_transferred == 0) {
       transfer->callback_.Run(true, 0);
-    // If this is an input transfer and the receive buffer is not the same as
-    // the target buffer, we need to copy the receive buffer into the target
-    // buffer, discarding the first byte. This is because the target buffer's
-    // owner is not expecting a report ID but Windows will always provide one.
-    if (transfer->receive_buffer_ &&
-        transfer->receive_buffer_ != transfer->target_buffer_) {
-      // Move one byte forward.
-      --bytes_transferred;
-      memcpy(transfer->target_buffer_->data(),
-             transfer->receive_buffer_->data() + 1,
-             bytes_transferred);
+      return;
     }
-    transfer->callback_.Run(true, bytes_transferred);
+
+    if (transfer->receive_buffer_) {
+      // If owner HID top-level collection does not have report ID, we need to
+      // copy the receive buffer into the target buffer, discarding the first
+      // byte. This is because the target buffer's owner is not expecting a
+      // report ID but Windows will always provide one.
+      if (!has_report_id()) {
+        uint8_t report_id = transfer->receive_buffer_->data()[0];
+        // Assert first byte is 0x00
+        if (report_id != HidConnection::kNullReportId) {
+          VLOG(1) << "Unexpected report ID in HID report:" << report_id;
+          transfer->callback_.Run(false, 0);
+        } else {
+          // Move one byte forward.
+          --bytes_transferred;
+          memcpy(transfer->target_buffer_->data(),
+                 transfer->receive_buffer_->data() + 1,
+                 bytes_transferred);
+        }
+      } else {
+        memcpy(transfer->target_buffer_->data(),
+               transfer->receive_buffer_->data(),
+               bytes_transferred);
+      }
+    }
+
+    CompleteRead(transfer->target_buffer_, transfer->callback_);
   } else {
     transfer->callback_.Run(false, 0);
   }
diff --git a/device/hid/hid_connection_win.h b/device/hid/hid_connection_win.h
index 263897a..6706044 100644
--- a/device/hid/hid_connection_win.h
+++ b/device/hid/hid_connection_win.h
@@ -9,12 +9,8 @@
 
 #include <set>
 
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/threading/thread_checker.h"
+#include "base/win/scoped_handle.h"
 #include "device/hid/hid_connection.h"
-#include "device/hid/hid_device_info.h"
 
 namespace device {
 
@@ -24,30 +20,35 @@
  public:
   explicit HidConnectionWin(const HidDeviceInfo& device_info);
 
-  bool available() const;
+  // HidConnection implementation.
+  virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
+                            const IOCallback& callback) OVERRIDE;
+  virtual void PlatformWrite(uint8_t report_id,
+                             scoped_refptr<net::IOBufferWithSize> buffer,
+                             const IOCallback& callback) OVERRIDE;
+  virtual void PlatformGetFeatureReport(
+      uint8_t report_id,
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const IOCallback& callback) OVERRIDE;
+  virtual void PlatformSendFeatureReport(
+      uint8_t report_id,
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const IOCallback& callback) OVERRIDE;
 
-  virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
-                    const IOCallback& callback) OVERRIDE;
-  virtual void Write(uint8_t report_id,
-                     scoped_refptr<net::IOBufferWithSize> buffer,
-                     const IOCallback& callback) OVERRIDE;
-  virtual void GetFeatureReport(uint8_t report_id,
-                                scoped_refptr<net::IOBufferWithSize> buffer,
-                                const IOCallback& callback) OVERRIDE;
-  virtual void SendFeatureReport(uint8_t report_id,
-                                 scoped_refptr<net::IOBufferWithSize> buffer,
-                                 const IOCallback& callback) OVERRIDE;
+ private:
+  friend class HidServiceWin;
+  friend struct PendingHidTransfer;
+
+  ~HidConnectionWin();
+
+  bool available() const { return file_.IsValid(); }
 
   void OnTransferFinished(scoped_refptr<PendingHidTransfer> transfer);
   void OnTransferCanceled(scoped_refptr<PendingHidTransfer> transfer);
 
- private:
-  ~HidConnectionWin();
-
   base::win::ScopedHandle file_;
-  std::set<scoped_refptr<PendingHidTransfer> > transfers_;
 
-  base::ThreadChecker thread_checker_;
+  std::set<scoped_refptr<PendingHidTransfer> > transfers_;
 
   DISALLOW_COPY_AND_ASSIGN(HidConnectionWin);
 };
diff --git a/device/hid/hid_device_info.cc b/device/hid/hid_device_info.cc
index 89be442..26891f8 100644
--- a/device/hid/hid_device_info.cc
+++ b/device/hid/hid_device_info.cc
@@ -12,13 +12,13 @@
 
 HidDeviceInfo::HidDeviceInfo()
     : device_id(kInvalidHidDeviceId),
-      bus_type(kHIDBusTypeUSB),
       vendor_id(0),
       product_id(0),
-      input_report_size(0),
-      output_report_size(0),
-      feature_report_size(0),
-      has_report_id(false) {}
+      bus_type(kHIDBusTypeUSB),
+      max_input_report_size(0),
+      max_output_report_size(0),
+      max_feature_report_size(0) {
+}
 
 HidDeviceInfo::~HidDeviceInfo() {}
 
diff --git a/device/hid/hid_device_info.h b/device/hid/hid_device_info.h
index 1b143c2..2ed51ac 100644
--- a/device/hid/hid_device_info.h
+++ b/device/hid/hid_device_info.h
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "build/build_config.h"
-#include "device/hid/hid_usage_and_page.h"
+#include "device/hid/hid_collection_info.h"
 
 #if defined(OS_MACOSX)
 #include <IOKit/hid/IOHIDDevice.h>
@@ -34,20 +34,19 @@
   HidDeviceInfo();
   ~HidDeviceInfo();
 
+  // Device identification.
   HidDeviceId device_id;
-
-  HidBusType bus_type;
   uint16_t vendor_id;
   uint16_t product_id;
-
-  int input_report_size;
-  int output_report_size;
-  int feature_report_size;
-  std::vector<HidUsageAndPage> usages;
-  bool has_report_id;
-
   std::string product_name;
   std::string serial_number;
+  HidBusType bus_type;
+
+  // Top-Level Collections information.
+  std::vector<HidCollectionInfo> collections;
+  int max_input_report_size;
+  int max_output_report_size;
+  int max_feature_report_size;
 };
 
 }  // namespace device
diff --git a/device/hid/hid_report_descriptor.cc b/device/hid/hid_report_descriptor.cc
index f2cb0f4..a06d284 100644
--- a/device/hid/hid_report_descriptor.cc
+++ b/device/hid/hid_report_descriptor.cc
@@ -8,6 +8,12 @@
 
 namespace device {
 
+namespace {
+
+const int kBitsPerByte = 8;
+
+}  // namespace
+
 HidReportDescriptor::HidReportDescriptor(const uint8_t* bytes, size_t size) {
   size_t header_index = 0;
   HidReportDescriptorItem* item = NULL;
@@ -20,42 +26,148 @@
 
 HidReportDescriptor::~HidReportDescriptor() {}
 
-void HidReportDescriptor::GetTopLevelCollections(
-    std::vector<HidUsageAndPage>* topLevelCollections) {
-  DCHECK(topLevelCollections);
-  STLClearObject(topLevelCollections);
+void HidReportDescriptor::GetDetails(
+    std::vector<HidCollectionInfo>* top_level_collections,
+    int* max_input_report_size,
+    int* max_output_report_size,
+    int* max_feature_report_size) {
+  DCHECK(top_level_collections);
+  DCHECK(max_input_report_size);
+  DCHECK(max_output_report_size);
+  DCHECK(max_feature_report_size);
+  STLClearObject(top_level_collections);
+
+  *max_input_report_size = 0;
+  *max_output_report_size = 0;
+  *max_feature_report_size = 0;
+
+  // Global tags data:
+  HidUsageAndPage::Page current_usage_page = HidUsageAndPage::kPageUndefined;
+  int current_report_count = 0;
+  int cached_report_count = 0;
+  int current_report_size = 0;
+  int cached_report_size = 0;
+  int current_input_report_size = 0;
+  int current_output_report_size = 0;
+  int current_feature_report_size = 0;
+
+  // Local tags data:
+  uint16_t current_usage = 0;
 
   for (std::vector<linked_ptr<HidReportDescriptorItem> >::const_iterator
            items_iter = items().begin();
        items_iter != items().end();
        ++items_iter) {
-    linked_ptr<HidReportDescriptorItem> item = *items_iter;
+    linked_ptr<HidReportDescriptorItem> current_item = *items_iter;
 
-    bool isTopLevelCollection =
-        item->tag() == HidReportDescriptorItem::kTagCollection &&
-        item->parent() == NULL;
+    switch (current_item->tag()) {
+      // Main tags:
+      case HidReportDescriptorItem::kTagCollection:
+        if (!current_item->parent()) {
+          // This is a top-level collection.
+          HidCollectionInfo collection;
+          collection.usage = HidUsageAndPage(current_usage, current_usage_page);
+          top_level_collections->push_back(collection);
+        }
+        break;
+      case HidReportDescriptorItem::kTagInput:
+        current_input_report_size += current_report_count * current_report_size;
+        break;
+      case HidReportDescriptorItem::kTagOutput:
+        current_output_report_size +=
+            current_report_count * current_report_size;
+        break;
+      case HidReportDescriptorItem::kTagFeature:
+        current_feature_report_size +=
+            current_report_count * current_report_size;
+        break;
 
-    if (isTopLevelCollection) {
-      uint16_t collection_usage = 0;
-      HidUsageAndPage::Page collection_usage_page =
-          HidUsageAndPage::kPageUndefined;
+      // Global tags:
+      case HidReportDescriptorItem::kTagUsagePage:
+        current_usage_page =
+            (HidUsageAndPage::Page)current_item->GetShortData();
+        break;
+      case HidReportDescriptorItem::kTagReportId:
+        if (top_level_collections->size() > 0) {
+          // Store report ID.
+          top_level_collections->back().report_ids.insert(
+              current_item->GetShortData());
 
-      HidReportDescriptorItem* usage = item->previous();
-      if (usage && usage->tag() == HidReportDescriptorItem::kTagUsage) {
-        collection_usage = usage->GetShortData();
-      }
+          // We need to increase report sizes by report ID field length.
+          if (current_input_report_size > 0)
+            current_input_report_size += kBitsPerByte;
+          if (current_output_report_size > 0)
+            current_output_report_size += kBitsPerByte;
+          if (current_feature_report_size > 0)
+            current_feature_report_size += kBitsPerByte;
 
-      HidReportDescriptorItem* usage_page = usage->previous();
-      if (usage_page &&
-          usage_page->tag() == HidReportDescriptorItem::kTagUsagePage) {
-        collection_usage_page =
-            (HidUsageAndPage::Page)usage_page->GetShortData();
-      }
+          // Update max report sizes.
+          *max_input_report_size =
+              std::max(*max_input_report_size, current_input_report_size);
+          *max_output_report_size =
+              std::max(*max_output_report_size, current_output_report_size);
+          *max_feature_report_size =
+              std::max(*max_feature_report_size, current_feature_report_size);
 
-      topLevelCollections->push_back(
-          HidUsageAndPage(collection_usage, collection_usage_page));
+          // Set report sizes to be 1-byte long (report ID field).
+          current_input_report_size = 0;
+          current_output_report_size = 0;
+          current_feature_report_size = 0;
+        }
+        break;
+      case HidReportDescriptorItem::kTagReportCount:
+        current_report_count = current_item->GetShortData();
+        break;
+      case HidReportDescriptorItem::kTagReportSize:
+        current_report_size = current_item->GetShortData();
+        break;
+      case HidReportDescriptorItem::kTagPush:
+        // Cache report count and size.
+        cached_report_count = current_report_count;
+        cached_report_size = current_report_size;
+        break;
+      case HidReportDescriptorItem::kTagPop:
+        // Restore cache.
+        current_report_count = cached_report_count;
+        current_report_size = cached_report_size;
+        // Reset cache.
+        cached_report_count = 0;
+        cached_report_size = 0;
+        break;
+
+      // Local tags:
+      case HidReportDescriptorItem::kTagUsage:
+        current_usage = current_item->GetShortData();
+        break;
+
+      default:
+        break;
     }
   }
+
+  if (top_level_collections->size() > 0 &&
+      top_level_collections->back().report_ids.size() > 0) {
+    // We need to increase report sizes by report ID field length.
+    if (current_input_report_size > 0)
+      current_input_report_size += kBitsPerByte;
+    if (current_output_report_size > 0)
+      current_output_report_size += kBitsPerByte;
+    if (current_feature_report_size > 0)
+      current_feature_report_size += kBitsPerByte;
+  }
+
+  // Update max report sizes
+  *max_input_report_size =
+      std::max(*max_input_report_size, current_input_report_size);
+  *max_output_report_size =
+      std::max(*max_output_report_size, current_output_report_size);
+  *max_feature_report_size =
+      std::max(*max_feature_report_size, current_feature_report_size);
+
+  // Convert bits into bytes
+  *max_input_report_size /= kBitsPerByte;
+  *max_output_report_size /= kBitsPerByte;
+  *max_feature_report_size /= kBitsPerByte;
 }
 
 }  // namespace device
diff --git a/device/hid/hid_report_descriptor.h b/device/hid/hid_report_descriptor.h
index fa67fa4..94d90ad 100644
--- a/device/hid/hid_report_descriptor.h
+++ b/device/hid/hid_report_descriptor.h
@@ -8,8 +8,8 @@
 #include <vector>
 
 #include "base/memory/linked_ptr.h"
+#include "device/hid/hid_collection_info.h"
 #include "device/hid/hid_report_descriptor_item.h"
-#include "device/hid/hid_usage_and_page.h"
 
 namespace device {
 
@@ -25,9 +25,12 @@
     return items_;
   }
 
-  // Returns HID usages of top-level collections present in the descriptor.
-  void GetTopLevelCollections(
-      std::vector<HidUsageAndPage>* topLevelCollections);
+  // Returns top-level collections present in the descriptor,
+  // together with max report sizes
+  void GetDetails(std::vector<HidCollectionInfo>* top_level_collections,
+                  int* max_input_report_size,
+                  int* max_output_report_size,
+                  int* max_feature_report_size);
 
  private:
   std::vector<linked_ptr<HidReportDescriptorItem> > items_;
diff --git a/device/hid/hid_report_descriptor_item.cc b/device/hid/hid_report_descriptor_item.cc
index bdd03ce..60ba764 100644
--- a/device/hid/hid_report_descriptor_item.cc
+++ b/device/hid/hid_report_descriptor_item.cc
@@ -90,17 +90,17 @@
     case 0x00:
       return kCollectionTypePhysical;
     case 0x01:
-      return kCollectionTypePhysical;
+      return kCollectionTypeApplication;
     case 0x02:
-      return kCollectionTypePhysical;
+      return kCollectionTypeLogical;
     case 0x03:
-      return kCollectionTypePhysical;
+      return kCollectionTypeReport;
     case 0x04:
-      return kCollectionTypePhysical;
+      return kCollectionTypeNamedArray;
     case 0x05:
-      return kCollectionTypePhysical;
+      return kCollectionTypeUsageSwitch;
     case 0x06:
-      return kCollectionTypePhysical;
+      return kCollectionTypeUsageModifier;
     default:
       break;
   }
diff --git a/device/hid/hid_report_descriptor_unittest.cc b/device/hid/hid_report_descriptor_unittest.cc
index 0d25889..619e682 100644
--- a/device/hid/hid_report_descriptor_unittest.cc
+++ b/device/hid/hid_report_descriptor_unittest.cc
@@ -14,392 +14,272 @@
 
 namespace {
 
-std::ostream& operator<<(std::ostream& os,
-                         const HidUsageAndPage::Page& usage_page) {
-  switch (usage_page) {
-    case HidUsageAndPage::kPageUndefined:
-      os << "Undefined";
-      break;
-    case HidUsageAndPage::kPageGenericDesktop:
-      os << "Generic Desktop";
-      break;
-    case HidUsageAndPage::kPageSimulation:
-      os << "Simulation";
-      break;
-    case HidUsageAndPage::kPageVirtualReality:
-      os << "Virtual Reality";
-      break;
-    case HidUsageAndPage::kPageSport:
-      os << "Sport";
-      break;
-    case HidUsageAndPage::kPageGame:
-      os << "Game";
-      break;
-    case HidUsageAndPage::kPageKeyboard:
-      os << "Keyboard";
-      break;
-    case HidUsageAndPage::kPageLed:
-      os << "Led";
-      break;
-    case HidUsageAndPage::kPageButton:
-      os << "Button";
-      break;
-    case HidUsageAndPage::kPageOrdinal:
-      os << "Ordinal";
-      break;
-    case HidUsageAndPage::kPageTelephony:
-      os << "Telephony";
-      break;
-    case HidUsageAndPage::kPageConsumer:
-      os << "Consumer";
-      break;
-    case HidUsageAndPage::kPageDigitizer:
-      os << "Digitizer";
-      break;
-    case HidUsageAndPage::kPagePidPage:
-      os << "Pid Page";
-      break;
-    case HidUsageAndPage::kPageUnicode:
-      os << "Unicode";
-      break;
-    case HidUsageAndPage::kPageAlphanumericDisplay:
-      os << "Alphanumeric Display";
-      break;
-    case HidUsageAndPage::kPageMedicalInstruments:
-      os << "Medical Instruments";
-      break;
-    case HidUsageAndPage::kPageMonitor0:
-      os << "Monitor 0";
-      break;
-    case HidUsageAndPage::kPageMonitor1:
-      os << "Monitor 1";
-      break;
-    case HidUsageAndPage::kPageMonitor2:
-      os << "Monitor 2";
-      break;
-    case HidUsageAndPage::kPageMonitor3:
-      os << "Monitor 3";
-      break;
-    case HidUsageAndPage::kPagePower0:
-      os << "Power 0";
-      break;
-    case HidUsageAndPage::kPagePower1:
-      os << "Power 1";
-      break;
-    case HidUsageAndPage::kPagePower2:
-      os << "Power 2";
-      break;
-    case HidUsageAndPage::kPagePower3:
-      os << "Power 3";
-      break;
-    case HidUsageAndPage::kPageBarCodeScanner:
-      os << "Bar Code Scanner";
-      break;
-    case HidUsageAndPage::kPageScale:
-      os << "Scale";
-      break;
-    case HidUsageAndPage::kPageMagneticStripeReader:
-      os << "Magnetic Stripe Reader";
-      break;
-    case HidUsageAndPage::kPageReservedPointOfSale:
-      os << "Reserved Point Of Sale";
-      break;
-    case HidUsageAndPage::kPageCameraControl:
-      os << "Camera Control";
-      break;
-    case HidUsageAndPage::kPageArcade:
-      os << "Arcade";
-      break;
-    case HidUsageAndPage::kPageVendor:
-      os << "Vendor";
-      break;
-    case HidUsageAndPage::kPageMediaCenter:
-      os << "Media Center";
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  return os;
-}
+// Digitizer descriptor from HID descriptor tool
+// http://www.usb.org/developers/hidpage/dt2_4.zip
+const uint8_t kDigitizer[] = {
+    0x05, 0x0d,        // Usage Page (Digitizer)
+    0x09, 0x01,        // Usage (0x1)
+    0xa1, 0x01,        // Collection (Application)
+    0x85, 0x01,        //  Report ID (0x1)
+    0x09, 0x21,        //  Usage (0x21)
+    0xa1, 0x00,        //  Collection (Physical)
+    0x05, 0x01,        //   Usage Page (Generic Desktop)
+    0x09, 0x30,        //   Usage (0x30)
+    0x09, 0x31,        //   Usage (0x31)
+    0x75, 0x10,        //   Report Size (16)
+    0x95, 0x02,        //   Report Count (2)
+    0x15, 0x00,        //   Logical Minimum (0)
+    0x26, 0xe0, 0x2e,  //   Logical Maximum (12000)
+    0x35, 0x00,        //   Physical Minimum (0)
+    0x45, 0x0c,        //   Physical Maximum (12)
+    0x65, 0x13,        //   Unit (19)
+    0x55, 0x00,        //   Unit Exponent (0)
+    0xa4,              //   Push
+    0x81, 0x02,        //   Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x05, 0x0d,        //   Usage Page (Digitizer)
+    0x09, 0x32,        //   Usage (0x32)
+    0x09, 0x44,        //   Usage (0x44)
+    0x09, 0x42,        //   Usage (0x42)
+    0x15, 0x00,        //   Logical Minimum (0)
+    0x25, 0x01,        //   Logical Maximum (1)
+    0x35, 0x00,        //   Physical Minimum (0)
+    0x45, 0x01,        //   Physical Maximum (1)
+    0x75, 0x01,        //   Report Size (1)
+    0x95, 0x03,        //   Report Count (3)
+    0x65, 0x00,        //   Unit (0)
+    0x81, 0x02,        //   Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x95, 0x01,        //   Report Count (1)
+    0x75, 0x05,        //   Report Size (5)
+    0x81, 0x03,        //   Input (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0xc0,              //  End Collection
+    0x85, 0x02,        //  Report ID (0x2)
+    0x09, 0x20,        //  Usage (0x20)
+    0xa1, 0x00,        //  Collection (Physical)
+    0xb4,              //   Pop
+    0xa4,              //   Push
+    0x09, 0x30,        //   Usage (0x30)
+    0x09, 0x31,        //   Usage (0x31)
+    0x81, 0x02,        //   Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x05, 0x0d,        //   Usage Page (Digitizer)
+    0x09, 0x32,        //   Usage (0x32)
+    0x15, 0x00,        //   Logical Minimum (0)
+    0x25, 0x01,        //   Logical Maximum (1)
+    0x35, 0x00,        //   Physical Minimum (0)
+    0x45, 0x01,        //   Physical Maximum (1)
+    0x65, 0x00,        //   Unit (0)
+    0x75, 0x01,        //   Report Size (1)
+    0x81, 0x02,        //   Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x05, 0x09,        //   Usage Page (Button)
+    0x19, 0x00,        //   Usage Minimum (0)
+    0x29, 0x10,        //   Usage Maximum (16)
+    0x25, 0x10,        //   Logical Maximum (16)
+    0x75, 0x05,        //   Report Size (5)
+    0x81, 0x40,        //   Input (Dat|Var|Rel|NoWrp|Lin|Prf|Null|BitF)
+    0x75, 0x02,        //   Report Size (2)
+    0x81, 0x01,        //   Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0xc0,              //  End Collection
+    0x85, 0x03,        //  Report ID (0x3)
+    0x05, 0x0d,        //  Usage Page (Digitizer)
+    0x09, 0x20,        //  Usage (0x20)
+    0xa1, 0x00,        //  Collection (Physical)
+    0xb4,              //   Pop
+    0x09, 0x30,        //   Usage (0x30)
+    0x09, 0x31,        //   Usage (0x31)
+    0x81, 0x02,        //   Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x05, 0x0d,        //   Usage Page (Digitizer)
+    0x09, 0x32,        //   Usage (0x32)
+    0x09, 0x44,        //   Usage (0x44)
+    0x75, 0x01,        //   Report Size (1)
+    0x15, 0x00,        //   Logical Minimum (0)
+    0x25, 0x01,        //   Logical Maximum (1)
+    0x35, 0x00,        //   Physical Minimum (0)
+    0x45, 0x01,        //   Physical Maximum (1)
+    0x65, 0x00,        //   Unit (0)
+    0x81, 0x02,        //   Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x95, 0x06,        //   Report Count (6)
+    0x81, 0x03,        //   Input (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x09, 0x30,        //   Usage (0x30)
+    0x15, 0x00,        //   Logical Minimum (0)
+    0x25, 0x7f,        //   Logical Maximum (127)
+    0x35, 0x00,        //   Physical Minimum (0)
+    0x45, 0x2d,        //   Physical Maximum (45)
+    0x67, 0x11, 0xe1,  //   Unit (57617)
+    0x00, 0x00,        //   Default
+    0x55, 0x04,        //   Unit Exponent (4)
+    0x75, 0x08,        //   Report Size (8)
+    0x95, 0x01,        //   Report Count (1)
+    0x81, 0x12,        //   Input (Dat|Arr|Rel|NoWrp|NoLin|Prf|NoNull|BitF)
+    0xc0,              //  End Collection
+    0xc0               // End Collection
+};
 
-std::ostream& operator<<(std::ostream& os,
-                         const HidUsageAndPage& usage_and_page) {
-  os << "Usage Page: " << usage_and_page.usage_page << ", Usage: "
-     << "0x" << std::hex << std::uppercase << usage_and_page.usage;
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os,
-                         const HidReportDescriptorItem::Tag& tag) {
-  switch (tag) {
-    case HidReportDescriptorItem::kTagDefault:
-      os << "Default";
-      break;
-    case HidReportDescriptorItem::kTagInput:
-      os << "Input";
-      break;
-    case HidReportDescriptorItem::kTagOutput:
-      os << "Output";
-      break;
-    case HidReportDescriptorItem::kTagFeature:
-      os << "Feature";
-      break;
-    case HidReportDescriptorItem::kTagCollection:
-      os << "Collection";
-      break;
-    case HidReportDescriptorItem::kTagEndCollection:
-      os << "End Collection";
-      break;
-    case HidReportDescriptorItem::kTagUsagePage:
-      os << "Usage Page";
-      break;
-    case HidReportDescriptorItem::kTagLogicalMinimum:
-      os << "Logical Minimum";
-      break;
-    case HidReportDescriptorItem::kTagLogicalMaximum:
-      os << "Logical Maximum";
-      break;
-    case HidReportDescriptorItem::kTagPhysicalMinimum:
-      os << "Physical Minimum";
-      break;
-    case HidReportDescriptorItem::kTagPhysicalMaximum:
-      os << "Physical Maximum";
-      break;
-    case HidReportDescriptorItem::kTagUnitExponent:
-      os << "Unit Exponent";
-      break;
-    case HidReportDescriptorItem::kTagUnit:
-      os << "Unit";
-      break;
-    case HidReportDescriptorItem::kTagReportSize:
-      os << "Report Size";
-      break;
-    case HidReportDescriptorItem::kTagReportId:
-      os << "Report ID";
-      break;
-    case HidReportDescriptorItem::kTagReportCount:
-      os << "Report Count";
-      break;
-    case HidReportDescriptorItem::kTagPush:
-      os << "Push";
-      break;
-    case HidReportDescriptorItem::kTagPop:
-      os << "Pop";
-      break;
-    case HidReportDescriptorItem::kTagUsage:
-      os << "Usage";
-      break;
-    case HidReportDescriptorItem::kTagUsageMinimum:
-      os << "Usage Minimum";
-      break;
-    case HidReportDescriptorItem::kTagUsageMaximum:
-      os << "Usage Maximum";
-      break;
-    case HidReportDescriptorItem::kTagDesignatorIndex:
-      os << "Designator Index";
-      break;
-    case HidReportDescriptorItem::kTagDesignatorMinimum:
-      os << "Designator Minimum";
-      break;
-    case HidReportDescriptorItem::kTagDesignatorMaximum:
-      os << "Designator Maximum";
-      break;
-    case HidReportDescriptorItem::kTagStringIndex:
-      os << "String Index";
-      break;
-    case HidReportDescriptorItem::kTagStringMinimum:
-      os << "String Minimum";
-      break;
-    case HidReportDescriptorItem::kTagStringMaximum:
-      os << "String Maximum";
-      break;
-    case HidReportDescriptorItem::kTagDelimiter:
-      os << "Delimeter";
-      break;
-    case HidReportDescriptorItem::kTagLong:
-      os << "Long";
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os,
-                         const HidReportDescriptorItem::ReportInfo& data) {
-  if (data.data_or_constant)
-    os << "Con";
-  else
-    os << "Dat";
-  if (data.array_or_variable)
-    os << "|Arr";
-  else
-    os << "|Var";
-  if (data.absolute_or_relative)
-    os << "|Abs";
-  else
-    os << "|Rel";
-  if (data.wrap)
-    os << "|Wrp";
-  else
-    os << "|NoWrp";
-  if (data.linear)
-    os << "|NoLin";
-  else
-    os << "|Lin";
-  if (data.preferred)
-    os << "|NoPrf";
-  else
-    os << "|Prf";
-  if (data.null)
-    os << "|Null";
-  else
-    os << "|NoNull";
-  if (data.bit_field_or_buffer)
-    os << "|Buff";
-  else
-    os << "|BitF";
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os,
-                         const HidReportDescriptorItem::CollectionType& type) {
-  switch (type) {
-    case HidReportDescriptorItem::kCollectionTypePhysical:
-      os << "Physical";
-      break;
-    case HidReportDescriptorItem::kCollectionTypeApplication:
-      os << "Application";
-      break;
-    case HidReportDescriptorItem::kCollectionTypeLogical:
-      os << "Logical";
-      break;
-    case HidReportDescriptorItem::kCollectionTypeReport:
-      os << "Report";
-      break;
-    case HidReportDescriptorItem::kCollectionTypeNamedArray:
-      os << "Named Array";
-      break;
-    case HidReportDescriptorItem::kCollectionTypeUsageSwitch:
-      os << "Usage Switch";
-      break;
-    case HidReportDescriptorItem::kCollectionTypeUsageModifier:
-      os << "Usage Modifier";
-      break;
-    case HidReportDescriptorItem::kCollectionTypeReserved:
-      os << "Reserved";
-      break;
-    case HidReportDescriptorItem::kCollectionTypeVendor:
-      os << "Vendor";
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os,
-                         const HidReportDescriptorItem& item) {
-  HidReportDescriptorItem::Tag item_tag = item.tag();
-  uint32_t data = item.GetShortData();
-
-  std::ostringstream sstr;
-  sstr << item_tag;
-  sstr << " (";
-
-  long pos = sstr.tellp();
-  switch (item_tag) {
-    case HidReportDescriptorItem::kTagDefault:
-    case HidReportDescriptorItem::kTagEndCollection:
-    case HidReportDescriptorItem::kTagPush:
-    case HidReportDescriptorItem::kTagPop:
-    case HidReportDescriptorItem::kTagLong:
-      break;
-
-    case HidReportDescriptorItem::kTagCollection:
-      sstr << HidReportDescriptorItem::GetCollectionTypeFromValue(data);
-      break;
-
-    case HidReportDescriptorItem::kTagInput:
-    case HidReportDescriptorItem::kTagOutput:
-    case HidReportDescriptorItem::kTagFeature:
-      sstr << (HidReportDescriptorItem::ReportInfo&)data;
-      break;
-
-    case HidReportDescriptorItem::kTagUsagePage:
-      sstr << (HidUsageAndPage::Page)data;
-      break;
-
-    case HidReportDescriptorItem::kTagUsage:
-    case HidReportDescriptorItem::kTagReportId:
-      sstr << "0x" << std::hex << std::uppercase << data;
-      break;
-
-    default:
-      sstr << data;
-      break;
-  }
-  if (pos == sstr.tellp()) {
-    std::string str = sstr.str();
-    str.erase(str.end() - 2, str.end());
-    os << str;
-  } else {
-    os << sstr.str() << ")";
-  }
-
-  return os;
-}
-
-const char kIndentStep[] = " ";
-
-std::ostream& operator<<(std::ostream& os,
-                         const HidReportDescriptor& descriptor) {
-  for (std::vector<linked_ptr<HidReportDescriptorItem> >::const_iterator
-           items_iter = descriptor.items().begin();
-       items_iter != descriptor.items().end();
-       ++items_iter) {
-    linked_ptr<HidReportDescriptorItem> item = *items_iter;
-    size_t indentLevel = item->GetDepth();
-    for (size_t i = 0; i < indentLevel; i++)
-      os << kIndentStep;
-    os << *item.get() << std::endl;
-  }
-  return os;
-}
-
-// See 'E.6 Report Descriptor (Keyboard)'
-// in HID specifications (v1.11)
+// Keyboard descriptor from HID descriptor tool
+// http://www.usb.org/developers/hidpage/dt2_4.zip
 const uint8_t kKeyboard[] = {
-    0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29,
-    0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02,
-    0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05,
-    0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03,
-    0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05,
-    0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xC0};
+    0x05, 0x01,  // Usage Page (Generic Desktop)
+    0x09, 0x06,  // Usage (0x6)
+    0xa1, 0x01,  // Collection (Application)
+    0x05, 0x07,  //  Usage Page (Keyboard)
+    0x19, 0xe0,  //  Usage Minimum (224)
+    0x29, 0xe7,  //  Usage Maximum (231)
+    0x15, 0x00,  //  Logical Minimum (0)
+    0x25, 0x01,  //  Logical Maximum (1)
+    0x75, 0x01,  //  Report Size (1)
+    0x95, 0x08,  //  Report Count (8)
+    0x81, 0x02,  //  Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x95, 0x01,  //  Report Count (1)
+    0x75, 0x08,  //  Report Size (8)
+    0x81, 0x03,  //  Input (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x95, 0x05,  //  Report Count (5)
+    0x75, 0x01,  //  Report Size (1)
+    0x05, 0x08,  //  Usage Page (Led)
+    0x19, 0x01,  //  Usage Minimum (1)
+    0x29, 0x05,  //  Usage Maximum (5)
+    0x91, 0x02,  //  Output (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x95, 0x01,  //  Report Count (1)
+    0x75, 0x03,  //  Report Size (3)
+    0x91, 0x03,  //  Output (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x95, 0x06,  //  Report Count (6)
+    0x75, 0x08,  //  Report Size (8)
+    0x15, 0x00,  //  Logical Minimum (0)
+    0x25, 0x65,  //  Logical Maximum (101)
+    0x05, 0x07,  //  Usage Page (Keyboard)
+    0x19, 0x00,  //  Usage Minimum (0)
+    0x29, 0x65,  //  Usage Maximum (101)
+    0x81, 0x00,  //  Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0xc0         // End Collection
+};
 
-// See 'E.10 Report Descriptor (Mouse)'
-// in HID specifications (v1.11)
-const uint8_t kMouse[] = {0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1,
-                          0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00,
-                          0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95,
-                          0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30,
-                          0x09, 0x31, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95,
-                          0x02, 0x81, 0x06, 0xC0, 0xC0};
+// Monitor descriptor from HID descriptor tool
+// http://www.usb.org/developers/hidpage/dt2_4.zip
+const uint8_t kMonitor[] = {
+    0x05, 0x80,        // Usage Page (Monitor 0)
+    0x09, 0x01,        // Usage (0x1)
+    0xa1, 0x01,        // Collection (Application)
+    0x85, 0x01,        //  Report ID (0x1)
+    0x15, 0x00,        //  Logical Minimum (0)
+    0x26, 0xff, 0x00,  //  Logical Maximum (255)
+    0x75, 0x08,        //  Report Size (8)
+    0x95, 0x80,        //  Report Count (128)
+    0x09, 0x02,        //  Usage (0x2)
+    0xb2, 0x02, 0x01,  //  Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|Buff)
+    0x85, 0x02,        //  Report ID (0x2)
+    0x95, 0xf3,        //  Report Count (243)
+    0x09, 0x03,        //  Usage (0x3)
+    0xb2, 0x02, 0x01,  //  Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|Buff)
+    0x85, 0x03,        //  Report ID (0x3)
+    0x05, 0x82,        //  Usage Page (Monitor 2)
+    0x95, 0x01,        //  Report Count (1)
+    0x75, 0x10,        //  Report Size (16)
+    0x26, 0xc8, 0x00,  //  Logical Maximum (200)
+    0x09, 0x10,        //  Usage (0x10)
+    0xb1, 0x02,        //  Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x85, 0x04,        //  Report ID (0x4)
+    0x25, 0x64,        //  Logical Maximum (100)
+    0x09, 0x12,        //  Usage (0x12)
+    0xb1, 0x02,        //  Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x95, 0x06,        //  Report Count (6)
+    0x26, 0xff, 0x00,  //  Logical Maximum (255)
+    0x09, 0x16,        //  Usage (0x16)
+    0x09, 0x18,        //  Usage (0x18)
+    0x09, 0x1a,        //  Usage (0x1A)
+    0x09, 0x6c,        //  Usage (0x6C)
+    0x09, 0x6e,        //  Usage (0x6E)
+    0x09, 0x70,        //  Usage (0x70)
+    0xb1, 0x02,        //  Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x85, 0x05,        //  Report ID (0x5)
+    0x25, 0x7f,        //  Logical Maximum (127)
+    0x09, 0x20,        //  Usage (0x20)
+    0x09, 0x22,        //  Usage (0x22)
+    0x09, 0x30,        //  Usage (0x30)
+    0x09, 0x32,        //  Usage (0x32)
+    0x09, 0x42,        //  Usage (0x42)
+    0x09, 0x44,        //  Usage (0x44)
+    0xb1, 0x02,        //  Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0xc0               // End Collection
+};
 
+// Mouse descriptor from HID descriptor tool
+// http://www.usb.org/developers/hidpage/dt2_4.zip
+const uint8_t kMouse[] = {
+    0x05, 0x01,  // Usage Page (Generic Desktop)
+    0x09, 0x02,  // Usage (0x2)
+    0xa1, 0x01,  // Collection (Application)
+    0x09, 0x01,  //  Usage (0x1)
+    0xa1, 0x00,  //  Collection (Physical)
+    0x05, 0x09,  //   Usage Page (Button)
+    0x19, 0x01,  //   Usage Minimum (1)
+    0x29, 0x03,  //   Usage Maximum (3)
+    0x15, 0x00,  //   Logical Minimum (0)
+    0x25, 0x01,  //   Logical Maximum (1)
+    0x95, 0x03,  //   Report Count (3)
+    0x75, 0x01,  //   Report Size (1)
+    0x81, 0x02,  //   Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x95, 0x01,  //   Report Count (1)
+    0x75, 0x05,  //   Report Size (5)
+    0x81, 0x03,  //   Input (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x05, 0x01,  //   Usage Page (Generic Desktop)
+    0x09, 0x30,  //   Usage (0x30)
+    0x09, 0x31,  //   Usage (0x31)
+    0x15, 0x81,  //   Logical Minimum (129)
+    0x25, 0x7f,  //   Logical Maximum (127)
+    0x75, 0x08,  //   Report Size (8)
+    0x95, 0x02,  //   Report Count (2)
+    0x81, 0x06,  //   Input (Dat|Arr|Abs|NoWrp|Lin|Prf|NoNull|BitF)
+    0xc0,        //  End Collection
+    0xc0         // End Collection
+};
+
+// Logitech Unifying receiver descriptor
 const uint8_t kLogitechUnifyingReceiver[] = {
-    0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x75, 0x08,
-    0x95, 0x06, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x09, 0x01, 0x81, 0x00,
-    0x09, 0x01, 0x91, 0x00, 0xC0, 0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1,
-    0x01, 0x85, 0x11, 0x75, 0x08, 0x95, 0x13, 0x15, 0x00, 0x26, 0xFF,
-    0x00, 0x09, 0x02, 0x81, 0x00, 0x09, 0x02, 0x91, 0x00, 0xC0, 0x06,
-    0x00, 0xFF, 0x09, 0x04, 0xA1, 0x01, 0x85, 0x20, 0x75, 0x08, 0x95,
-    0x0E, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x09, 0x41, 0x81, 0x00, 0x09,
-    0x41, 0x91, 0x00, 0x85, 0x21, 0x95, 0x1F, 0x15, 0x00, 0x26, 0xFF,
-    0x00, 0x09, 0x42, 0x81, 0x00, 0x09, 0x42, 0x91, 0x00, 0xC0};
+    0x06, 0x00, 0xFF,  // Usage Page (Vendor)
+    0x09, 0x01,        // Usage (0x1)
+    0xA1, 0x01,        // Collection (Application)
+    0x85, 0x10,        //  Report ID (0x10)
+    0x75, 0x08,        //  Report Size (8)
+    0x95, 0x06,        //  Report Count (6)
+    0x15, 0x00,        //  Logical Minimum (0)
+    0x26, 0xFF, 0x00,  //  Logical Maximum (255)
+    0x09, 0x01,        //  Usage (0x1)
+    0x81, 0x00,        //  Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x09, 0x01,        //  Usage (0x1)
+    0x91, 0x00,        //  Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0xC0,              // End Collection
+    0x06, 0x00, 0xFF,  // Usage Page (Vendor)
+    0x09, 0x02,        // Usage (0x2)
+    0xA1, 0x01,        // Collection (Application)
+    0x85, 0x11,        //  Report ID (0x11)
+    0x75, 0x08,        //  Report Size (8)
+    0x95, 0x13,        //  Report Count (19)
+    0x15, 0x00,        //  Logical Minimum (0)
+    0x26, 0xFF, 0x00,  //  Logical Maximum (255)
+    0x09, 0x02,        //  Usage (0x2)
+    0x81, 0x00,        //  Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x09, 0x02,        //  Usage (0x2)
+    0x91, 0x00,        //  Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0xC0,              // End Collection
+    0x06, 0x00, 0xFF,  // Usage Page (Vendor)
+    0x09, 0x04,        // Usage (0x4)
+    0xA1, 0x01,        // Collection (Application)
+    0x85, 0x20,        //  Report ID (0x20)
+    0x75, 0x08,        //  Report Size (8)
+    0x95, 0x0E,        //  Report Count (14)
+    0x15, 0x00,        //  Logical Minimum (0)
+    0x26, 0xFF, 0x00,  //  Logical Maximum (255)
+    0x09, 0x41,        //  Usage (0x41)
+    0x81, 0x00,        //  Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x09, 0x41,        //  Usage (0x41)
+    0x91, 0x00,        //  Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x85, 0x21,        //  Report ID (0x21)
+    0x95, 0x1F,        //  Report Count (31)
+    0x15, 0x00,        //  Logical Minimum (0)
+    0x26, 0xFF, 0x00,  //  Logical Maximum (255)
+    0x09, 0x42,        //  Usage (0x42)
+    0x81, 0x00,        //  Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0x09, 0x42,        //  Usage (0x42)
+    0x91, 0x00,        //  Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)
+    0xC0               // End Collection
+};
 
 }  // namespace
 
@@ -415,199 +295,135 @@
   }
 
  public:
-  void ParseDescriptor(const std::string& expected,
-                       const uint8_t* bytes,
-                       size_t size) {
+  void ValidateDetails(
+      const std::vector<HidCollectionInfo>& expected_collections,
+      const int expected_max_input_report_size,
+      const int expected_max_output_report_size,
+      const int expected_max_feature_report_size,
+      const uint8_t* bytes,
+      size_t size) {
     descriptor_ = new HidReportDescriptor(bytes, size);
 
-    std::stringstream actual;
-    actual << *descriptor_;
+    std::vector<HidCollectionInfo> actual_collections;
+    int actual_max_input_report_size;
+    int actual_max_output_report_size;
+    int actual_max_feature_report_size;
+    descriptor_->GetDetails(&actual_collections,
+                            &actual_max_input_report_size,
+                            &actual_max_output_report_size,
+                            &actual_max_feature_report_size);
 
-    std::cout << "HID report descriptor:" << std::endl;
-    std::cout << actual.str();
+    ASSERT_EQ(expected_collections.size(), actual_collections.size());
 
-    // TODO(jracle@logitech.com): refactor string comparison in favor of
-    // testing individual fields.
-    ASSERT_EQ(expected, actual.str());
-  }
+    std::vector<HidCollectionInfo>::const_iterator actual_collections_iter =
+        actual_collections.begin();
+    std::vector<HidCollectionInfo>::const_iterator expected_collections_iter =
+        expected_collections.begin();
 
-  void GetTopLevelCollections(const std::vector<HidUsageAndPage>& expected,
-                              const uint8_t* bytes,
-                              size_t size) {
-    descriptor_ = new HidReportDescriptor(bytes, size);
+    while (expected_collections_iter != expected_collections.end() &&
+           actual_collections_iter != actual_collections.end()) {
+      HidCollectionInfo expected_collection = *expected_collections_iter;
+      HidCollectionInfo actual_collection = *actual_collections_iter;
 
-    std::vector<HidUsageAndPage> actual;
-    descriptor_->GetTopLevelCollections(&actual);
+      ASSERT_EQ(expected_collection.usage.usage_page,
+                actual_collection.usage.usage_page);
+      ASSERT_EQ(expected_collection.usage.usage, actual_collection.usage.usage);
+      ASSERT_THAT(actual_collection.report_ids,
+                  ContainerEq(expected_collection.report_ids));
 
-    std::cout << "HID top-level collections:" << std::endl;
-    for (std::vector<HidUsageAndPage>::const_iterator iter = actual.begin();
-         iter != actual.end();
-         ++iter) {
-      std::cout << *iter << std::endl;
+      expected_collections_iter++;
+      actual_collections_iter++;
     }
 
-    ASSERT_THAT(actual, ContainerEq(expected));
+    ASSERT_EQ(expected_max_input_report_size, actual_max_input_report_size);
+    ASSERT_EQ(expected_max_output_report_size, actual_max_output_report_size);
+    ASSERT_EQ(expected_max_feature_report_size, actual_max_feature_report_size);
   }
 
  private:
   HidReportDescriptor* descriptor_;
 };
 
-TEST_F(HidReportDescriptorTest, ParseDescriptor_Keyboard) {
-  const char expected[] = {
-      "Usage Page (Generic Desktop)\n"
-      "Usage (0x6)\n"
-      "Collection (Physical)\n"
-      " Usage Page (Keyboard)\n"
-      " Usage Minimum (224)\n"
-      " Usage Maximum (231)\n"
-      " Logical Minimum (0)\n"
-      " Logical Maximum (1)\n"
-      " Report Size (1)\n"
-      " Report Count (8)\n"
-      " Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Report Count (1)\n"
-      " Report Size (8)\n"
-      " Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Report Count (5)\n"
-      " Report Size (1)\n"
-      " Usage Page (Led)\n"
-      " Usage Minimum (1)\n"
-      " Usage Maximum (5)\n"
-      " Output (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Report Count (1)\n"
-      " Report Size (3)\n"
-      " Output (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Report Count (6)\n"
-      " Report Size (8)\n"
-      " Logical Minimum (0)\n"
-      " Logical Maximum (101)\n"
-      " Usage Page (Keyboard)\n"
-      " Usage Minimum (0)\n"
-      " Usage Maximum (101)\n"
-      " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      "End Collection\n"};
-
-  ParseDescriptor(std::string(expected), kKeyboard, sizeof(kKeyboard));
+TEST_F(HidReportDescriptorTest, ValidateDetails_Digitizer) {
+  HidCollectionInfo digitizer;
+  digitizer.usage = HidUsageAndPage(0x01, HidUsageAndPage::kPageDigitizer);
+  digitizer.report_ids.insert(1);
+  digitizer.report_ids.insert(2);
+  digitizer.report_ids.insert(3);
+  HidCollectionInfo expected[] = {digitizer};
+  ValidateDetails(std::vector<HidCollectionInfo>(
+                      expected, expected + ARRAYSIZE_UNSAFE(expected)),
+                  7,
+                  0,
+                  0,
+                  kDigitizer,
+                  sizeof(kDigitizer));
 }
 
-TEST_F(HidReportDescriptorTest, TopLevelCollections_Keyboard) {
-  HidUsageAndPage expected[] = {
-      HidUsageAndPage(0x06, HidUsageAndPage::kPageGenericDesktop)};
-
-  GetTopLevelCollections(std::vector<HidUsageAndPage>(
-                             expected, expected + ARRAYSIZE_UNSAFE(expected)),
-                         kKeyboard,
-                         sizeof(kKeyboard));
+TEST_F(HidReportDescriptorTest, ValidateDetails_Keyboard) {
+  HidCollectionInfo keyboard;
+  keyboard.usage = HidUsageAndPage(0x06, HidUsageAndPage::kPageGenericDesktop);
+  HidCollectionInfo expected[] = {keyboard};
+  ValidateDetails(std::vector<HidCollectionInfo>(
+                      expected, expected + ARRAYSIZE_UNSAFE(expected)),
+                  8,
+                  1,
+                  0,
+                  kKeyboard,
+                  sizeof(kKeyboard));
 }
 
-TEST_F(HidReportDescriptorTest, ParseDescriptor_Mouse) {
-  const char expected[] = {
-      "Usage Page (Generic Desktop)\n"
-      "Usage (0x2)\n"
-      "Collection (Physical)\n"
-      " Usage (0x1)\n"
-      " Collection (Physical)\n"
-      "  Usage Page (Button)\n"
-      "  Usage Minimum (1)\n"
-      "  Usage Maximum (3)\n"
-      "  Logical Minimum (0)\n"
-      "  Logical Maximum (1)\n"
-      "  Report Count (3)\n"
-      "  Report Size (1)\n"
-      "  Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      "  Report Count (1)\n"
-      "  Report Size (5)\n"
-      "  Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      "  Usage Page (Generic Desktop)\n"
-      "  Usage (0x30)\n"
-      "  Usage (0x31)\n"
-      "  Logical Minimum (129)\n"
-      "  Logical Maximum (127)\n"
-      "  Report Size (8)\n"
-      "  Report Count (2)\n"
-      "  Input (Dat|Arr|Abs|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " End Collection\n"
-      "End Collection\n"};
-
-  ParseDescriptor(std::string(expected), kMouse, sizeof(kMouse));
+TEST_F(HidReportDescriptorTest, ValidateDetails_Monitor) {
+  HidCollectionInfo monitor;
+  monitor.usage = HidUsageAndPage(0x01, HidUsageAndPage::kPageMonitor0);
+  monitor.report_ids.insert(1);
+  monitor.report_ids.insert(2);
+  monitor.report_ids.insert(3);
+  monitor.report_ids.insert(4);
+  monitor.report_ids.insert(5);
+  HidCollectionInfo expected[] = {monitor};
+  ValidateDetails(std::vector<HidCollectionInfo>(
+                      expected, expected + ARRAYSIZE_UNSAFE(expected)),
+                  0,
+                  0,
+                  244,
+                  kMonitor,
+                  sizeof(kMonitor));
 }
 
-TEST_F(HidReportDescriptorTest, TopLevelCollections_Mouse) {
-  HidUsageAndPage expected[] = {
-      HidUsageAndPage(0x02, HidUsageAndPage::kPageGenericDesktop)};
-
-  GetTopLevelCollections(std::vector<HidUsageAndPage>(
-                             expected, expected + ARRAYSIZE_UNSAFE(expected)),
-                         kMouse,
-                         sizeof(kMouse));
+TEST_F(HidReportDescriptorTest, ValidateDetails_Mouse) {
+  HidCollectionInfo mouse;
+  mouse.usage = HidUsageAndPage(0x02, HidUsageAndPage::kPageGenericDesktop);
+  HidCollectionInfo expected[] = {mouse};
+  ValidateDetails(std::vector<HidCollectionInfo>(
+                      expected, expected + ARRAYSIZE_UNSAFE(expected)),
+                  3,
+                  0,
+                  0,
+                  kMouse,
+                  sizeof(kMouse));
 }
 
-TEST_F(HidReportDescriptorTest, ParseDescriptor_LogitechUnifyingReceiver) {
-  const char expected[] = {
-      "Usage Page (Vendor)\n"
-      "Usage (0x1)\n"
-      "Collection (Physical)\n"
-      " Report ID (0x10)\n"
-      " Report Size (8)\n"
-      " Report Count (6)\n"
-      " Logical Minimum (0)\n"
-      " Logical Maximum (255)\n"
-      " Usage (0x1)\n"
-      " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Usage (0x1)\n"
-      " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      "End Collection\n"
-      "Usage Page (Vendor)\n"
-      "Usage (0x2)\n"
-      "Collection (Physical)\n"
-      " Report ID (0x11)\n"
-      " Report Size (8)\n"
-      " Report Count (19)\n"
-      " Logical Minimum (0)\n"
-      " Logical Maximum (255)\n"
-      " Usage (0x2)\n"
-      " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Usage (0x2)\n"
-      " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      "End Collection\n"
-      "Usage Page (Vendor)\n"
-      "Usage (0x4)\n"
-      "Collection (Physical)\n"
-      " Report ID (0x20)\n"
-      " Report Size (8)\n"
-      " Report Count (14)\n"
-      " Logical Minimum (0)\n"
-      " Logical Maximum (255)\n"
-      " Usage (0x41)\n"
-      " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Usage (0x41)\n"
-      " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Report ID (0x21)\n"
-      " Report Count (31)\n"
-      " Logical Minimum (0)\n"
-      " Logical Maximum (255)\n"
-      " Usage (0x42)\n"
-      " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      " Usage (0x42)\n"
-      " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
-      "End Collection\n"};
+TEST_F(HidReportDescriptorTest, ValidateDetails_LogitechUnifyingReceiver) {
+  HidCollectionInfo hidpp_short;
+  hidpp_short.usage = HidUsageAndPage(0x01, HidUsageAndPage::kPageVendor);
+  hidpp_short.report_ids.insert(0x10);
+  HidCollectionInfo hidpp_long;
+  hidpp_long.usage = HidUsageAndPage(0x02, HidUsageAndPage::kPageVendor);
+  hidpp_long.report_ids.insert(0x11);
+  HidCollectionInfo hidpp_dj;
+  hidpp_dj.usage = HidUsageAndPage(0x04, HidUsageAndPage::kPageVendor);
+  hidpp_dj.report_ids.insert(0x20);
+  hidpp_dj.report_ids.insert(0x21);
 
-  ParseDescriptor(std::string(expected),
+  HidCollectionInfo expected[] = {hidpp_short, hidpp_long, hidpp_dj};
+  ValidateDetails(std::vector<HidCollectionInfo>(
+                      expected, expected + ARRAYSIZE_UNSAFE(expected)),
+                  32,
+                  32,
+                  0,
                   kLogitechUnifyingReceiver,
                   sizeof(kLogitechUnifyingReceiver));
 }
 
-TEST_F(HidReportDescriptorTest, TopLevelCollections_LogitechUnifyingReceiver) {
-  HidUsageAndPage expected[] = {
-      HidUsageAndPage(0x01, HidUsageAndPage::kPageVendor),
-      HidUsageAndPage(0x02, HidUsageAndPage::kPageVendor),
-      HidUsageAndPage(0x04, HidUsageAndPage::kPageVendor), };
-
-  GetTopLevelCollections(std::vector<HidUsageAndPage>(
-                             expected, expected + ARRAYSIZE_UNSAFE(expected)),
-                         kLogitechUnifyingReceiver,
-                         sizeof(kLogitechUnifyingReceiver));
-}
-
 }  // namespace device
diff --git a/device/hid/hid_service_linux.cc b/device/hid/hid_service_linux.cc
index 5257dcd..4f1d8c7 100644
--- a/device/hid/hid_service_linux.cc
+++ b/device/hid/hid_service_linux.cc
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "device/hid/hid_service_linux.h"
+
 #include <linux/hidraw.h>
 #include <sys/ioctl.h>
-
 #include <stdint.h>
 
 #include <string>
 
 #include "base/bind.h"
+#include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
-#include "base/platform_file.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
@@ -20,14 +21,12 @@
 #include "device/hid/hid_connection_linux.h"
 #include "device/hid/hid_device_info.h"
 #include "device/hid/hid_report_descriptor.h"
-#include "device/hid/hid_service_linux.h"
 #include "device/udev_linux/udev.h"
 
 namespace device {
 
 namespace {
 
-const char kHIDSubSystem[] = "hid";
 const char kHidrawSubsystem[] = "hidraw";
 const char kHIDID[] = "HID_ID";
 const char kHIDName[] = "HID_NAME";
@@ -53,11 +52,7 @@
           device_info.device_id);
 
   if (device) {
-    std::string dev_node;
-    if (!FindHidrawDevNode(device.get(), &dev_node)) {
-      LOG(ERROR) << "Cannot open HID device as hidraw device.";
-      return NULL;
-    }
+    std::string dev_node = udev_device_get_devnode(device.get());
     return new HidConnectionLinux(device_info, dev_node);
   }
 
@@ -77,7 +72,7 @@
   if (!device_path)
     return;
   const char* subsystem = udev_device_get_subsystem(device);
-  if (!subsystem || strcmp(subsystem, kHIDSubSystem) != 0)
+  if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0)
     return;
 
   HidDeviceInfo device_info;
@@ -86,7 +81,12 @@
   uint32_t int_property = 0;
   const char* str_property = NULL;
 
-  const char* hid_id = udev_device_get_property_value(device, kHIDID);
+  udev_device *parent = udev_device_get_parent(device);
+  if (!parent) {
+    return;
+  }
+
+  const char* hid_id = udev_device_get_property_value(parent, kHIDID);
   if (!hid_id)
     return;
 
@@ -104,21 +104,16 @@
     device_info.product_id = int_property;
   }
 
-  str_property = udev_device_get_property_value(device, kHIDUnique);
+  str_property = udev_device_get_property_value(parent, kHIDUnique);
   if (str_property != NULL)
     device_info.serial_number = str_property;
 
-  str_property = udev_device_get_property_value(device, kHIDName);
+  str_property = udev_device_get_property_value(parent, kHIDName);
   if (str_property != NULL)
     device_info.product_name = str_property;
 
-  std::string dev_node;
-  if (!FindHidrawDevNode(device, &dev_node)) {
-    LOG(ERROR) << "Cannot find device node for HID device.";
-    return;
-  }
-
-  int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
+  const std::string dev_node = udev_device_get_devnode(device);
+  const int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
 
   base::File device_file(base::FilePath(dev_node), flags);
   if (!device_file.IsValid()) {
@@ -130,7 +125,7 @@
   int desc_size = 0;
   int res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESCSIZE, &desc_size);
   if (res < 0) {
-    LOG(ERROR) << "HIDIOCGRDESCSIZE failed.";
+    PLOG(ERROR) << "Failed to get report descriptor size";
     device_file.Close();
     return;
   }
@@ -140,7 +135,7 @@
 
   res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESC, &rpt_desc);
   if (res < 0) {
-    LOG(ERROR) << "HIDIOCGRDESC failed.";
+    PLOG(ERROR) << "Failed to get report descriptor";
     device_file.Close();
     return;
   }
@@ -148,7 +143,10 @@
   device_file.Close();
 
   HidReportDescriptor report_descriptor(rpt_desc.value, rpt_desc.size);
-  report_descriptor.GetTopLevelCollections(&device_info.usages);
+  report_descriptor.GetDetails(&device_info.collections,
+                               &device_info.max_input_report_size,
+                               &device_info.max_output_report_size,
+                               &device_info.max_feature_report_size);
 
   AddDevice(device_info);
 }
@@ -159,44 +157,4 @@
     RemoveDevice(device_path);
 }
 
-bool HidServiceLinux::FindHidrawDevNode(udev_device* parent,
-                                        std::string* result) {
-  udev* udev = udev_device_get_udev(parent);
-  if (!udev) {
-    return false;
-  }
-  ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev));
-  if (!enumerate) {
-    return false;
-  }
-  if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) {
-    return false;
-  }
-  if (udev_enumerate_scan_devices(enumerate.get())) {
-    return false;
-  }
-  std::string parent_path(udev_device_get_devpath(parent));
-  if (parent_path.length() == 0 || *parent_path.rbegin() != '/')
-    parent_path += '/';
-  udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
-  for (udev_list_entry* i = devices; i != NULL;
-       i = udev_list_entry_get_next(i)) {
-    ScopedUdevDevicePtr hid_dev(
-        udev_device_new_from_syspath(udev, udev_list_entry_get_name(i)));
-    const char* raw_path = udev_device_get_devnode(hid_dev.get());
-    std::string device_path = udev_device_get_devpath(hid_dev.get());
-    if (raw_path &&
-        !device_path.compare(0, parent_path.length(), parent_path)) {
-      std::string sub_path = device_path.substr(parent_path.length());
-      if (sub_path.substr(0, sizeof(kHidrawSubsystem) - 1) ==
-          kHidrawSubsystem) {
-        *result = raw_path;
-        return true;
-      }
-    }
-  }
-
-  return false;
-}
-
 }  // namespace device
diff --git a/device/hid/hid_service_linux.h b/device/hid/hid_service_linux.h
index 8d5b115..c69096d 100644
--- a/device/hid/hid_service_linux.h
+++ b/device/hid/hid_service_linux.h
@@ -32,8 +32,6 @@
  private:
   virtual ~HidServiceLinux();
 
-  static bool FindHidrawDevNode(udev_device* parent, std::string* result);
-
   DISALLOW_COPY_AND_ASSIGN(HidServiceLinux);
 };
 
diff --git a/device/hid/hid_service_mac.cc b/device/hid/hid_service_mac.cc
index ed85ec2..98bfd92 100644
--- a/device/hid/hid_service_mac.cc
+++ b/device/hid/hid_service_mac.cc
@@ -7,17 +7,19 @@
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/hid/IOHIDManager.h>
 
+#include <set>
 #include <string>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/mac/foundation_util.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/sys_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
 #include "device/hid/hid_connection_mac.h"
-#include "device/hid/hid_utils_mac.h"
 
 namespace device {
 
@@ -48,6 +50,89 @@
     CFSetApplyFunction(devices, HidEnumerationBackInserter, device_list);
 }
 
+bool TryGetHidIntProperty(IOHIDDeviceRef device,
+                          CFStringRef key,
+                          int32_t* result) {
+  CFNumberRef ref =
+      base::mac::CFCast<CFNumberRef>(IOHIDDeviceGetProperty(device, key));
+  return ref && CFNumberGetValue(ref, kCFNumberSInt32Type, result);
+}
+
+int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key) {
+  int32_t value;
+  if (TryGetHidIntProperty(device, key, &value))
+    return value;
+  return 0;
+}
+
+bool TryGetHidStringProperty(IOHIDDeviceRef device,
+                             CFStringRef key,
+                             std::string* result) {
+  CFStringRef ref =
+      base::mac::CFCast<CFStringRef>(IOHIDDeviceGetProperty(device, key));
+  if (!ref) {
+    return false;
+  }
+  *result = base::SysCFStringRefToUTF8(ref);
+  return true;
+}
+
+std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key) {
+  std::string value;
+  TryGetHidStringProperty(device, key, &value);
+  return value;
+}
+
+void GetReportIds(IOHIDElementRef element, std::set<int>& reportIDs) {
+  CFArrayRef children = IOHIDElementGetChildren(element);
+  if (!children)
+    return;
+  CFIndex childrenCount = CFArrayGetCount(children);
+  for (CFIndex j = 0; j < childrenCount; ++j) {
+    const IOHIDElementRef child = static_cast<IOHIDElementRef>(
+        const_cast<void*>(CFArrayGetValueAtIndex(children, j)));
+    uint32_t reportID = IOHIDElementGetReportID(child);
+    if (reportID) {
+      reportIDs.insert(reportID);
+    }
+    GetReportIds(child, reportIDs);
+  }
+}
+
+void GetCollectionInfos(IOHIDDeviceRef device,
+                        std::vector<HidCollectionInfo>* top_level_collections) {
+  STLClearObject(top_level_collections);
+  CFMutableDictionaryRef collections_filter =
+      CFDictionaryCreateMutable(kCFAllocatorDefault,
+                                0,
+                                &kCFTypeDictionaryKeyCallBacks,
+                                &kCFTypeDictionaryValueCallBacks);
+  const int kCollectionTypeValue = kIOHIDElementTypeCollection;
+  CFNumberRef collection_type_id = CFNumberCreate(
+      kCFAllocatorDefault, kCFNumberIntType, &kCollectionTypeValue);
+  CFDictionarySetValue(
+      collections_filter, CFSTR(kIOHIDElementTypeKey), collection_type_id);
+  CFRelease(collection_type_id);
+  CFArrayRef collections = IOHIDDeviceCopyMatchingElements(
+      device, collections_filter, kIOHIDOptionsTypeNone);
+  CFIndex collectionsCount = CFArrayGetCount(collections);
+  for (CFIndex i = 0; i < collectionsCount; i++) {
+    const IOHIDElementRef collection = static_cast<IOHIDElementRef>(
+        const_cast<void*>(CFArrayGetValueAtIndex(collections, i)));
+    // Top-Level Collection has no parent
+    if (IOHIDElementGetParent(collection) == 0) {
+      HidCollectionInfo collection_info;
+      HidUsageAndPage::Page page = static_cast<HidUsageAndPage::Page>(
+          IOHIDElementGetUsagePage(collection));
+      uint16_t usage = IOHIDElementGetUsage(collection);
+      collection_info.usage = HidUsageAndPage(usage, page);
+      // Explore children recursively and retrieve their report IDs
+      GetReportIds(collection, collection_info.report_ids);
+      top_level_collections->push_back(collection_info);
+    }
+  }
+}
+
 }  // namespace
 
 HidServiceMac::HidServiceMac() {
@@ -136,41 +221,23 @@
   // Note that our ownership of hid_device is implied if calling this method.
   // It is balanced in PlatformRemoveDevice.
   DCHECK(thread_checker_.CalledOnValidThread());
-
   HidDeviceInfo device_info;
   device_info.device_id = hid_device;
   device_info.vendor_id =
       GetHidIntProperty(hid_device, CFSTR(kIOHIDVendorIDKey));
   device_info.product_id =
       GetHidIntProperty(hid_device, CFSTR(kIOHIDProductIDKey));
-  device_info.input_report_size =
-      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxInputReportSizeKey));
-  device_info.output_report_size =
-      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxOutputReportSizeKey));
-  device_info.feature_report_size =
-      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxFeatureReportSizeKey));
-  CFTypeRef deviceUsagePairsRaw =
-      IOHIDDeviceGetProperty(hid_device, CFSTR(kIOHIDDeviceUsagePairsKey));
-  CFArrayRef deviceUsagePairs =
-      base::mac::CFCast<CFArrayRef>(deviceUsagePairsRaw);
-  CFIndex deviceUsagePairsCount = CFArrayGetCount(deviceUsagePairs);
-  for (CFIndex i = 0; i < deviceUsagePairsCount; i++) {
-    CFDictionaryRef deviceUsagePair = base::mac::CFCast<CFDictionaryRef>(
-        CFArrayGetValueAtIndex(deviceUsagePairs, i));
-    CFNumberRef usage_raw = base::mac::CFCast<CFNumberRef>(
-        CFDictionaryGetValue(deviceUsagePair, CFSTR(kIOHIDDeviceUsageKey)));
-    uint16_t usage;
-    CFNumberGetValue(usage_raw, kCFNumberSInt32Type, &usage);
-    CFNumberRef page_raw = base::mac::CFCast<CFNumberRef>(
-        CFDictionaryGetValue(deviceUsagePair, CFSTR(kIOHIDDeviceUsagePageKey)));
-    HidUsageAndPage::Page page;
-    CFNumberGetValue(page_raw, kCFNumberSInt32Type, &page);
-    device_info.usages.push_back(HidUsageAndPage(usage, page));
-  }
   device_info.product_name =
       GetHidStringProperty(hid_device, CFSTR(kIOHIDProductKey));
   device_info.serial_number =
       GetHidStringProperty(hid_device, CFSTR(kIOHIDSerialNumberKey));
+  GetCollectionInfos(hid_device, &device_info.collections);
+  device_info.max_input_report_size =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxInputReportSizeKey));
+  device_info.max_output_report_size =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxOutputReportSizeKey));
+  device_info.max_feature_report_size =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxFeatureReportSizeKey));
   AddDevice(device_info);
 }
 
diff --git a/device/hid/hid_service_win.cc b/device/hid/hid_service_win.cc
index 82477a5..9f27cff 100644
--- a/device/hid/hid_service_win.cc
+++ b/device/hid/hid_service_win.cc
@@ -187,38 +187,50 @@
   PHIDP_PREPARSED_DATA preparsed_data;
   if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) &&
       preparsed_data) {
-    HIDP_CAPS capabilities;
+    HIDP_CAPS capabilities = {0};
     if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) {
-      device_info.input_report_size = capabilities.InputReportByteLength;
-      device_info.output_report_size = capabilities.OutputReportByteLength;
-      device_info.feature_report_size = capabilities.FeatureReportByteLength;
-      device_info.usages.push_back(HidUsageAndPage(
-        capabilities.Usage,
-        static_cast<HidUsageAndPage::Page>(capabilities.UsagePage)));
-    }
-    // Detect if the device supports report ids.
-    if (capabilities.NumberInputValueCaps > 0) {
-      scoped_ptr<HIDP_VALUE_CAPS[]> value_caps(
-          new HIDP_VALUE_CAPS[capabilities.NumberInputValueCaps]);
-      USHORT value_caps_length = capabilities.NumberInputValueCaps;
-      if (HidP_GetValueCaps(HidP_Input, &value_caps[0], &value_caps_length,
-                            preparsed_data) == HIDP_STATUS_SUCCESS) {
-        device_info.has_report_id = (value_caps[0].ReportID != 0);
-      }
-    }
-    if (!device_info.has_report_id && capabilities.NumberInputButtonCaps > 0)
-    {
-      scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(
-        new HIDP_BUTTON_CAPS[capabilities.NumberInputButtonCaps]);
+      device_info.max_input_report_size = capabilities.InputReportByteLength;
+      device_info.max_output_report_size = capabilities.OutputReportByteLength;
+      device_info.max_feature_report_size =
+          capabilities.FeatureReportByteLength;
+      HidCollectionInfo collection_info;
+      collection_info.usage = HidUsageAndPage(
+          capabilities.Usage,
+          static_cast<HidUsageAndPage::Page>(capabilities.UsagePage));
       USHORT button_caps_length = capabilities.NumberInputButtonCaps;
-      if (HidP_GetButtonCaps(HidP_Input,
-                             &button_caps[0],
-                             &button_caps_length,
-                             preparsed_data) == HIDP_STATUS_SUCCESS) {
-        device_info.has_report_id = (button_caps[0].ReportID != 0);
+      if (button_caps_length > 0) {
+        scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(
+            new HIDP_BUTTON_CAPS[button_caps_length]);
+        if (HidP_GetButtonCaps(HidP_Input,
+                               &button_caps[0],
+                               &button_caps_length,
+                               preparsed_data) == HIDP_STATUS_SUCCESS) {
+          for (int i = 0; i < button_caps_length; i++) {
+            int report_id = button_caps[i].ReportID;
+            if (report_id != 0) {
+              collection_info.report_ids.insert(report_id);
+            }
+          }
+        }
       }
+      USHORT value_caps_length = capabilities.NumberInputValueCaps;
+      if (value_caps_length > 0) {
+        scoped_ptr<HIDP_VALUE_CAPS[]> value_caps(
+            new HIDP_VALUE_CAPS[value_caps_length]);
+        if (HidP_GetValueCaps(HidP_Input,
+                              &value_caps[0],
+                              &value_caps_length,
+                              preparsed_data) == HIDP_STATUS_SUCCESS) {
+          for (int i = 0; i < value_caps_length; i++) {
+            int report_id = value_caps[i].ReportID;
+            if (report_id != 0) {
+              collection_info.report_ids.insert(report_id);
+            }
+          }
+        }
+      }
+      device_info.collections.push_back(collection_info);
     }
-
     HidD_FreePreparsedData(preparsed_data);
   }
 
diff --git a/device/hid/hid_usage_and_page.cc b/device/hid/hid_usage_and_page.cc
index 773346b..079ec66 100644
--- a/device/hid/hid_usage_and_page.cc
+++ b/device/hid/hid_usage_and_page.cc
@@ -6,8 +6,31 @@
 
 namespace device {
 
-bool HidUsageAndPage::operator==(const HidUsageAndPage& other) const {
-  return usage == other.usage && usage_page == other.usage_page;
+bool HidUsageAndPage::IsProtected() const {
+  if (usage_page == HidUsageAndPage::kPageKeyboard)
+    return true;
+
+  if (usage_page != HidUsageAndPage::kPageGenericDesktop)
+    return false;
+
+  if (usage == HidUsageAndPage::kGenericDesktopPointer ||
+      usage == HidUsageAndPage::kGenericDesktopMouse ||
+      usage == HidUsageAndPage::kGenericDesktopKeyboard ||
+      usage == HidUsageAndPage::kGenericDesktopKeypad) {
+    return true;
+  }
+
+  if (usage >= HidUsageAndPage::kGenericDesktopSystemControl &&
+      usage <= HidUsageAndPage::kGenericDesktopSystemWarmRestart) {
+    return true;
+  }
+
+  if (usage >= HidUsageAndPage::kGenericDesktopSystemDock &&
+      usage <= HidUsageAndPage::kGenericDesktopSystemDisplaySwap) {
+    return true;
+  }
+
+  return false;
 }
 
 }  // namespace device
diff --git a/device/hid/hid_usage_and_page.h b/device/hid/hid_usage_and_page.h
index 98ac80d..635e9b3 100644
--- a/device/hid/hid_usage_and_page.h
+++ b/device/hid/hid_usage_and_page.h
@@ -126,7 +126,8 @@
   uint16_t usage;
   Page usage_page;
 
-  bool operator==(const HidUsageAndPage& other) const;
+  // Indicates whether this usage is protected by Chrome.
+  bool IsProtected() const;
 };
 
 }  // namespace device
diff --git a/device/hid/hid_utils_mac.cc b/device/hid/hid_utils_mac.cc
deleted file mode 100644
index 46605d8..0000000
--- a/device/hid/hid_utils_mac.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "device/hid/hid_utils_mac.h"
-
-#include "base/mac/foundation_util.h"
-#include "base/strings/sys_string_conversions.h"
-
-namespace device {
-
-int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key) {
-  int32_t value;
-  if (TryGetHidIntProperty(device, key, &value))
-    return value;
-  return 0;
-}
-
-std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key) {
-  std::string value;
-  TryGetHidStringProperty(device, key, &value);
-  return value;
-}
-
-bool TryGetHidIntProperty(IOHIDDeviceRef device,
-                          CFStringRef key,
-                          int32_t* result) {
-  CFNumberRef ref = base::mac::CFCast<CFNumberRef>(
-      IOHIDDeviceGetProperty(device, key));
-  return ref && CFNumberGetValue(ref, kCFNumberSInt32Type, result);
-}
-
-bool TryGetHidStringProperty(IOHIDDeviceRef device,
-                             CFStringRef key,
-                             std::string* result) {
-  CFStringRef ref = base::mac::CFCast<CFStringRef>(
-      IOHIDDeviceGetProperty(device, key));
-  if (!ref) {
-    return false;
-  }
-  *result = base::SysCFStringRefToUTF8(ref);
-  return true;
-}
-
-}  // namespace device
diff --git a/device/hid/hid_utils_mac.h b/device/hid/hid_utils_mac.h
deleted file mode 100644
index e9b2524..0000000
--- a/device/hid/hid_utils_mac.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef DEVICE_HID_HID_UTILS_MAC_H_
-#define DEVICE_HID_HID_UTILS_MAC_H_
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <IOKit/hid/IOHIDManager.h>
-#include <stdint.h>
-
-#include <string>
-
-namespace device {
-
-int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key);
-
-std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key);
-
-bool TryGetHidIntProperty(IOHIDDeviceRef device,
-                          CFStringRef key,
-                          int32_t* result);
-
-bool TryGetHidStringProperty(IOHIDDeviceRef device,
-                             CFStringRef key,
-                             std::string* result);
-
-}  // namespace device
-
-#endif  // DEVICE_HID_HID_UTILS_MAC_H_