[M71 Dev][Tizen] Fix compiler errors
[platform/framework/web/chromium-efl.git] / device / serial / serial_device_enumerator_mac.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "device/serial/serial_device_enumerator_mac.h"
6
7 #include <IOKit/serial/IOSerialKeys.h>
8 #include <IOKit/usb/IOUSBLib.h>
9 #include <stdint.h>
10
11 #include <algorithm>
12 #include <memory>
13 #include <unordered_set>
14 #include <utility>
15
16 #include "base/files/file_enumerator.h"
17 #include "base/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/mac/scoped_cftyperef.h"
20 #include "base/mac/scoped_ioobject.h"
21 #include "base/metrics/histogram_functions.h"
22 #include "base/strings/pattern.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/sys_string_conversions.h"
25 #include "base/threading/scoped_blocking_call.h"
26
27 namespace device {
28
29 namespace {
30
31 // Searches a service and all ancestor services for a property with the
32 // specified key, returning NULL if no such key was found.
33 CFTypeRef GetCFProperty(io_service_t service, const CFStringRef key) {
34   // We search for the specified property not only on the specified service, but
35   // all ancestors of that service. This is important because if a device is
36   // both serial and USB, in the registry tree it appears as a serial service
37   // with a USB service as its ancestor. Without searching ancestors services
38   // for the specified property, we'd miss all USB properties.
39   return IORegistryEntrySearchCFProperty(
40       service, kIOServicePlane, key, NULL,
41       kIORegistryIterateRecursively | kIORegistryIterateParents);
42 }
43
44 // Searches a service and all ancestor services for a string property with the
45 // specified key, returning NULL if no such key was found.
46 CFStringRef GetCFStringProperty(io_service_t service, const CFStringRef key) {
47   CFTypeRef value = GetCFProperty(service, key);
48   if (value && (CFGetTypeID(value) == CFStringGetTypeID()))
49     return static_cast<CFStringRef>(value);
50
51   return NULL;
52 }
53
54 // Searches a service and all ancestor services for a number property with the
55 // specified key, returning NULL if no such key was found.
56 CFNumberRef GetCFNumberProperty(io_service_t service, const CFStringRef key) {
57   CFTypeRef value = GetCFProperty(service, key);
58   if (value && (CFGetTypeID(value) == CFNumberGetTypeID()))
59     return static_cast<CFNumberRef>(value);
60
61   return NULL;
62 }
63
64 // Searches the specified service for a string property with the specified key,
65 // sets value to that property's value, and returns whether the operation was
66 // successful.
67 bool GetStringProperty(io_service_t service,
68                        const CFStringRef key,
69                        std::string* value) {
70   CFStringRef propValue = GetCFStringProperty(service, key);
71   if (propValue) {
72     *value = base::SysCFStringRefToUTF8(propValue);
73     return true;
74   }
75
76   return false;
77 }
78
79 // Searches the specified service for a uint16_t property with the specified
80 // key, sets value to that property's value, and returns whether the operation
81 // was successful.
82 bool GetUInt16Property(io_service_t service,
83                        const CFStringRef key,
84                        uint16_t* value) {
85   CFNumberRef propValue = GetCFNumberProperty(service, key);
86   if (propValue) {
87     int intValue;
88     if (CFNumberGetValue(propValue, kCFNumberIntType, &intValue)) {
89       *value = static_cast<uint16_t>(intValue);
90       return true;
91     }
92   }
93
94   return false;
95 }
96
97 // Returns value clamped to the range of [min, max].
98 int Clamp(int value, int min, int max) {
99   return std::min(std::max(value, min), max);
100 }
101
102 // Returns an array of devices as retrieved through the new method of
103 // enumerating serial devices (IOKit).  This new method gives more information
104 // about the devices than the old method.
105 std::vector<mojom::SerialDeviceInfoPtr> GetDevicesNew() {
106   std::vector<mojom::SerialDeviceInfoPtr> devices;
107   
108   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
109   // Make a service query to find all serial devices.
110   CFMutableDictionaryRef matchingDict =
111       IOServiceMatching(kIOSerialBSDServiceValue);
112   if (!matchingDict)
113     return devices;
114
115   io_iterator_t it;
116   kern_return_t kr =
117       IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &it);
118   if (kr != KERN_SUCCESS)
119     return devices;
120
121   base::mac::ScopedIOObject<io_iterator_t> scoped_it(it);
122   base::mac::ScopedIOObject<io_service_t> scoped_device;
123   while (scoped_device.reset(IOIteratorNext(scoped_it.get())), scoped_device) {
124     auto callout_info = mojom::SerialDeviceInfo::New();
125
126     uint16_t vendorId;
127     if (GetUInt16Property(scoped_device.get(), CFSTR(kUSBVendorID),
128                           &vendorId)) {
129       callout_info->has_vendor_id = true;
130       callout_info->vendor_id = vendorId;
131     }
132
133     uint16_t productId;
134     if (GetUInt16Property(scoped_device.get(), CFSTR(kUSBProductID),
135                           &productId)) {
136       callout_info->has_product_id = true;
137       callout_info->product_id = productId;
138     }
139
140     std::string display_name;
141     if (GetStringProperty(scoped_device.get(), CFSTR(kUSBProductString),
142                           &display_name)) {
143       callout_info->display_name = std::move(display_name);
144     }
145
146     // Each serial device has two "paths" in /dev/ associated with it: a
147     // "dialin" path starting with "tty" and a "callout" path starting with
148     // "cu". Each of these is considered a different device from Chrome's
149     // standpoint, but both should share the device's USB properties.
150     std::string dialinDevice;
151     if (GetStringProperty(scoped_device.get(), CFSTR(kIODialinDeviceKey),
152                           &dialinDevice)) {
153       mojom::SerialDeviceInfoPtr dialin_info = callout_info.Clone();
154       dialin_info->path = dialinDevice;
155       devices.push_back(std::move(dialin_info));
156     }
157
158     std::string calloutDevice;
159     if (GetStringProperty(scoped_device.get(), CFSTR(kIOCalloutDeviceKey),
160                           &calloutDevice)) {
161       callout_info->path = calloutDevice;
162       devices.push_back(std::move(callout_info));
163     }
164   }
165
166   return devices;
167 }
168
169 // Returns an array of devices as retrieved through the old method of
170 // enumerating serial devices (pattern matching in /dev/). This old method gives
171 // less information about the devices than the new method.
172 std::vector<mojom::SerialDeviceInfoPtr> GetDevicesOld() {
173   const base::FilePath kDevRoot("/dev");
174   const int kFilesAndSymLinks =
175       base::FileEnumerator::FILES | base::FileEnumerator::SHOW_SYM_LINKS;
176
177   std::set<std::string> valid_patterns;
178   valid_patterns.insert("/dev/*Bluetooth*");
179   valid_patterns.insert("/dev/*Modem*");
180   valid_patterns.insert("/dev/*bluetooth*");
181   valid_patterns.insert("/dev/*modem*");
182   valid_patterns.insert("/dev/*serial*");
183   valid_patterns.insert("/dev/tty.*");
184   valid_patterns.insert("/dev/cu.*");
185
186   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
187   std::vector<mojom::SerialDeviceInfoPtr> devices;
188   base::FileEnumerator enumerator(kDevRoot, false, kFilesAndSymLinks);
189   do {
190     const base::FilePath next_device_path(enumerator.Next());
191     const std::string next_device = next_device_path.value();
192     if (next_device.empty())
193       break;
194
195     std::set<std::string>::const_iterator i = valid_patterns.begin();
196     for (; i != valid_patterns.end(); ++i) {
197       if (base::MatchPattern(next_device, *i)) {
198         auto info = mojom::SerialDeviceInfo::New();
199         info->path = next_device;
200         devices.push_back(std::move(info));
201         break;
202       }
203     }
204   } while (true);
205   return devices;
206 }
207
208 }  // namespace
209
210 // static
211 std::unique_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
212   return std::unique_ptr<SerialDeviceEnumerator>(
213       new SerialDeviceEnumeratorMac());
214 }
215
216 SerialDeviceEnumeratorMac::SerialDeviceEnumeratorMac() {}
217
218 SerialDeviceEnumeratorMac::~SerialDeviceEnumeratorMac() {}
219
220 std::vector<mojom::SerialDeviceInfoPtr>
221 SerialDeviceEnumeratorMac::GetDevices() {
222   std::vector<mojom::SerialDeviceInfoPtr> devices = GetDevicesNew();
223   std::vector<mojom::SerialDeviceInfoPtr> old_devices = GetDevicesOld();
224
225   base::UmaHistogramSparse("Hardware.Serial.NewMinusOldDeviceListSize",
226                            Clamp(devices.size() - old_devices.size(), -10, 10));
227
228   // Add devices found from both the new and old methods of enumeration. If a
229   // device is found using both the new and the old enumeration method, then we
230   // take the device from the new enumeration method because it's able to
231   // collect more information. We do this by inserting the new devices first,
232   // because insertions are ignored if the key already exists.
233   std::unordered_set<std::string> devices_seen;
234   for (const auto& device : devices) {
235     bool inserted = devices_seen.insert(device->path).second;
236     DCHECK(inserted);
237   }
238   for (auto& device : old_devices) {
239     if (devices_seen.insert(device->path).second)
240       devices.push_back(std::move(device));
241   }
242   return devices;
243 }
244
245 }  // namespace device