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