Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2008 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "talk/base/systeminfo.h"
     29 
     30 #if defined(WIN32)
     31 #include <winsock2.h>
     32 #ifndef EXCLUDE_D3D9
     33 #include <d3d9.h>
     34 #endif
     35 #include <intrin.h>  // for __cpuid()
     36 #elif defined(OSX)
     37 #include <ApplicationServices/ApplicationServices.h>
     38 #include <CoreServices/CoreServices.h>
     39 #elif defined(LINUX) || defined(ANDROID)
     40 #include <unistd.h>
     41 #endif
     42 #if defined(OSX) || defined(IOS)
     43 #include <sys/sysctl.h>
     44 #endif
     45 
     46 #if defined(WIN32)
     47 #include "talk/base/scoped_ptr.h"
     48 #include "talk/base/win32.h"
     49 #elif defined(OSX)
     50 #include "talk/base/macconversion.h"
     51 #elif defined(LINUX) || defined(ANDROID)
     52 #include "talk/base/linux.h"
     53 #endif
     54 #include "talk/base/common.h"
     55 #include "talk/base/logging.h"
     56 #include "talk/base/stringutils.h"
     57 
     58 namespace talk_base {
     59 
     60 // See Also: http://msdn.microsoft.com/en-us/library/ms683194(v=vs.85).aspx
     61 #if defined(WIN32)
     62 typedef BOOL (WINAPI *LPFN_GLPI)(
     63     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
     64     PDWORD);
     65 
     66 static void GetProcessorInformation(int* physical_cpus, int* cache_size) {
     67   // GetLogicalProcessorInformation() is available on Windows XP SP3 and beyond.
     68   LPFN_GLPI glpi = reinterpret_cast<LPFN_GLPI>(GetProcAddress(
     69       GetModuleHandle(L"kernel32"),
     70       "GetLogicalProcessorInformation"));
     71   if (NULL == glpi) {
     72     return;
     73   }
     74   // Determine buffer size, allocate and get processor information.
     75   // Size can change between calls (unlikely), so a loop is done.
     76   DWORD return_length = 0;
     77   scoped_array<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> infos;
     78   while (!glpi(infos.get(), &return_length)) {
     79     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
     80       infos.reset(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[
     81           return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)]);
     82     } else {
     83       return;
     84     }
     85   }
     86   *physical_cpus = 0;
     87   *cache_size = 0;
     88   for (size_t i = 0;
     89       i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
     90     if (infos[i].Relationship == RelationProcessorCore) {
     91       ++*physical_cpus;
     92     } else if (infos[i].Relationship == RelationCache) {
     93       int next_cache_size = static_cast<int>(infos[i].Cache.Size);
     94       if (next_cache_size >= *cache_size) {
     95         *cache_size = next_cache_size;
     96       }
     97     }
     98   }
     99   return;
    100 }
    101 #else
    102 // TODO(fbarchard): Use gcc 4.4 provided cpuid intrinsic
    103 // 32 bit fpic requires ebx be preserved
    104 #if (defined(__pic__) || defined(__APPLE__)) && defined(__i386__)
    105 static inline void __cpuid(int cpu_info[4], int info_type) {
    106   __asm__ volatile (  // NOLINT
    107     "mov %%ebx, %%edi\n"
    108     "cpuid\n"
    109     "xchg %%edi, %%ebx\n"
    110     : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
    111     : "a"(info_type)
    112   );  // NOLINT
    113 }
    114 #elif defined(__i386__) || defined(__x86_64__)
    115 static inline void __cpuid(int cpu_info[4], int info_type) {
    116   __asm__ volatile (  // NOLINT
    117     "cpuid\n"
    118     : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
    119     : "a"(info_type)
    120   );  // NOLINT
    121 }
    122 #endif
    123 #endif  // WIN32
    124 
    125 // Note(fbarchard):
    126 // Family and model are extended family and extended model.  8 bits each.
    127 SystemInfo::SystemInfo()
    128     : physical_cpus_(1), logical_cpus_(1), cache_size_(0),
    129       cpu_family_(0), cpu_model_(0), cpu_stepping_(0),
    130       cpu_speed_(0), memory_(0) {
    131   // Initialize the basic information.
    132 #if defined(__arm__) || defined(_M_ARM)
    133   cpu_arch_ = SI_ARCH_ARM;
    134 #elif defined(__x86_64__) || defined(_M_X64)
    135   cpu_arch_ = SI_ARCH_X64;
    136 #elif defined(__i386__) || defined(_M_IX86)
    137   cpu_arch_ = SI_ARCH_X86;
    138 #else
    139   cpu_arch_ = SI_ARCH_UNKNOWN;
    140 #endif
    141 
    142 #if defined(WIN32)
    143   SYSTEM_INFO si;
    144   GetSystemInfo(&si);
    145   logical_cpus_ = si.dwNumberOfProcessors;
    146   GetProcessorInformation(&physical_cpus_, &cache_size_);
    147   if (physical_cpus_ <= 0) {
    148     physical_cpus_ = logical_cpus_;
    149   }
    150   cpu_family_ = si.wProcessorLevel;
    151   cpu_model_ = si.wProcessorRevision >> 8;
    152   cpu_stepping_ = si.wProcessorRevision & 0xFF;
    153 #elif defined(OSX) || defined(IOS)
    154   uint32_t sysctl_value;
    155   size_t length = sizeof(sysctl_value);
    156   if (!sysctlbyname("hw.physicalcpu_max", &sysctl_value, &length, NULL, 0)) {
    157     physical_cpus_ = static_cast<int>(sysctl_value);
    158   }
    159   length = sizeof(sysctl_value);
    160   if (!sysctlbyname("hw.logicalcpu_max", &sysctl_value, &length, NULL, 0)) {
    161     logical_cpus_ = static_cast<int>(sysctl_value);
    162   }
    163   uint64_t sysctl_value64;
    164   length = sizeof(sysctl_value64);
    165   if (!sysctlbyname("hw.l3cachesize", &sysctl_value64, &length, NULL, 0)) {
    166     cache_size_ = static_cast<int>(sysctl_value64);
    167   }
    168   if (!cache_size_) {
    169     length = sizeof(sysctl_value64);
    170     if (!sysctlbyname("hw.l2cachesize", &sysctl_value64, &length, NULL, 0)) {
    171       cache_size_ = static_cast<int>(sysctl_value64);
    172     }
    173   }
    174   length = sizeof(sysctl_value);
    175   if (!sysctlbyname("machdep.cpu.family", &sysctl_value, &length, NULL, 0)) {
    176     cpu_family_ = static_cast<int>(sysctl_value);
    177   }
    178   length = sizeof(sysctl_value);
    179   if (!sysctlbyname("machdep.cpu.model", &sysctl_value, &length, NULL, 0)) {
    180     cpu_model_ = static_cast<int>(sysctl_value);
    181   }
    182   length = sizeof(sysctl_value);
    183   if (!sysctlbyname("machdep.cpu.stepping", &sysctl_value, &length, NULL, 0)) {
    184     cpu_stepping_ = static_cast<int>(sysctl_value);
    185   }
    186 #else  // LINUX || ANDROID
    187   ProcCpuInfo proc_info;
    188   if (proc_info.LoadFromSystem()) {
    189     proc_info.GetNumCpus(&logical_cpus_);
    190     proc_info.GetNumPhysicalCpus(&physical_cpus_);
    191     proc_info.GetCpuFamily(&cpu_family_);
    192 #if defined(CPU_X86)
    193     // These values only apply to x86 systems.
    194     proc_info.GetSectionIntValue(0, "model", &cpu_model_);
    195     proc_info.GetSectionIntValue(0, "stepping", &cpu_stepping_);
    196     proc_info.GetSectionIntValue(0, "cpu MHz", &cpu_speed_);
    197     proc_info.GetSectionIntValue(0, "cache size", &cache_size_);
    198     cache_size_ *= 1024;
    199 #endif
    200   }
    201   // ProcCpuInfo reads cpu speed from "cpu MHz" under /proc/cpuinfo.
    202   // But that number is a moving target which can change on-the-fly according to
    203   // many factors including system workload.
    204   // See /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors.
    205   // The one in /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq is more
    206   // accurate. We use it as our cpu speed when it is available.
    207   // cpuinfo_max_freq is measured in KHz and requires conversion to MHz.
    208   int max_freq = talk_base::ReadCpuMaxFreq();
    209   if (max_freq > 0) {
    210     cpu_speed_ = max_freq / 1000;
    211   }
    212 #endif
    213 // For L2 CacheSize see also
    214 // http://www.flounder.com/cpuid_explorer2.htm#CPUID(0x800000006)
    215 #ifdef CPU_X86
    216   if (cache_size_ == 0) {
    217     int cpu_info[4];
    218     __cpuid(cpu_info, 0x80000000);  // query maximum extended cpuid function.
    219     if (static_cast<uint32>(cpu_info[0]) >= 0x80000006) {
    220       __cpuid(cpu_info, 0x80000006);
    221       cache_size_ = (cpu_info[2] >> 16) * 1024;
    222     }
    223   }
    224 #endif
    225 }
    226 
    227 // Return the number of cpu threads available to the system.
    228 int SystemInfo::GetMaxCpus() {
    229   return logical_cpus_;
    230 }
    231 
    232 // Return the number of cpu cores available to the system.
    233 int SystemInfo::GetMaxPhysicalCpus() {
    234   return physical_cpus_;
    235 }
    236 
    237 // Return the number of cpus available to the process.  Since affinity can be
    238 // changed on the fly, do not cache this value.
    239 // Can be affected by heat.
    240 int SystemInfo::GetCurCpus() {
    241   int cur_cpus;
    242 #if defined(WIN32)
    243   DWORD_PTR process_mask, system_mask;
    244   ::GetProcessAffinityMask(::GetCurrentProcess(), &process_mask, &system_mask);
    245   for (cur_cpus = 0; process_mask; ++cur_cpus) {
    246     // Sparse-ones algorithm. There are slightly faster methods out there but
    247     // they are unintuitive and won't make a difference on a single dword.
    248     process_mask &= (process_mask - 1);
    249   }
    250 #elif defined(OSX) || defined(IOS)
    251   uint32_t sysctl_value;
    252   size_t length = sizeof(sysctl_value);
    253   int error = sysctlbyname("hw.ncpu", &sysctl_value, &length, NULL, 0);
    254   cur_cpus = !error ? static_cast<int>(sysctl_value) : 1;
    255 #else
    256   // Linux, Solaris, ANDROID
    257   cur_cpus = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
    258 #endif
    259   return cur_cpus;
    260 }
    261 
    262 // Return the type of this CPU.
    263 SystemInfo::Architecture SystemInfo::GetCpuArchitecture() {
    264   return cpu_arch_;
    265 }
    266 
    267 // Returns the vendor string from the cpu, e.g. "GenuineIntel", "AuthenticAMD".
    268 // See "Intel Processor Identification and the CPUID Instruction"
    269 // (Intel document number: 241618)
    270 std::string SystemInfo::GetCpuVendor() {
    271   if (cpu_vendor_.empty()) {
    272 #if defined(CPU_X86)
    273     int cpu_info[4];
    274     __cpuid(cpu_info, 0);
    275     cpu_info[0] = cpu_info[1];  // Reorder output
    276     cpu_info[1] = cpu_info[3];
    277     cpu_info[2] = cpu_info[2];
    278     cpu_info[3] = 0;
    279     cpu_vendor_ = std::string(reinterpret_cast<char*>(&cpu_info[0]));
    280 #elif defined(CPU_ARM)
    281     cpu_vendor_ = std::string("ARM");
    282 #else
    283     cpu_vendor_ = std::string("Undefined");
    284 #endif
    285   }
    286   return cpu_vendor_;
    287 }
    288 
    289 int SystemInfo::GetCpuCacheSize() {
    290   return cache_size_;
    291 }
    292 
    293 // Return the "family" of this CPU.
    294 int SystemInfo::GetCpuFamily() {
    295   return cpu_family_;
    296 }
    297 
    298 // Return the "model" of this CPU.
    299 int SystemInfo::GetCpuModel() {
    300   return cpu_model_;
    301 }
    302 
    303 // Return the "stepping" of this CPU.
    304 int SystemInfo::GetCpuStepping() {
    305   return cpu_stepping_;
    306 }
    307 
    308 // Return the clockrate of the primary processor in Mhz.  This value can be
    309 // cached.  Returns -1 on error.
    310 int SystemInfo::GetMaxCpuSpeed() {
    311   if (cpu_speed_) {
    312     return cpu_speed_;
    313   }
    314 #if defined(WIN32)
    315   HKEY key;
    316   static const WCHAR keyName[] =
    317       L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
    318 
    319   if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName , 0, KEY_QUERY_VALUE, &key)
    320       == ERROR_SUCCESS) {
    321     DWORD data, len;
    322     len = sizeof(data);
    323 
    324     if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast<LPBYTE>(&data),
    325                         &len) == ERROR_SUCCESS) {
    326       cpu_speed_ = data;
    327     } else {
    328       LOG(LS_WARNING) << "Failed to query registry value HKLM\\" << keyName
    329                       << "\\~Mhz";
    330       cpu_speed_ = -1;
    331     }
    332 
    333     RegCloseKey(key);
    334   } else {
    335     LOG(LS_WARNING) << "Failed to open registry key HKLM\\" << keyName;
    336     cpu_speed_ = -1;
    337   }
    338 #elif defined(IOS) || defined(OSX)
    339   uint64_t sysctl_value;
    340   size_t length = sizeof(sysctl_value);
    341   int error = sysctlbyname("hw.cpufrequency_max", &sysctl_value, &length,
    342                            NULL, 0);
    343   cpu_speed_ = !error ? static_cast<int>(sysctl_value/1000000) : -1;
    344 #else
    345   // TODO(fbarchard): Implement using proc/cpuinfo
    346   cpu_speed_ = 0;
    347 #endif
    348   return cpu_speed_;
    349 }
    350 
    351 // Dynamically check the current clockrate, which could be reduced because of
    352 // powersaving profiles.  Eventually for windows we want to query WMI for
    353 // root\WMI::ProcessorPerformance.InstanceName="Processor_Number_0".frequency
    354 int SystemInfo::GetCurCpuSpeed() {
    355 #if defined(WIN32)
    356   // TODO(fbarchard): Add WMI check, requires COM initialization
    357   // NOTE(fbarchard): Testable on Sandy Bridge.
    358   return GetMaxCpuSpeed();
    359 #elif defined(IOS) || defined(OSX)
    360   uint64_t sysctl_value;
    361   size_t length = sizeof(sysctl_value);
    362   int error = sysctlbyname("hw.cpufrequency", &sysctl_value, &length, NULL, 0);
    363   return !error ? static_cast<int>(sysctl_value/1000000) : GetMaxCpuSpeed();
    364 #else  // LINUX || ANDROID
    365   // TODO(fbarchard): Use proc/cpuinfo for Cur speed on Linux.
    366   return GetMaxCpuSpeed();
    367 #endif
    368 }
    369 
    370 // Returns the amount of installed physical memory in Bytes.  Cacheable.
    371 // Returns -1 on error.
    372 int64 SystemInfo::GetMemorySize() {
    373   if (memory_) {
    374     return memory_;
    375   }
    376 
    377 #if defined(WIN32)
    378   MEMORYSTATUSEX status = {0};
    379   status.dwLength = sizeof(status);
    380 
    381   if (GlobalMemoryStatusEx(&status)) {
    382     memory_ = status.ullTotalPhys;
    383   } else {
    384     LOG_GLE(LS_WARNING) << "GlobalMemoryStatusEx failed.";
    385     memory_ = -1;
    386   }
    387 
    388 #elif defined(OSX) || defined(IOS)
    389   size_t len = sizeof(memory_);
    390   int error = sysctlbyname("hw.memsize", &memory_, &len, NULL, 0);
    391   if (error || memory_ == 0) {
    392     memory_ = -1;
    393   }
    394 #else  // LINUX || ANDROID
    395   memory_ = static_cast<int64>(sysconf(_SC_PHYS_PAGES)) *
    396       static_cast<int64>(sysconf(_SC_PAGESIZE));
    397   if (memory_ < 0) {
    398     LOG(LS_WARNING) << "sysconf(_SC_PHYS_PAGES) failed."
    399                     << "sysconf(_SC_PHYS_PAGES) " << sysconf(_SC_PHYS_PAGES)
    400                     << "sysconf(_SC_PAGESIZE) " << sysconf(_SC_PAGESIZE);
    401     memory_ = -1;
    402   }
    403 #endif
    404 
    405   return memory_;
    406 }
    407 
    408 
    409 // Return the name of the machine model we are currently running on.
    410 // This is a human readable string that consists of the name and version
    411 // number of the hardware, i.e 'MacBookAir1,1'. Returns an empty string if
    412 // model can not be determined. The string is cached for subsequent calls.
    413 std::string SystemInfo::GetMachineModel() {
    414   if (!machine_model_.empty()) {
    415     return machine_model_;
    416   }
    417 
    418 #if defined(OSX) || defined(IOS)
    419   char buffer[128];
    420   size_t length = sizeof(buffer);
    421   int error = sysctlbyname("hw.model", buffer, &length, NULL, 0);
    422   if (!error) {
    423     machine_model_.assign(buffer, length - 1);
    424   } else {
    425     machine_model_.clear();
    426   }
    427 #else
    428   machine_model_ = "Not available";
    429 #endif
    430 
    431   return machine_model_;
    432 }
    433 
    434 #ifdef OSX
    435 // Helper functions to query IOKit for video hardware properties.
    436 static CFTypeRef SearchForProperty(io_service_t port, CFStringRef name) {
    437   return IORegistryEntrySearchCFProperty(port, kIOServicePlane,
    438       name, kCFAllocatorDefault,
    439       kIORegistryIterateRecursively | kIORegistryIterateParents);
    440 }
    441 
    442 static void GetProperty(io_service_t port, CFStringRef name, int* value) {
    443   if (!value) return;
    444   CFTypeRef ref = SearchForProperty(port, name);
    445   if (ref) {
    446     CFTypeID refType = CFGetTypeID(ref);
    447     if (CFNumberGetTypeID() == refType) {
    448       CFNumberRef number = reinterpret_cast<CFNumberRef>(ref);
    449       p_convertCFNumberToInt(number, value);
    450     } else if (CFDataGetTypeID() == refType) {
    451       CFDataRef data = reinterpret_cast<CFDataRef>(ref);
    452       if (CFDataGetLength(data) == sizeof(UInt32)) {
    453         *value = *reinterpret_cast<const UInt32*>(CFDataGetBytePtr(data));
    454       }
    455     }
    456     CFRelease(ref);
    457   }
    458 }
    459 
    460 static void GetProperty(io_service_t port, CFStringRef name,
    461                         std::string* value) {
    462   if (!value) return;
    463   CFTypeRef ref = SearchForProperty(port, name);
    464   if (ref) {
    465     CFTypeID refType = CFGetTypeID(ref);
    466     if (CFStringGetTypeID() == refType) {
    467       CFStringRef stringRef = reinterpret_cast<CFStringRef>(ref);
    468       p_convertHostCFStringRefToCPPString(stringRef, *value);
    469     } else if (CFDataGetTypeID() == refType) {
    470       CFDataRef dataRef = reinterpret_cast<CFDataRef>(ref);
    471       *value = std::string(reinterpret_cast<const char*>(
    472           CFDataGetBytePtr(dataRef)), CFDataGetLength(dataRef));
    473     }
    474     CFRelease(ref);
    475   }
    476 }
    477 #endif
    478 
    479 // Fills a struct with information on the graphics adapater and returns true
    480 // iff successful.
    481 bool SystemInfo::GetGpuInfo(GpuInfo *info) {
    482   if (!info) return false;
    483 #if defined(WIN32) && !defined(EXCLUDE_D3D9)
    484   D3DADAPTER_IDENTIFIER9 identifier;
    485   HRESULT hr = E_FAIL;
    486   HINSTANCE d3d_lib = LoadLibrary(L"d3d9.dll");
    487 
    488   if (d3d_lib) {
    489     typedef IDirect3D9* (WINAPI *D3DCreate9Proc)(UINT);
    490     D3DCreate9Proc d3d_create_proc = reinterpret_cast<D3DCreate9Proc>(
    491         GetProcAddress(d3d_lib, "Direct3DCreate9"));
    492     if (d3d_create_proc) {
    493       IDirect3D9* d3d = d3d_create_proc(D3D_SDK_VERSION);
    494       if (d3d) {
    495         hr = d3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier);
    496         d3d->Release();
    497       }
    498     }
    499     FreeLibrary(d3d_lib);
    500   }
    501 
    502   if (hr != D3D_OK) {
    503     LOG(LS_ERROR) << "Failed to access Direct3D9 information.";
    504     return false;
    505   }
    506 
    507   info->device_name = identifier.DeviceName;
    508   info->description = identifier.Description;
    509   info->vendor_id = identifier.VendorId;
    510   info->device_id = identifier.DeviceId;
    511   info->driver = identifier.Driver;
    512   // driver_version format: product.version.subversion.build
    513   std::stringstream ss;
    514   ss << HIWORD(identifier.DriverVersion.HighPart) << "."
    515      << LOWORD(identifier.DriverVersion.HighPart) << "."
    516      << HIWORD(identifier.DriverVersion.LowPart) << "."
    517      << LOWORD(identifier.DriverVersion.LowPart);
    518   info->driver_version = ss.str();
    519   return true;
    520 #elif defined(OSX)
    521   // We'll query the IOKit for the gpu of the main display.
    522   io_service_t display_service_port = CGDisplayIOServicePort(
    523       kCGDirectMainDisplay);
    524   GetProperty(display_service_port, CFSTR("vendor-id"), &info->vendor_id);
    525   GetProperty(display_service_port, CFSTR("device-id"), &info->device_id);
    526   GetProperty(display_service_port, CFSTR("model"), &info->description);
    527   return true;
    528 #else  // LINUX || ANDROID
    529   // TODO(fbarchard): Implement this on Linux
    530   return false;
    531 #endif
    532 }
    533 }  // namespace talk_base
    534