1 /*
2  * Copyright (C) 2017 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;
18 
19 import static junit.framework.Assert.*;
20 
21 import static org.mockito.Mockito.*;
22 
23 import android.hardware.health.V2_0.IHealth;
24 import android.hidl.manager.V1_0.IServiceManager;
25 import android.hidl.manager.V1_0.IServiceNotification;
26 import android.test.AndroidTestCase;
27 
28 import androidx.test.filters.SmallTest;
29 
30 import org.mockito.ArgumentMatcher;
31 import org.mockito.Mock;
32 import org.mockito.MockitoAnnotations;
33 import org.mockito.invocation.InvocationOnMock;
34 
35 import java.util.Arrays;
36 import java.util.Collection;
37 import java.util.NoSuchElementException;
38 
39 public class BatteryServiceTest extends AndroidTestCase {
40 
41     @Mock IServiceManager mMockedManager;
42     @Mock IHealth mMockedHal;
43     @Mock IHealth mMockedHal2;
44 
45     @Mock BatteryService.HealthServiceWrapper.Callback mCallback;
46     @Mock BatteryService.HealthServiceWrapper.IServiceManagerSupplier mManagerSupplier;
47     @Mock BatteryService.HealthServiceWrapper.IHealthSupplier mHealthServiceSupplier;
48     BatteryService.HealthServiceWrapper mWrapper;
49 
50     private static final String HEALTHD = BatteryService.HealthServiceWrapper.INSTANCE_HEALTHD;
51     private static final String VENDOR = BatteryService.HealthServiceWrapper.INSTANCE_VENDOR;
52 
53     @Override
setUp()54     public void setUp() {
55         MockitoAnnotations.initMocks(this);
56     }
57 
58     @Override
tearDown()59     public void tearDown() {
60         if (mWrapper != null)
61             mWrapper.getHandlerThread().quitSafely();
62     }
63 
isOneOf(Collection<T> collection)64     public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) {
65         return new ArgumentMatcher<T>() {
66             @Override public boolean matches(T e) {
67                 return collection.contains(e);
68             }
69             @Override public String toString() {
70                 return collection.toString();
71             }
72         };
73     }
74 
75     private void initForInstances(String... instanceNamesArr) throws Exception {
76         final Collection<String> instanceNames = Arrays.asList(instanceNamesArr);
77         doAnswer((invocation) -> {
78                 // technically, preexisting is ignored by
79                 // BatteryService.HealthServiceWrapper.Notification, but still call it correctly.
80                 sendNotification(invocation, true);
81                 sendNotification(invocation, true);
82                 sendNotification(invocation, false);
83                 return null;
84             }).when(mMockedManager).registerForNotifications(
85                 eq(IHealth.kInterfaceName),
86                 argThat(isOneOf(instanceNames)),
87                 any(IServiceNotification.class));
88 
89         doReturn(mMockedManager).when(mManagerSupplier).get();
90         doReturn(mMockedHal)        // init calls this
91             .doReturn(mMockedHal)   // notification 1
92             .doReturn(mMockedHal)   // notification 2
93             .doReturn(mMockedHal2)  // notification 3
94             .doThrow(new RuntimeException("Should not call getService for more than 4 times"))
95             .when(mHealthServiceSupplier).get(argThat(isOneOf(instanceNames)));
96 
97         mWrapper = new BatteryService.HealthServiceWrapper();
98     }
99 
100     private void waitHandlerThreadFinish() throws Exception {
101         for (int i = 0; i < 5; i++) {
102             if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) {
103                 return;
104             }
105             Thread.sleep(300);
106         }
107         assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks());
108     }
109 
110     private static void sendNotification(InvocationOnMock invocation, boolean preexisting)
111             throws Exception {
112         ((IServiceNotification)invocation.getArguments()[2]).onRegistration(
113                 IHealth.kInterfaceName,
114                 (String)invocation.getArguments()[1],
115                 preexisting);
116     }
117 
118     @SmallTest
119     public void testWrapPreferVendor() throws Exception {
120         initForInstances(VENDOR, HEALTHD);
121         mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
122         waitHandlerThreadFinish();
123         verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
124         verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
125         verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR));
126     }
127 
128     @SmallTest
129     public void testUseHealthd() throws Exception {
130         initForInstances(HEALTHD);
131         mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
132         waitHandlerThreadFinish();
133         verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(HEALTHD));
134         verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
135         verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(HEALTHD));
136     }
137 
138     @SmallTest
139     public void testNoService() throws Exception {
140         initForInstances("unrelated");
141         try {
142             mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
143             fail("Expect NoSuchElementException");
144         } catch (NoSuchElementException ex) {
145             // expected
146         }
147     }
148 }
149