View Javadoc
1   /*
2    * Copyright 2016-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.platform.mac;
6   
7   import java.time.LocalDate;
8   import java.util.ArrayList;
9   import java.util.List;
10  
11  import com.sun.jna.Pointer;
12  import com.sun.jna.platform.mac.CoreFoundation;
13  import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef;
14  import com.sun.jna.platform.mac.CoreFoundation.CFBooleanRef;
15  import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef;
16  import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef;
17  import com.sun.jna.platform.mac.CoreFoundation.CFStringRef;
18  import com.sun.jna.platform.mac.CoreFoundation.CFTypeRef;
19  import com.sun.jna.platform.mac.IOKit;
20  import com.sun.jna.platform.mac.IOKit.IORegistryEntry;
21  import com.sun.jna.platform.mac.IOKitUtil;
22  
23  import oshi.annotation.concurrent.ThreadSafe;
24  import oshi.hardware.PowerSource;
25  import oshi.hardware.common.AbstractPowerSource;
26  import oshi.util.Constants;
27  import oshi.util.platform.mac.CFUtil;
28  
29  /**
30   * A Power Source
31   */
32  @ThreadSafe
33  public final class MacPowerSource extends AbstractPowerSource {
34  
35      private static final CoreFoundation CF = CoreFoundation.INSTANCE;
36      private static final IOKit IO = IOKit.INSTANCE;
37  
38      public MacPowerSource(String psName, String psDeviceName, double psRemainingCapacityPercent,
39              double psTimeRemainingEstimated, double psTimeRemainingInstant, double psPowerUsageRate, double psVoltage,
40              double psAmperage, boolean psPowerOnLine, boolean psCharging, boolean psDischarging,
41              CapacityUnits psCapacityUnits, int psCurrentCapacity, int psMaxCapacity, int psDesignCapacity,
42              int psCycleCount, String psChemistry, LocalDate psManufactureDate, String psManufacturer,
43              String psSerialNumber, double psTemperature) {
44          super(psName, psDeviceName, psRemainingCapacityPercent, psTimeRemainingEstimated, psTimeRemainingInstant,
45                  psPowerUsageRate, psVoltage, psAmperage, psPowerOnLine, psCharging, psDischarging, psCapacityUnits,
46                  psCurrentCapacity, psMaxCapacity, psDesignCapacity, psCycleCount, psChemistry, psManufactureDate,
47                  psManufacturer, psSerialNumber, psTemperature);
48      }
49  
50      /**
51       * Gets Battery Information.
52       *
53       * @return An array of PowerSource objects representing batteries, etc.
54       */
55      public static List<PowerSource> getPowerSources() {
56          String psDeviceName = Constants.UNKNOWN;
57          double psTimeRemainingInstant = 0d;
58          double psPowerUsageRate = 0d;
59          double psVoltage = -1d;
60          double psAmperage = 0d;
61          boolean psPowerOnLine = false;
62          boolean psCharging = false;
63          boolean psDischarging = false;
64          CapacityUnits psCapacityUnits = CapacityUnits.RELATIVE;
65          int psCurrentCapacity = 0;
66          int psMaxCapacity = 1;
67          int psDesignCapacity = 1;
68          int psCycleCount = -1;
69          String psChemistry = Constants.UNKNOWN;
70          LocalDate psManufactureDate = null;
71          String psManufacturer = Constants.UNKNOWN;
72          String psSerialNumber = Constants.UNKNOWN;
73          double psTemperature = 0d;
74  
75          // Mac PowerSource information comes from two sources: the IOKit's IOPS
76          // functions (which, in theory, return an array of objects but in most cases
77          // should return one), and the IORegistry's entry for AppleSmartBattery, which
78          // always returns one object.
79          //
80          // We start by fetching the registry information, which will be replicated
81          // across all IOPS entries if there are more than one.
82  
83          IORegistryEntry smartBattery = IOKitUtil.getMatchingService("AppleSmartBattery");
84          if (smartBattery != null) {
85              String s = smartBattery.getStringProperty("DeviceName");
86              if (s != null) {
87                  psDeviceName = s;
88              }
89              s = smartBattery.getStringProperty("Manufacturer");
90              if (s != null) {
91                  psManufacturer = s;
92              }
93              s = smartBattery.getStringProperty("BatterySerialNumber");
94              if (s != null) {
95                  psSerialNumber = s;
96              }
97  
98              Integer temp = smartBattery.getIntegerProperty("ManufactureDate");
99              if (temp != null) {
100                 // Bits 0...4 => day (value 1-31; 5 bits)
101                 // Bits 5...8 => month (value 1-12; 4 bits)
102                 // Bits 9...15 => years since 1980 (value 0-127; 7 bits)
103                 int day = temp & 0x1f;
104                 int month = (temp >> 5) & 0xf;
105                 int year80 = (temp >> 9) & 0x7f;
106                 psManufactureDate = LocalDate.of(1980 + year80, month, day);
107             }
108 
109             temp = smartBattery.getIntegerProperty("DesignCapacity");
110             if (temp != null) {
111                 psDesignCapacity = temp;
112             }
113             temp = smartBattery.getIntegerProperty("MaxCapacity");
114             if (temp != null) {
115                 psMaxCapacity = temp;
116             }
117             temp = smartBattery.getIntegerProperty("CurrentCapacity");
118             if (temp != null) {
119                 psCurrentCapacity = temp;
120             }
121             psCapacityUnits = CapacityUnits.MAH;
122 
123             temp = smartBattery.getIntegerProperty("TimeRemaining");
124             if (temp != null) {
125                 psTimeRemainingInstant = temp * 60d;
126             }
127             temp = smartBattery.getIntegerProperty("CycleCount");
128             if (temp != null) {
129                 psCycleCount = temp;
130             }
131             temp = smartBattery.getIntegerProperty("Temperature");
132             if (temp != null) {
133                 psTemperature = temp / 100d;
134             }
135             temp = smartBattery.getIntegerProperty("Voltage");
136             if (temp != null) {
137                 psVoltage = temp / 1000d;
138             }
139             temp = smartBattery.getIntegerProperty("Amperage");
140             if (temp != null) {
141                 psAmperage = temp;
142             }
143             psPowerUsageRate = psVoltage * psAmperage;
144 
145             Boolean bool = smartBattery.getBooleanProperty("ExternalConnected");
146             if (bool != null) {
147                 psPowerOnLine = bool;
148             }
149             bool = smartBattery.getBooleanProperty("IsCharging");
150             if (bool != null) {
151                 psCharging = bool;
152             }
153             psDischarging = !psCharging;
154 
155             smartBattery.release();
156         }
157 
158         // Get the blob containing current power source state
159         CFTypeRef powerSourcesInfo = IO.IOPSCopyPowerSourcesInfo();
160         CFArrayRef powerSourcesList = IO.IOPSCopyPowerSourcesList(powerSourcesInfo);
161         int powerSourcesCount = powerSourcesList.getCount();
162 
163         // Get time remaining
164         // -1 = unknown, -2 = unlimited
165         double psTimeRemainingEstimated = IO.IOPSGetTimeRemainingEstimate();
166 
167         CFStringRef nameKey = CFStringRef.createCFString("Name");
168         CFStringRef isPresentKey = CFStringRef.createCFString("Is Present");
169         CFStringRef currentCapacityKey = CFStringRef.createCFString("Current Capacity");
170         CFStringRef maxCapacityKey = CFStringRef.createCFString("Max Capacity");
171         // For each power source, output various info
172         List<PowerSource> psList = new ArrayList<>(powerSourcesCount);
173         for (int ps = 0; ps < powerSourcesCount; ps++) {
174             // Get the dictionary for that Power Source
175             Pointer pwrSrcPtr = powerSourcesList.getValueAtIndex(ps);
176             CFTypeRef powerSource = new CFTypeRef();
177             powerSource.setPointer(pwrSrcPtr);
178             CFDictionaryRef dictionary = IO.IOPSGetPowerSourceDescription(powerSourcesInfo, powerSource);
179 
180             // Get values from dictionary (See IOPSKeys.h)
181             // Skip if not present
182             Pointer result = dictionary.getValue(isPresentKey);
183             if (result != null) {
184                 CFBooleanRef isPresentRef = new CFBooleanRef(result);
185                 if (0 != CF.CFBooleanGetValue(isPresentRef)) {
186                     // Get name
187                     result = dictionary.getValue(nameKey);
188                     String psName = CFUtil.cfPointerToString(result);
189                     // Remaining Capacity = current / max
190                     double currentCapacity = 0d;
191                     if (dictionary.getValueIfPresent(currentCapacityKey, null)) {
192                         result = dictionary.getValue(currentCapacityKey);
193                         CFNumberRef cap = new CFNumberRef(result);
194                         currentCapacity = cap.intValue();
195                     }
196                     double maxCapacity = 1d;
197                     if (dictionary.getValueIfPresent(maxCapacityKey, null)) {
198                         result = dictionary.getValue(maxCapacityKey);
199                         CFNumberRef cap = new CFNumberRef(result);
200                         maxCapacity = cap.intValue();
201                     }
202                     double psRemainingCapacityPercent = Math.min(1d, currentCapacity / maxCapacity);
203                     // Add to list
204                     psList.add(new MacPowerSource(psName, psDeviceName, psRemainingCapacityPercent,
205                             psTimeRemainingEstimated, psTimeRemainingInstant, psPowerUsageRate, psVoltage, psAmperage,
206                             psPowerOnLine, psCharging, psDischarging, psCapacityUnits, psCurrentCapacity, psMaxCapacity,
207                             psDesignCapacity, psCycleCount, psChemistry, psManufactureDate, psManufacturer,
208                             psSerialNumber, psTemperature));
209                 }
210             }
211         }
212         isPresentKey.release();
213         nameKey.release();
214         currentCapacityKey.release();
215         maxCapacityKey.release();
216         // Release the blob
217         powerSourcesList.release();
218         powerSourcesInfo.release();
219 
220         return psList;
221     }
222 }