1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.health; 18 19 import static android.os.Flags.batteryPartStatusApi; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.hardware.health.BatteryHealthData; 24 import android.hardware.health.HealthInfo; 25 import android.hardware.health.IHealth; 26 import android.os.BatteryManager; 27 import android.os.BatteryProperty; 28 import android.os.Binder; 29 import android.os.HandlerThread; 30 import android.os.IBinder; 31 import android.os.IServiceCallback; 32 import android.os.RemoteException; 33 import android.os.ServiceManager; 34 import android.os.ServiceSpecificException; 35 import android.os.Trace; 36 import android.util.Slog; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 40 import java.util.NoSuchElementException; 41 import java.util.Objects; 42 import java.util.concurrent.atomic.AtomicReference; 43 44 /** 45 * Implement {@link HealthServiceWrapper} backed by the AIDL HAL. 46 * 47 * @hide 48 */ 49 class HealthServiceWrapperAidl extends HealthServiceWrapper { 50 private static final String TAG = "HealthServiceWrapperAidl"; 51 @VisibleForTesting static final String SERVICE_NAME = IHealth.DESCRIPTOR + "/default"; 52 private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceBinder"); 53 private final AtomicReference<IHealth> mLastService = new AtomicReference<>(); 54 private final IServiceCallback mServiceCallback = new ServiceCallback(); 55 private final HealthRegCallbackAidl mRegCallback; 56 57 /** Stub interface into {@link ServiceManager} for testing. */ 58 interface ServiceManagerStub { waitForDeclaredService(@onNull String name)59 default @Nullable IHealth waitForDeclaredService(@NonNull String name) { 60 return IHealth.Stub.asInterface(ServiceManager.waitForDeclaredService(name)); 61 } 62 registerForNotifications( @onNull String name, @NonNull IServiceCallback callback)63 default void registerForNotifications( 64 @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException { 65 ServiceManager.registerForNotifications(name, callback); 66 } 67 } 68 HealthServiceWrapperAidl( @ullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager)69 HealthServiceWrapperAidl( 70 @Nullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager) 71 throws RemoteException, NoSuchElementException { 72 73 traceBegin("HealthInitGetServiceAidl"); 74 IHealth newService; 75 try { 76 newService = serviceManager.waitForDeclaredService(SERVICE_NAME); 77 } finally { 78 traceEnd(); 79 } 80 if (newService == null) { 81 throw new NoSuchElementException( 82 "IHealth service instance isn't available. Perhaps no permission?"); 83 } 84 mLastService.set(newService); 85 mRegCallback = regCallback; 86 if (mRegCallback != null) { 87 mRegCallback.onRegistration(null /* oldService */, newService); 88 } 89 90 traceBegin("HealthInitRegisterNotificationAidl"); 91 mHandlerThread.start(); 92 try { 93 serviceManager.registerForNotifications(SERVICE_NAME, mServiceCallback); 94 } finally { 95 traceEnd(); 96 } 97 Slog.i(TAG, "health: HealthServiceWrapper listening to AIDL HAL"); 98 } 99 100 @Override 101 @VisibleForTesting getHandlerThread()102 public HandlerThread getHandlerThread() { 103 return mHandlerThread; 104 } 105 106 @Override getProperty(int id, BatteryProperty prop)107 public int getProperty(int id, BatteryProperty prop) throws RemoteException { 108 traceBegin("HealthGetPropertyAidl"); 109 try { 110 return getPropertyInternal(id, prop); 111 } finally { 112 traceEnd(); 113 } 114 } 115 getPropertyInternal(int id, BatteryProperty prop)116 private int getPropertyInternal(int id, BatteryProperty prop) throws RemoteException { 117 IHealth service = mLastService.get(); 118 if (service == null) throw new RemoteException("no health service"); 119 BatteryHealthData healthData; 120 try { 121 switch (id) { 122 case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: 123 prop.setLong(service.getChargeCounterUah()); 124 break; 125 case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW: 126 prop.setLong(service.getCurrentNowMicroamps()); 127 break; 128 case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE: 129 prop.setLong(service.getCurrentAverageMicroamps()); 130 break; 131 case BatteryManager.BATTERY_PROPERTY_CAPACITY: 132 prop.setLong(service.getCapacity()); 133 break; 134 case BatteryManager.BATTERY_PROPERTY_STATUS: 135 prop.setLong(service.getChargeStatus()); 136 break; 137 case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER: 138 prop.setLong(service.getEnergyCounterNwh()); 139 break; 140 case BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE: 141 healthData = service.getBatteryHealthData(); 142 prop.setLong(healthData.batteryManufacturingDateSeconds); 143 break; 144 case BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE: 145 healthData = service.getBatteryHealthData(); 146 prop.setLong(healthData.batteryFirstUsageSeconds); 147 break; 148 case BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY: 149 prop.setLong(service.getChargingPolicy()); 150 break; 151 case BatteryManager.BATTERY_PROPERTY_STATE_OF_HEALTH: 152 healthData = service.getBatteryHealthData(); 153 prop.setLong(healthData.batteryStateOfHealth); 154 break; 155 case BatteryManager.BATTERY_PROPERTY_SERIAL_NUMBER: 156 if (batteryPartStatusApi()) { 157 healthData = service.getBatteryHealthData(); 158 prop.setString(healthData.batterySerialNumber); 159 } 160 break; 161 case BatteryManager.BATTERY_PROPERTY_PART_STATUS: 162 if (batteryPartStatusApi()) { 163 healthData = service.getBatteryHealthData(); 164 prop.setLong(healthData.batteryPartStatus); 165 } 166 break; 167 } 168 } catch (UnsupportedOperationException e) { 169 // Leave prop untouched. 170 return -1; 171 } catch (ServiceSpecificException e) { 172 // Leave prop untouched. 173 return -2; 174 } 175 // throws RemoteException as-is. BatteryManager wraps it into a RuntimeException 176 // and throw it to apps. 177 178 // If no error, return 0. 179 return 0; 180 } 181 182 @Override scheduleUpdate()183 public void scheduleUpdate() throws RemoteException { 184 getHandlerThread() 185 .getThreadHandler() 186 .post( 187 () -> { 188 traceBegin("HealthScheduleUpdate"); 189 try { 190 IHealth service = mLastService.get(); 191 if (service == null) { 192 Slog.e(TAG, "no health service"); 193 return; 194 } 195 service.update(); 196 } catch (RemoteException | ServiceSpecificException ex) { 197 Slog.e(TAG, "Cannot call update on health AIDL HAL", ex); 198 } finally { 199 traceEnd(); 200 } 201 }); 202 } 203 204 @Override getHealthInfo()205 public HealthInfo getHealthInfo() throws RemoteException { 206 IHealth service = mLastService.get(); 207 if (service == null) return null; 208 try { 209 return service.getHealthInfo(); 210 } catch (UnsupportedOperationException | ServiceSpecificException ex) { 211 return null; 212 } 213 } 214 setChargingPolicy(int policy)215 public void setChargingPolicy(int policy) throws RemoteException { 216 IHealth service = mLastService.get(); 217 if (service == null) return; 218 try { 219 service.setChargingPolicy(policy); 220 } catch (UnsupportedOperationException | ServiceSpecificException ex) { 221 return; 222 } 223 } 224 traceBegin(String name)225 private static void traceBegin(String name) { 226 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name); 227 } 228 traceEnd()229 private static void traceEnd() { 230 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); 231 } 232 233 private class ServiceCallback extends IServiceCallback.Stub { 234 @Override onRegistration(String name, @NonNull final IBinder newBinder)235 public void onRegistration(String name, @NonNull final IBinder newBinder) 236 throws RemoteException { 237 if (!SERVICE_NAME.equals(name)) return; 238 // This runnable only runs on mHandlerThread and ordering is ensured, hence 239 // no locking is needed inside the runnable. 240 getHandlerThread() 241 .getThreadHandler() 242 .post( 243 () -> { 244 IHealth newService = 245 IHealth.Stub.asInterface(Binder.allowBlocking(newBinder)); 246 IHealth oldService = mLastService.getAndSet(newService); 247 IBinder oldBinder = 248 oldService != null ? oldService.asBinder() : null; 249 if (Objects.equals(newBinder, oldBinder)) return; 250 251 Slog.i(TAG, "New health AIDL HAL service registered"); 252 if (mRegCallback != null) { 253 mRegCallback.onRegistration(oldService, newService); 254 } 255 }); 256 } 257 } 258 } 259