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 android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.hardware.health.BatteryHealthData; 22 import android.hardware.health.HealthInfo; 23 import android.hardware.health.IHealth; 24 import android.os.BatteryManager; 25 import android.os.BatteryProperty; 26 import android.os.Binder; 27 import android.os.HandlerThread; 28 import android.os.IBinder; 29 import android.os.IServiceCallback; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.os.ServiceSpecificException; 33 import android.os.Trace; 34 import android.util.Slog; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 38 import java.util.NoSuchElementException; 39 import java.util.Objects; 40 import java.util.concurrent.atomic.AtomicReference; 41 42 /** 43 * Implement {@link HealthServiceWrapper} backed by the AIDL HAL. 44 * 45 * @hide 46 */ 47 class HealthServiceWrapperAidl extends HealthServiceWrapper { 48 private static final String TAG = "HealthServiceWrapperAidl"; 49 @VisibleForTesting static final String SERVICE_NAME = IHealth.DESCRIPTOR + "/default"; 50 private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceBinder"); 51 private final AtomicReference<IHealth> mLastService = new AtomicReference<>(); 52 private final IServiceCallback mServiceCallback = new ServiceCallback(); 53 private final HealthRegCallbackAidl mRegCallback; 54 55 /** Stub interface into {@link ServiceManager} for testing. */ 56 interface ServiceManagerStub { waitForDeclaredService(@onNull String name)57 default @Nullable IHealth waitForDeclaredService(@NonNull String name) { 58 return IHealth.Stub.asInterface(ServiceManager.waitForDeclaredService(name)); 59 } 60 registerForNotifications( @onNull String name, @NonNull IServiceCallback callback)61 default void registerForNotifications( 62 @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException { 63 ServiceManager.registerForNotifications(name, callback); 64 } 65 } 66 HealthServiceWrapperAidl( @ullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager)67 HealthServiceWrapperAidl( 68 @Nullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager) 69 throws RemoteException, NoSuchElementException { 70 71 traceBegin("HealthInitGetServiceAidl"); 72 IHealth newService; 73 try { 74 newService = serviceManager.waitForDeclaredService(SERVICE_NAME); 75 } finally { 76 traceEnd(); 77 } 78 if (newService == null) { 79 throw new NoSuchElementException( 80 "IHealth service instance isn't available. Perhaps no permission?"); 81 } 82 mLastService.set(newService); 83 mRegCallback = regCallback; 84 if (mRegCallback != null) { 85 mRegCallback.onRegistration(null /* oldService */, newService); 86 } 87 88 traceBegin("HealthInitRegisterNotificationAidl"); 89 mHandlerThread.start(); 90 try { 91 serviceManager.registerForNotifications(SERVICE_NAME, mServiceCallback); 92 } finally { 93 traceEnd(); 94 } 95 Slog.i(TAG, "health: HealthServiceWrapper listening to AIDL HAL"); 96 } 97 98 @Override 99 @VisibleForTesting getHandlerThread()100 public HandlerThread getHandlerThread() { 101 return mHandlerThread; 102 } 103 104 @Override getProperty(int id, BatteryProperty prop)105 public int getProperty(int id, BatteryProperty prop) throws RemoteException { 106 traceBegin("HealthGetPropertyAidl"); 107 try { 108 return getPropertyInternal(id, prop); 109 } finally { 110 traceEnd(); 111 } 112 } 113 getPropertyInternal(int id, BatteryProperty prop)114 private int getPropertyInternal(int id, BatteryProperty prop) throws RemoteException { 115 IHealth service = mLastService.get(); 116 if (service == null) throw new RemoteException("no health service"); 117 BatteryHealthData healthData; 118 try { 119 switch (id) { 120 case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: 121 prop.setLong(service.getChargeCounterUah()); 122 break; 123 case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW: 124 prop.setLong(service.getCurrentNowMicroamps()); 125 break; 126 case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE: 127 prop.setLong(service.getCurrentAverageMicroamps()); 128 break; 129 case BatteryManager.BATTERY_PROPERTY_CAPACITY: 130 prop.setLong(service.getCapacity()); 131 break; 132 case BatteryManager.BATTERY_PROPERTY_STATUS: 133 prop.setLong(service.getChargeStatus()); 134 break; 135 case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER: 136 prop.setLong(service.getEnergyCounterNwh()); 137 break; 138 case BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE: 139 healthData = service.getBatteryHealthData(); 140 prop.setLong(healthData.batteryManufacturingDateSeconds); 141 break; 142 case BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE: 143 healthData = service.getBatteryHealthData(); 144 prop.setLong(healthData.batteryFirstUsageSeconds); 145 break; 146 case BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY: 147 prop.setLong(service.getChargingPolicy()); 148 break; 149 case BatteryManager.BATTERY_PROPERTY_STATE_OF_HEALTH: 150 healthData = service.getBatteryHealthData(); 151 prop.setLong(healthData.batteryStateOfHealth); 152 break; 153 } 154 } catch (UnsupportedOperationException e) { 155 // Leave prop untouched. 156 return -1; 157 } catch (ServiceSpecificException e) { 158 // Leave prop untouched. 159 return -2; 160 } 161 // throws RemoteException as-is. BatteryManager wraps it into a RuntimeException 162 // and throw it to apps. 163 164 // If no error, return 0. 165 return 0; 166 } 167 168 @Override scheduleUpdate()169 public void scheduleUpdate() throws RemoteException { 170 getHandlerThread() 171 .getThreadHandler() 172 .post( 173 () -> { 174 traceBegin("HealthScheduleUpdate"); 175 try { 176 IHealth service = mLastService.get(); 177 if (service == null) { 178 Slog.e(TAG, "no health service"); 179 return; 180 } 181 service.update(); 182 } catch (RemoteException | ServiceSpecificException ex) { 183 Slog.e(TAG, "Cannot call update on health AIDL HAL", ex); 184 } finally { 185 traceEnd(); 186 } 187 }); 188 } 189 190 @Override getHealthInfo()191 public HealthInfo getHealthInfo() throws RemoteException { 192 IHealth service = mLastService.get(); 193 if (service == null) return null; 194 try { 195 return service.getHealthInfo(); 196 } catch (UnsupportedOperationException | ServiceSpecificException ex) { 197 return null; 198 } 199 } 200 traceBegin(String name)201 private static void traceBegin(String name) { 202 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name); 203 } 204 traceEnd()205 private static void traceEnd() { 206 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); 207 } 208 209 private class ServiceCallback extends IServiceCallback.Stub { 210 @Override onRegistration(String name, @NonNull final IBinder newBinder)211 public void onRegistration(String name, @NonNull final IBinder newBinder) 212 throws RemoteException { 213 if (!SERVICE_NAME.equals(name)) return; 214 // This runnable only runs on mHandlerThread and ordering is ensured, hence 215 // no locking is needed inside the runnable. 216 getHandlerThread() 217 .getThreadHandler() 218 .post( 219 () -> { 220 IHealth newService = 221 IHealth.Stub.asInterface(Binder.allowBlocking(newBinder)); 222 IHealth oldService = mLastService.getAndSet(newService); 223 IBinder oldBinder = 224 oldService != null ? oldService.asBinder() : null; 225 if (Objects.equals(newBinder, oldBinder)) return; 226 227 Slog.i(TAG, "New health AIDL HAL service registered"); 228 if (mRegCallback != null) { 229 mRegCallback.onRegistration(oldService, newService); 230 } 231 }); 232 } 233 } 234 } 235