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