001package com.pi4j.system; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : SystemInfo.java 009 * 010 * This file is part of the Pi4J project. More information about 011 * this project can be found here: http://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2015 Pi4J 015 * %% 016 * This program is free software: you can redistribute it and/or modify 017 * it under the terms of the GNU Lesser General Public License as 018 * published by the Free Software Foundation, either version 3 of the 019 * License, or (at your option) any later version. 020 * 021 * This program is distributed in the hope that it will be useful, 022 * but WITHOUT ANY WARRANTY; without even the implied warranty of 023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 024 * GNU General Lesser Public License for more details. 025 * 026 * You should have received a copy of the GNU General Lesser Public 027 * License along with this program. If not, see 028 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 029 * #L% 030 */ 031 032 033import com.pi4j.util.ExecUtil; 034import com.pi4j.util.StringUtil; 035 036import java.io.BufferedReader; 037import java.io.FileReader; 038import java.io.IOException; 039import java.security.AccessController; 040import java.security.PrivilegedAction; 041import java.text.ParseException; 042import java.util.ArrayList; 043import java.util.HashMap; 044import java.util.List; 045import java.util.Map; 046 047public class SystemInfo { 048 049 // private constructor 050 private SystemInfo() { 051 // forbid object construction 052 } 053 054 public enum BoardType { 055 UNKNOWN, 056 ModelA_Rev1, 057 ModelA_Plus_Rev1, 058 ModelB_Rev1, 059 ModelB_Rev2, 060 ModelB_Plus_Rev1, 061 Compute_Module_Rev1, 062 Model2B_Rev1 063 } 064 065 private static Map<String, String> cpuInfo; 066 067 private static String getCpuInfo(String target) throws IOException, InterruptedException { 068 // if the CPU data has not been previously acquired, then acquire it now 069 if (cpuInfo == null) { 070 cpuInfo = new HashMap<>(); 071 072 try(BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"))) { 073 for(String line; (line = br.readLine()) != null; ) { 074 String parts[] = line.split(":", 2); 075 if (parts.length >= 2 && !parts[0].trim().isEmpty() && !parts[1].trim().isEmpty()) { 076 String cpuKey = parts[0].trim().toLowerCase(); 077 cpuInfo.put(cpuKey, parts[1].trim()); 078 } 079 } 080 } 081 } 082 083 target = target.toLowerCase(); 084 if (cpuInfo.containsKey(target)) { 085 return cpuInfo.get(target); 086 } 087 088 throw new RuntimeException("Invalid target: " + target); 089 } 090 091 public static String getProcessor() throws IOException, InterruptedException { 092 return getCpuInfo("processor"); 093 } 094 095 public static String getModelName() throws IOException, InterruptedException { 096 return getCpuInfo("model name"); 097 } 098 099 @Deprecated 100 public static String getBogoMIPS() throws IOException, InterruptedException { 101 return "UNKNOWN"; 102 } 103 104 public static String[] getCpuFeatures() throws IOException, InterruptedException { 105 return getCpuInfo("Features").split(" "); 106 } 107 108 public static String getCpuImplementer() throws IOException, InterruptedException { 109 return getCpuInfo("CPU implementer"); 110 } 111 112 public static String getCpuArchitecture() throws IOException, InterruptedException { 113 return getCpuInfo("CPU architecture"); 114 } 115 116 public static String getCpuVariant() throws IOException, InterruptedException { 117 return getCpuInfo("CPU variant"); 118 } 119 120 public static String getCpuPart() throws IOException, InterruptedException { 121 return getCpuInfo("CPU part"); 122 } 123 124 public static String getCpuRevision() throws IOException, InterruptedException { 125 return getCpuInfo("CPU revision"); 126 } 127 128 public static String getHardware() throws IOException, InterruptedException { 129 return getCpuInfo("Hardware"); 130 } 131 132 public static String getRevision() throws IOException, InterruptedException { 133 return getCpuInfo("Revision"); 134 } 135 136 public static String getSerial() throws IOException, InterruptedException { 137 return getCpuInfo("Serial"); 138 } 139 140 public static String getOsName() { 141 return System.getProperty("os.name"); 142 } 143 144 public static String getOsVersion() { 145 return System.getProperty("os.version"); 146 } 147 148 public static String getOsArch() { 149 return System.getProperty("os.arch"); 150 } 151 152 public static String getOsFirmwareBuild() throws IOException, InterruptedException { 153 String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd version"); 154 if(result != null){ 155 for(String line : result) { 156 if(line.startsWith("version ")){ 157 return line.substring(8); 158 } 159 } 160 } 161 throw new RuntimeException("Invalid command or response."); 162 } 163 164 public static String getOsFirmwareDate() throws IOException, InterruptedException, ParseException { 165 String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd version"); 166 if(result != null && result.length > 0){ 167 for(String line : result) { 168 return line; // return 1st line 169 } 170 } 171 throw new RuntimeException("Invalid command or response."); 172 } 173 174 public static String getJavaVendor() { 175 return System.getProperty("java.vendor"); 176 } 177 178 public static String getJavaVendorUrl() { 179 return System.getProperty("java.vendor.url"); 180 } 181 182 public static String getJavaVersion() { 183 return System.getProperty("java.version"); 184 } 185 186 public static String getJavaVirtualMachine() { 187 return System.getProperty("java.vm.name"); 188 } 189 190 public static String getJavaRuntime() { 191 return AccessController.doPrivileged(new PrivilegedAction<String>() { 192 public String run() { 193 return System.getProperty("java.runtime.name"); 194 } 195 }); 196 } 197 198 /* 199 * this method was partially derived from :: (project) jogamp / (developer) sgothel 200 * https://github.com/sgothel/gluegen/blob/master/src/java/jogamp/common/os/PlatformPropsImpl.java#L160 201 * https://github.com/sgothel/gluegen/blob/master/LICENSE.txt 202 */ 203 public static boolean isHardFloatAbi() { 204 205 return AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 206 private final String[] gnueabihf = new String[] { "gnueabihf", "armhf" }; 207 public Boolean run() { 208 return ( StringUtil.contains(System.getProperty("sun.boot.library.path"), gnueabihf) || 209 StringUtil.contains(System.getProperty("java.library.path"), gnueabihf) || 210 StringUtil.contains(System.getProperty("java.home"), gnueabihf) || 211 getBashVersionInfo().contains("gnueabihf") || 212 hasReadElfTag("Tag_ABI_HardFP_use")); 213 } } ); 214 } 215 216 217 private static List<Long> getMemory() throws IOException, InterruptedException { 218 // Memory information is in the form 219 // root@mypi:/home/pi# free -b 220 // total used free shared buffers cached 221 // Mem: 459771904 144654336 315117568 0 21319680 63713280 222 // -/+ buffers/cache: 59621376 400150528 223 // Swap: 104853504 0 104853504 224 List<Long> values = new ArrayList<>(); 225 String result[] = ExecUtil.execute("free -b"); 226 if(result != null){ 227 for(String line : result) { 228 if(line.startsWith("Mem:")){ 229 String parts[] = line.split(" "); 230 for(String part : parts){ 231 part = part.trim(); 232 if(!part.isEmpty() && !part.equalsIgnoreCase("Mem:")){ 233 values.add(new Long(part)); 234 } 235 } 236 } 237 } 238 } 239 return values; 240 } 241 242 public static long getMemoryTotal() throws IOException, InterruptedException { 243 List<Long> values = getMemory(); 244 if(!values.isEmpty() && values.size() > 0){ 245 return values.get(0); // total memory value is in first position 246 } 247 return -1; 248 } 249 250 public static long getMemoryUsed() throws IOException, InterruptedException { 251 List<Long> values = getMemory(); 252 if(!values.isEmpty() && values.size() > 1){ 253 return values.get(1); // used memory value is in second position 254 } 255 return -1; 256 } 257 258 public static long getMemoryFree() throws IOException, InterruptedException { 259 List<Long> values = getMemory(); 260 if(!values.isEmpty() && values.size() > 2){ 261 return values.get(2); // free memory value is in third position 262 } 263 return -1; 264 } 265 266 public static long getMemoryShared() throws IOException, InterruptedException { 267 List<Long> values = getMemory(); 268 if(!values.isEmpty() && values.size() > 3){ 269 return values.get(3); // shared memory value is in fourth position 270 } 271 return -1; 272 } 273 274 public static long getMemoryBuffers() throws IOException, InterruptedException { 275 List<Long> values = getMemory(); 276 if(!values.isEmpty() && values.size() > 4){ 277 return values.get(4); // buffers memory value is in fifth position 278 } 279 return -1; 280 } 281 282 public static long getMemoryCached() throws IOException, InterruptedException { 283 List<Long> values = getMemory(); 284 if(!values.isEmpty() && values.size() > 5){ 285 return values.get(5); // cached memory value is in sixth position 286 } 287 return -1; 288 } 289 290 public static BoardType getBoardType() throws IOException, InterruptedException 291 { 292 String revision = getRevision(); 293 long irevision = Long.parseLong(revision, 16); 294 long scheme = (irevision >> 23) & 0x1; 295 long ram = (irevision >> 20) & 0x7; 296 long manufacturer = (irevision >> 16) & 0xF; 297 long processor = (irevision >> 12) & 0xF; 298 long type = (irevision >> 4) & 0xFF; 299 long rev = irevision & 0xF; 300 301 // determine board type based on revision scheme 302 if (scheme == 0) { 303 // The following info obtained from: 304 // http://www.raspberrypi.org/archives/1929 305 // http://raspberryalphaomega.org.uk/?p=428 306 // http://www.raspberrypi.org/phpBB3/viewtopic.php?p=281039#p281039 307 // http://elinux.org/RPi_HardwareHistory 308 switch (revision) { 309 case "0002": // Model B Revision 1 310 case "0003": // Model B Revision 1 + Fuses mod and D14 removed 311 return BoardType.ModelB_Rev1; 312 case "0004": // Model B Revision 2 256MB (Sony) 313 case "0005": // Model B Revision 2 256MB (Qisda) 314 case "0006": // Model B Revision 2 256MB (Egoman) 315 return BoardType.ModelB_Rev2; 316 case "0007": // Model A 256MB (Egoman) 317 case "0008": // Model A 256MB (Sony) 318 case "0009": // Model A 256MB (Qisda) 319 return BoardType.ModelA_Rev1; 320 case "000d": // Model B Revision 2 512MB (Egoman) 321 case "000e": // Model B Revision 2 512MB (Sony) 322 case "000f": // Model B Revision 2 512MB (Qisda) 323 return BoardType.ModelB_Rev2; 324 case "0010": // Model B Plus 512MB (Sony) 325 { // Model 2B, Rev 1.1, Quad Core, 1GB (Sony) 326 if (getHardware().equalsIgnoreCase("BCM2709")) 327 return BoardType.Model2B_Rev1; 328 else 329 return BoardType.ModelB_Plus_Rev1; 330 } 331 case "0011": // Compute Module 512MB (Sony) 332 return BoardType.Compute_Module_Rev1; 333 case "0012": // Model A Plus 512MB (Sony) 334 return BoardType.ModelA_Plus_Rev1; 335 default: 336 return BoardType.UNKNOWN; 337 } 338 } 339 else if (type == 4) { 340 return SystemInfo.BoardType.Model2B_Rev1; 341 } 342 else { 343 return SystemInfo.BoardType.UNKNOWN; 344 } 345 } 346 347 public static float getCpuTemperature() throws IOException, InterruptedException, NumberFormatException { 348 // CPU temperature is in the form 349 // pi@mypi$ /opt/vc/bin/vcgencmd measure_temp 350 // temp=42.3'C 351 // Support for this was added around firmware version 3357xx per info 352 // at http://www.raspberrypi.org/phpBB3/viewtopic.php?p=169909#p169909 353 String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd measure_temp"); 354 if(result != null && result.length > 0){ 355 for(String line : result) { 356 String parts[] = line.split("[=']", 3); 357 return Float.parseFloat(parts[1]); 358 } 359 } 360 throw new RuntimeException("Invalid command or response."); 361 } 362 363 private static float getVoltage(String id) throws IOException, InterruptedException, NumberFormatException { 364 String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd measure_volts " + id); 365 if(result != null && result.length > 0){ 366 for(String line : result) { 367 String parts[] = line.split("[=V]", 3); 368 return Float.parseFloat(parts[1]); 369 } 370 } 371 throw new RuntimeException("Invalid command or response."); 372 } 373 374 public static float getCpuVoltage() throws IOException, InterruptedException, NumberFormatException { 375 return getVoltage("core"); 376 } 377 378 public static float getMemoryVoltageSDRam_C() throws IOException, InterruptedException, NumberFormatException { 379 return getVoltage("sdram_c"); 380 } 381 382 public static float getMemoryVoltageSDRam_I() throws IOException, InterruptedException, NumberFormatException { 383 return getVoltage("sdram_i"); 384 } 385 386 public static float getMemoryVoltageSDRam_P() throws IOException, InterruptedException, NumberFormatException { 387 return getVoltage("sdram_p"); 388 } 389 390 391 private static boolean getCodecEnabled(String codec) throws IOException, InterruptedException { 392 String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd codec_enabled " + codec); 393 if(result != null && result.length > 0){ 394 for(String line : result) { 395 String parts[] = line.split("=", 2); 396 return parts[1].trim().equalsIgnoreCase("enabled"); 397 } 398 } 399 throw new RuntimeException("Invalid command or response."); 400 } 401 402 public static boolean getCodecH264Enabled() throws IOException, InterruptedException { 403 return getCodecEnabled("H264"); 404 } 405 406 public static boolean getCodecMPG2Enabled() throws IOException, InterruptedException { 407 return getCodecEnabled("MPG2"); 408 } 409 410 public static boolean getCodecWVC1Enabled() throws IOException, InterruptedException { 411 return getCodecEnabled("WVC1"); 412 } 413 414 415 private static long getClockFrequency(String target) throws IOException, InterruptedException { 416 String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd measure_clock " + target.trim()); 417 if(result != null && result.length > 0){ 418 for(String line : result) { 419 String parts[] = line.split("=", 2); 420 return Long.parseLong(parts[1].trim()); 421 } 422 } 423 throw new RuntimeException("Invalid command or response."); 424 } 425 426 public static long getClockFrequencyArm() throws IOException, InterruptedException { 427 return getClockFrequency("arm"); 428 } 429 430 public static long getClockFrequencyCore() throws IOException, InterruptedException { 431 return getClockFrequency("core"); 432 } 433 434 public static long getClockFrequencyH264() throws IOException, InterruptedException { 435 return getClockFrequency("h264"); 436 } 437 438 public static long getClockFrequencyISP() throws IOException, InterruptedException { 439 return getClockFrequency("isp"); 440 } 441 442 public static long getClockFrequencyV3D() throws IOException, InterruptedException { 443 return getClockFrequency("v3d"); 444 } 445 446 public static long getClockFrequencyUART() throws IOException, InterruptedException { 447 return getClockFrequency("uart"); 448 } 449 450 public static long getClockFrequencyPWM() throws IOException, InterruptedException { 451 return getClockFrequency("pwm"); 452 } 453 454 public static long getClockFrequencyEMMC() throws IOException, InterruptedException { 455 return getClockFrequency("emmc"); 456 } 457 458 public static long getClockFrequencyPixel() throws IOException, InterruptedException { 459 return getClockFrequency("pixel"); 460 } 461 462 public static long getClockFrequencyVEC() throws IOException, InterruptedException { 463 return getClockFrequency("vec"); 464 } 465 466 public static long getClockFrequencyHDMI() throws IOException, InterruptedException { 467 return getClockFrequency("hdmi"); 468 } 469 470 public static long getClockFrequencyDPI() throws IOException, InterruptedException { 471 return getClockFrequency("dpi"); 472 } 473 474 475 /* 476 * this method will to obtain the version info string from the 'bash' program 477 * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system) 478 */ 479 private static String getBashVersionInfo() { 480 String versionInfo = ""; 481 try { 482 String result[] = ExecUtil.execute("bash --version"); 483 for(String line : result) { 484 if(!line.isEmpty()) { 485 versionInfo = line; // return only first output line of version info 486 break; 487 } 488 } 489 } 490 catch (IOException|InterruptedException ioe) { ioe.printStackTrace(); } 491 return versionInfo; 492 } 493 494 /* 495 * this method will determine if a specified tag exists from the elf info in the '/proc/self/exe' program 496 * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system) 497 */ 498 private static boolean hasReadElfTag(String tag) { 499 String tagValue = getReadElfTag(tag); 500 return (tagValue != null && !tagValue.isEmpty()); 501 } 502 503 /* 504 * this method will obtain a specified tag value from the elf info in the '/proc/self/exe' program 505 * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system) 506 */ 507 private static String getReadElfTag(String tag) { 508 String tagValue = null; 509 try { 510 String result[] = ExecUtil.execute("/usr/bin/readelf -A /proc/self/exe"); 511 if(result != null){ 512 for(String line : result) { 513 line = line.trim(); 514 if (line.startsWith(tag) && line.contains(":")) { 515 String lineParts[] = line.split(":", 2); 516 if(lineParts.length > 1) 517 tagValue = lineParts[1].trim(); 518 break; 519 } 520 } 521 } 522 } 523 catch (IOException|InterruptedException ioe) { ioe.printStackTrace(); } 524 return tagValue; 525 } 526}