Merge changes I044d0363,I0be3b802,Ib584ea57,I92291023
* changes:
bpf is required so /sys/fs/bpf/map_netd_app_uid_stats_map always exists
bpf is required - remove mUseBpfTrafficStats field
bpf is required - remove checkBpfStatsEnable()
remove qtaguid parsing
diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
new file mode 100644
index 0000000..629011a
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.health.HealthInfo;
+import android.hardware.health.IHealth;
+import android.hardware.health.IHealthInfoCallback;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * On service registration, {@link #onRegistration} is called, which registers {@code this}, an
+ * {@link IHealthInfoCallback}, to the health service.
+ *
+ * <p>When the health service has updates to health info via {@link IHealthInfoCallback}, {@link
+ * HealthInfoCallback#update} is called.
+ *
+ * <p>AIDL variant of {@link HealthHalCallbackHidl}.
+ *
+ * @hide
+ */
+// It is made public so Mockito can access this class. It should have been package private if not
+// for testing.
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class HealthRegCallbackAidl {
+ private static final String TAG = "HealthRegCallbackAidl";
+ private final HealthInfoCallback mServiceInfoCallback;
+ private final IHealthInfoCallback mHalInfoCallback = new HalInfoCallback();
+
+ HealthRegCallbackAidl(@Nullable HealthInfoCallback healthInfoCallback) {
+ mServiceInfoCallback = healthInfoCallback;
+ }
+
+ /**
+ * Called when the service manager sees {@code newService} replacing {@code oldService}.
+ * This unregisters the health info callback from the old service (ignoring errors), then
+ * registers the health info callback to the new service.
+ *
+ * @param oldService the old IHealth service
+ * @param newService the new IHealth service
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void onRegistration(@Nullable IHealth oldService, @NonNull IHealth newService) {
+ if (mServiceInfoCallback == null) return;
+
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthUnregisterCallbackAidl");
+ try {
+ unregisterCallback(oldService, mHalInfoCallback);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthRegisterCallbackAidl");
+ try {
+ registerCallback(newService, mHalInfoCallback);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+
+ private static void unregisterCallback(@Nullable IHealth oldService, IHealthInfoCallback cb) {
+ if (oldService == null) return;
+ try {
+ oldService.unregisterCallback(cb);
+ } catch (RemoteException e) {
+ // Ignore errors. The service might have died.
+ Slog.w(
+ TAG,
+ "health: cannot unregister previous callback (transaction error): "
+ + e.getMessage());
+ }
+ }
+
+ private static void registerCallback(@NonNull IHealth newService, IHealthInfoCallback cb) {
+ try {
+ newService.registerCallback(cb);
+ } catch (RemoteException e) {
+ Slog.e(
+ TAG,
+ "health: cannot register callback, framework may cease to"
+ + " receive updates on health / battery info!",
+ e);
+ return;
+ }
+ // registerCallback does NOT guarantee that update is called immediately, so request a
+ // manual update here.
+ try {
+ newService.update();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "health: cannot update after registering health info callback", e);
+ }
+ }
+
+ private class HalInfoCallback extends IHealthInfoCallback.Stub {
+ @Override
+ public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException {
+ mServiceInfoCallback.update(healthInfo);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java
index 9b97554..25d1a88 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapper.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java
@@ -81,6 +81,8 @@
public static HealthServiceWrapper create(@Nullable HealthInfoCallback healthInfoCallback)
throws RemoteException, NoSuchElementException {
return create(
+ healthInfoCallback == null ? null : new HealthRegCallbackAidl(healthInfoCallback),
+ new HealthServiceWrapperAidl.ServiceManagerStub() {},
healthInfoCallback == null ? null : new HealthHalCallbackHidl(healthInfoCallback),
new HealthServiceWrapperHidl.IServiceManagerSupplier() {},
new HealthServiceWrapperHidl.IHealthSupplier() {});
@@ -89,6 +91,9 @@
/**
* Create a new HealthServiceWrapper instance for testing.
*
+ * @param aidlRegCallback callback for AIDL service registration, or {@code null} if the client
+ * does not care about AIDL service registration notifications
+ * @param aidlServiceManager Stub for AIDL ServiceManager
* @param hidlRegCallback callback for HIDL service registration, or {@code null} if the client
* does not care about HIDL service registration notifications
* @param hidlServiceManagerSupplier supplier of HIDL service manager
@@ -97,10 +102,17 @@
*/
@VisibleForTesting
static @NonNull HealthServiceWrapper create(
+ @Nullable HealthRegCallbackAidl aidlRegCallback,
+ @NonNull HealthServiceWrapperAidl.ServiceManagerStub aidlServiceManager,
@Nullable HealthServiceWrapperHidl.Callback hidlRegCallback,
@NonNull HealthServiceWrapperHidl.IServiceManagerSupplier hidlServiceManagerSupplier,
@NonNull HealthServiceWrapperHidl.IHealthSupplier hidlHealthSupplier)
throws RemoteException, NoSuchElementException {
+ try {
+ return new HealthServiceWrapperAidl(aidlRegCallback, aidlServiceManager);
+ } catch (NoSuchElementException e) {
+ // Ignore, try HIDL
+ }
return new HealthServiceWrapperHidl(
hidlRegCallback, hidlServiceManagerSupplier, hidlHealthSupplier);
}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
new file mode 100644
index 0000000..4f2ed68
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.health.HealthInfo;
+import android.hardware.health.IHealth;
+import android.os.BatteryManager;
+import android.os.BatteryProperty;
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.IServiceCallback;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.os.Trace;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Implement {@link HealthServiceWrapper} backed by the AIDL HAL.
+ *
+ * @hide
+ */
+class HealthServiceWrapperAidl extends HealthServiceWrapper {
+ private static final String TAG = "HealthServiceWrapperAidl";
+ @VisibleForTesting static final String SERVICE_NAME = IHealth.DESCRIPTOR + "/default";
+ private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceBinder");
+ private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
+ private final IServiceCallback mServiceCallback = new ServiceCallback();
+ private final HealthRegCallbackAidl mRegCallback;
+
+ /** Stub interface into {@link ServiceManager} for testing. */
+ interface ServiceManagerStub {
+ default @Nullable IHealth waitForDeclaredService(@NonNull String name) {
+ return IHealth.Stub.asInterface(ServiceManager.waitForDeclaredService(name));
+ }
+
+ default void registerForNotifications(
+ @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException {
+ ServiceManager.registerForNotifications(name, callback);
+ }
+ }
+
+ HealthServiceWrapperAidl(
+ @Nullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager)
+ throws RemoteException, NoSuchElementException {
+
+ traceBegin("HealthInitGetServiceAidl");
+ IHealth newService;
+ try {
+ newService = serviceManager.waitForDeclaredService(SERVICE_NAME);
+ } finally {
+ traceEnd();
+ }
+ if (newService == null) {
+ throw new NoSuchElementException(
+ "IHealth service instance isn't available. Perhaps no permission?");
+ }
+ mLastService.set(newService);
+ mRegCallback = regCallback;
+ if (mRegCallback != null) {
+ mRegCallback.onRegistration(null /* oldService */, newService);
+ }
+
+ traceBegin("HealthInitRegisterNotificationAidl");
+ mHandlerThread.start();
+ try {
+ serviceManager.registerForNotifications(SERVICE_NAME, mServiceCallback);
+ } finally {
+ traceEnd();
+ }
+ Slog.i(TAG, "health: HealthServiceWrapper listening to AIDL HAL");
+ }
+
+ @Override
+ @VisibleForTesting
+ public HandlerThread getHandlerThread() {
+ return mHandlerThread;
+ }
+
+ @Override
+ public int getProperty(int id, BatteryProperty prop) throws RemoteException {
+ traceBegin("HealthGetPropertyAidl");
+ try {
+ return getPropertyInternal(id, prop);
+ } finally {
+ traceEnd();
+ }
+ }
+
+ private int getPropertyInternal(int id, BatteryProperty prop) throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) throw new RemoteException("no health service");
+ try {
+ switch (id) {
+ case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
+ prop.setLong(service.getChargeCounterUah());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
+ prop.setLong(service.getCurrentNowMicroamps());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+ prop.setLong(service.getCurrentAverageMicroamps());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+ prop.setLong(service.getCapacity());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_STATUS:
+ prop.setLong(service.getChargeStatus());
+ break;
+ case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
+ prop.setLong(service.getEnergyCounterNwh());
+ break;
+ }
+ } catch (UnsupportedOperationException e) {
+ // Leave prop untouched.
+ return -1;
+ } catch (ServiceSpecificException e) {
+ // Leave prop untouched.
+ return -2;
+ }
+ // throws RemoteException as-is. BatteryManager wraps it into a RuntimeException
+ // and throw it to apps.
+
+ // If no error, return 0.
+ return 0;
+ }
+
+ @Override
+ public void scheduleUpdate() throws RemoteException {
+ getHandlerThread()
+ .getThreadHandler()
+ .post(
+ () -> {
+ traceBegin("HealthScheduleUpdate");
+ try {
+ IHealth service = mLastService.get();
+ if (service == null) {
+ Slog.e(TAG, "no health service");
+ return;
+ }
+ service.update();
+ } catch (RemoteException | ServiceSpecificException ex) {
+ Slog.e(TAG, "Cannot call update on health AIDL HAL", ex);
+ } finally {
+ traceEnd();
+ }
+ });
+ }
+
+ @Override
+ public HealthInfo getHealthInfo() throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) return null;
+ try {
+ return service.getHealthInfo();
+ } catch (UnsupportedOperationException | ServiceSpecificException ex) {
+ return null;
+ }
+ }
+
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ private class ServiceCallback extends IServiceCallback.Stub {
+ @Override
+ public void onRegistration(String name, @NonNull final IBinder newBinder)
+ throws RemoteException {
+ if (!SERVICE_NAME.equals(name)) return;
+ // This runnable only runs on mHandlerThread and ordering is ensured, hence
+ // no locking is needed inside the runnable.
+ getHandlerThread()
+ .getThreadHandler()
+ .post(
+ () -> {
+ IHealth newService =
+ IHealth.Stub.asInterface(Binder.allowBlocking(newBinder));
+ IHealth oldService = mLastService.getAndSet(newService);
+ IBinder oldBinder =
+ oldService != null ? oldService.asBinder() : null;
+ if (Objects.equals(newBinder, oldBinder)) return;
+
+ Slog.i(TAG, "New health AIDL HAL service registered");
+ mRegCallback.onRegistration(oldService, newService);
+ });
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
index c97a67b..16d97a4 100644
--- a/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
@@ -19,11 +19,12 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.fail;
+import static org.mockito.AdditionalMatchers.not;
import static org.mockito.Mockito.*;
-import android.hardware.health.V2_0.IHealth;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IServiceCallback;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
@@ -44,28 +45,47 @@
@RunWith(AndroidJUnit4.class)
public class HealthServiceWrapperTest {
-
@Mock IServiceManager mMockedManager;
- @Mock IHealth mMockedHal;
- @Mock IHealth mMockedHal2;
+ @Mock android.hardware.health.V2_0.IHealth mMockedHal;
+ @Mock android.hardware.health.V2_0.IHealth mMockedHal2;
@Mock HealthServiceWrapperHidl.Callback mCallback;
@Mock HealthServiceWrapperHidl.IServiceManagerSupplier mManagerSupplier;
@Mock HealthServiceWrapperHidl.IHealthSupplier mHealthServiceSupplier;
+
+ @Mock android.hardware.health.IHealth.Stub mMockedAidlHal;
+ @Mock android.hardware.health.IHealth.Stub mMockedAidlHal2;
+ @Mock HealthServiceWrapperAidl.ServiceManagerStub mMockedAidlManager;
+ @Mock HealthRegCallbackAidl mRegCallbackAidl;
+
HealthServiceWrapper mWrapper;
private static final String VENDOR = HealthServiceWrapperHidl.INSTANCE_VENDOR;
+ private static final String AIDL_SERVICE_NAME = HealthServiceWrapperAidl.SERVICE_NAME;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ // Mocks the conversion between IHealth and IBinder.
+ when(mMockedAidlHal.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal
+ when(mMockedAidlHal2.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal2
+ when(mMockedAidlHal.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR))
+ .thenReturn(mMockedAidlHal);
+ when(mMockedAidlHal2.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR))
+ .thenReturn(mMockedAidlHal2);
}
@After
public void tearDown() {
+ validateMockitoUsage();
if (mWrapper != null) mWrapper.getHandlerThread().quitSafely();
}
+ public static <T> ArgumentMatcher<T> isOneOf(T[] collection) {
+ return isOneOf(Arrays.asList(collection));
+ }
+
public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) {
return new ArgumentMatcher<T>() {
@Override
@@ -75,13 +95,39 @@
@Override
public String toString() {
- return collection.toString();
+ return "is one of " + collection.toString();
}
};
}
- private void initForInstances(String... instanceNamesArr) throws Exception {
- final Collection<String> instanceNames = Arrays.asList(instanceNamesArr);
+ /**
+ * Set up mock objects to pretend that the given AIDL and HIDL instances exists.
+ *
+ * <p>Also, when registering service notifications, the mocked service managers immediately
+ * sends 3 registration notifications, including 2 referring to the original HAL and 1 referring
+ * to the new HAL.
+ *
+ * @param aidlInstances e.g. {"android.hardware.health.IHealth/default"}
+ * @param hidlInstances e.g. {"default", "backup"}
+ * @throws Exception
+ */
+ private void initForInstances(String[] aidlInstances, String[] hidlInstances) throws Exception {
+ doAnswer(
+ (invocation) -> {
+ sendAidlRegCallback(invocation, mMockedAidlHal);
+ sendAidlRegCallback(invocation, mMockedAidlHal);
+ sendAidlRegCallback(invocation, mMockedAidlHal2);
+ return null;
+ })
+ .when(mMockedAidlManager)
+ .registerForNotifications(
+ argThat(isOneOf(aidlInstances)), any(IServiceCallback.class));
+ when(mMockedAidlManager.waitForDeclaredService(argThat(isOneOf(aidlInstances))))
+ .thenReturn(mMockedAidlHal)
+ .thenThrow(new RuntimeException("waitForDeclaredService called more than once"));
+ when(mMockedAidlManager.waitForDeclaredService(not(argThat(isOneOf(aidlInstances)))))
+ .thenReturn(null);
+
doAnswer(
(invocation) -> {
// technically, preexisting is ignored by
@@ -93,8 +139,8 @@
})
.when(mMockedManager)
.registerForNotifications(
- eq(IHealth.kInterfaceName),
- argThat(isOneOf(instanceNames)),
+ eq(android.hardware.health.V2_0.IHealth.kInterfaceName),
+ argThat(isOneOf(hidlInstances)),
any(IServiceNotification.class));
doReturn(mMockedManager).when(mManagerSupplier).get();
@@ -104,7 +150,7 @@
.doReturn(mMockedHal2) // notification 3
.doThrow(new RuntimeException("Should not call getService for more than 4 times"))
.when(mHealthServiceSupplier)
- .get(argThat(isOneOf(instanceNames)));
+ .get(argThat(isOneOf(hidlInstances)));
}
private void waitHandlerThreadFinish() throws Exception {
@@ -121,19 +167,62 @@
throws Exception {
((IServiceNotification) invocation.getArguments()[2])
.onRegistration(
- IHealth.kInterfaceName, (String) invocation.getArguments()[1], preexisting);
+ android.hardware.health.V2_0.IHealth.kInterfaceName,
+ (String) invocation.getArguments()[1],
+ preexisting);
+ }
+
+ private static void sendAidlRegCallback(
+ InvocationOnMock invocation, android.hardware.health.IHealth service) throws Exception {
+ ((IServiceCallback) invocation.getArguments()[1])
+ .onRegistration((String) invocation.getArguments()[0], service.asBinder());
}
private void createWrapper() throws RemoteException {
- mWrapper = HealthServiceWrapper.create(mCallback, mManagerSupplier, mHealthServiceSupplier);
+ mWrapper =
+ HealthServiceWrapper.create(
+ mRegCallbackAidl,
+ mMockedAidlManager,
+ mCallback,
+ mManagerSupplier,
+ mHealthServiceSupplier);
}
@SmallTest
@Test
- public void testWrapPreferVendor() throws Exception {
- initForInstances(VENDOR);
+ public void testWrapAidlOnly() throws Exception {
+ initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[0]);
createWrapper();
waitHandlerThreadFinish();
+ verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal));
+ verify(mRegCallbackAidl, never())
+ .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal));
+ verify(mRegCallbackAidl, times(1))
+ .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2));
+ verify(mCallback, never()).onRegistration(any(), any(), anyString());
+ }
+
+ @SmallTest
+ @Test
+ public void testWrapPreferAidl() throws Exception {
+ initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[] {VENDOR});
+ createWrapper();
+ waitHandlerThreadFinish();
+ verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal));
+ verify(mRegCallbackAidl, never())
+ .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal));
+ verify(mRegCallbackAidl, times(1))
+ .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2));
+ verify(mCallback, never()).onRegistration(any(), any(), anyString());
+ }
+
+ @SmallTest
+ @Test
+ public void testWrapFallbackHidl() throws Exception {
+ initForInstances(new String[0], new String[] {VENDOR});
+ createWrapper();
+ waitHandlerThreadFinish();
+ verify(mRegCallbackAidl, never()).onRegistration(any(), any());
verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR));
@@ -142,7 +231,7 @@
@SmallTest
@Test
public void testNoService() throws Exception {
- initForInstances("unrelated");
+ initForInstances(new String[0], new String[] {"unrelated"});
try {
createWrapper();
fail("Expect NoSuchElementException");