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.health; 18 19 import static junit.framework.Assert.assertFalse; 20 import static junit.framework.Assert.fail; 21 22 import static org.mockito.AdditionalMatchers.not; 23 import static org.mockito.Mockito.*; 24 25 import android.hidl.manager.V1_0.IServiceManager; 26 import android.hidl.manager.V1_0.IServiceNotification; 27 import android.os.IServiceCallback; 28 import android.os.RemoteException; 29 30 import androidx.test.filters.SmallTest; 31 import androidx.test.runner.AndroidJUnit4; 32 33 import org.junit.After; 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 import org.mockito.ArgumentMatcher; 38 import org.mockito.Mock; 39 import org.mockito.MockitoAnnotations; 40 import org.mockito.invocation.InvocationOnMock; 41 42 import java.util.Arrays; 43 import java.util.Collection; 44 import java.util.NoSuchElementException; 45 46 @RunWith(AndroidJUnit4.class) 47 public class HealthServiceWrapperTest { 48 @Mock IServiceManager mMockedManager; 49 @Mock android.hardware.health.V2_0.IHealth mMockedHal; 50 @Mock android.hardware.health.V2_0.IHealth mMockedHal2; 51 52 @Mock HealthServiceWrapperHidl.Callback mCallback; 53 @Mock HealthServiceWrapperHidl.IServiceManagerSupplier mManagerSupplier; 54 @Mock HealthServiceWrapperHidl.IHealthSupplier mHealthServiceSupplier; 55 56 @Mock android.hardware.health.IHealth.Stub mMockedAidlHal; 57 @Mock android.hardware.health.IHealth.Stub mMockedAidlHal2; 58 @Mock HealthServiceWrapperAidl.ServiceManagerStub mMockedAidlManager; 59 @Mock HealthRegCallbackAidl mRegCallbackAidl; 60 61 HealthServiceWrapper mWrapper; 62 63 private static final String VENDOR = HealthServiceWrapperHidl.INSTANCE_VENDOR; 64 private static final String AIDL_SERVICE_NAME = HealthServiceWrapperAidl.SERVICE_NAME; 65 66 @Before setUp()67 public void setUp() { 68 MockitoAnnotations.initMocks(this); 69 70 // Mocks the conversion between IHealth and IBinder. 71 when(mMockedAidlHal.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal 72 when(mMockedAidlHal2.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal2 73 when(mMockedAidlHal.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR)) 74 .thenReturn(mMockedAidlHal); 75 when(mMockedAidlHal2.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR)) 76 .thenReturn(mMockedAidlHal2); 77 } 78 79 @After tearDown()80 public void tearDown() { 81 validateMockitoUsage(); 82 if (mWrapper != null) mWrapper.getHandlerThread().quitSafely(); 83 } 84 isOneOf(T[] collection)85 public static <T> ArgumentMatcher<T> isOneOf(T[] collection) { 86 return isOneOf(Arrays.asList(collection)); 87 } 88 isOneOf(Collection<T> collection)89 public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) { 90 return new ArgumentMatcher<T>() { 91 @Override 92 public boolean matches(T e) { 93 return collection.contains(e); 94 } 95 96 @Override 97 public String toString() { 98 return "is one of " + collection.toString(); 99 } 100 }; 101 } 102 103 /** 104 * Set up mock objects to pretend that the given AIDL and HIDL instances exists. 105 * 106 * <p>Also, when registering service notifications, the mocked service managers immediately 107 * sends 3 registration notifications, including 2 referring to the original HAL and 1 referring 108 * to the new HAL. 109 * 110 * @param aidlInstances e.g. {"android.hardware.health.IHealth/default"} 111 * @param hidlInstances e.g. {"default", "backup"} 112 * @throws Exception 113 */ 114 private void initForInstances(String[] aidlInstances, String[] hidlInstances) throws Exception { 115 doAnswer( 116 (invocation) -> { 117 sendAidlRegCallback(invocation, mMockedAidlHal); 118 sendAidlRegCallback(invocation, mMockedAidlHal); 119 sendAidlRegCallback(invocation, mMockedAidlHal2); 120 return null; 121 }) 122 .when(mMockedAidlManager) 123 .registerForNotifications( 124 argThat(isOneOf(aidlInstances)), any(IServiceCallback.class)); 125 when(mMockedAidlManager.waitForDeclaredService(argThat(isOneOf(aidlInstances)))) 126 .thenReturn(mMockedAidlHal) 127 .thenThrow(new RuntimeException("waitForDeclaredService called more than once")); 128 when(mMockedAidlManager.waitForDeclaredService(not(argThat(isOneOf(aidlInstances))))) 129 .thenReturn(null); 130 131 doAnswer( 132 (invocation) -> { 133 // technically, preexisting is ignored by 134 // HealthServiceWrapperHidl.Notification, but still call it correctly. 135 sendNotification(invocation, true); 136 sendNotification(invocation, true); 137 sendNotification(invocation, false); 138 return null; 139 }) 140 .when(mMockedManager) 141 .registerForNotifications( 142 eq(android.hardware.health.V2_0.IHealth.kInterfaceName), 143 argThat(isOneOf(hidlInstances)), 144 any(IServiceNotification.class)); 145 146 doReturn(mMockedManager).when(mManagerSupplier).get(); 147 doReturn(mMockedHal) // init calls this 148 .doReturn(mMockedHal) // notification 1 149 .doReturn(mMockedHal) // notification 2 150 .doReturn(mMockedHal2) // notification 3 151 .doThrow(new RuntimeException("Should not call getService for more than 4 times")) 152 .when(mHealthServiceSupplier) 153 .get(argThat(isOneOf(hidlInstances))); 154 } 155 156 private void waitHandlerThreadFinish() throws Exception { 157 for (int i = 0; i < 5; i++) { 158 if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) { 159 return; 160 } 161 Thread.sleep(300); 162 } 163 assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()); 164 } 165 166 private static void sendNotification(InvocationOnMock invocation, boolean preexisting) 167 throws Exception { 168 ((IServiceNotification) invocation.getArguments()[2]) 169 .onRegistration( 170 android.hardware.health.V2_0.IHealth.kInterfaceName, 171 (String) invocation.getArguments()[1], 172 preexisting); 173 } 174 175 private static void sendAidlRegCallback( 176 InvocationOnMock invocation, android.hardware.health.IHealth service) throws Exception { 177 ((IServiceCallback) invocation.getArguments()[1]) 178 .onRegistration((String) invocation.getArguments()[0], service.asBinder()); 179 } 180 181 private void createWrapper() throws RemoteException { 182 mWrapper = 183 HealthServiceWrapper.create( 184 mRegCallbackAidl, 185 mMockedAidlManager, 186 mCallback, 187 mManagerSupplier, 188 mHealthServiceSupplier); 189 } 190 191 @SmallTest 192 @Test 193 public void testWrapAidlOnly() throws Exception { 194 initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[0]); 195 createWrapper(); 196 waitHandlerThreadFinish(); 197 verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal)); 198 verify(mRegCallbackAidl, never()) 199 .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal)); 200 verify(mRegCallbackAidl, times(1)) 201 .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2)); 202 verify(mCallback, never()).onRegistration(any(), any(), anyString()); 203 } 204 205 @SmallTest 206 @Test 207 public void testWrapPreferAidl() throws Exception { 208 initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[] {VENDOR}); 209 createWrapper(); 210 waitHandlerThreadFinish(); 211 verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal)); 212 verify(mRegCallbackAidl, never()) 213 .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal)); 214 verify(mRegCallbackAidl, times(1)) 215 .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2)); 216 verify(mCallback, never()).onRegistration(any(), any(), anyString()); 217 } 218 219 @SmallTest 220 @Test 221 public void testWrapFallbackHidl() throws Exception { 222 initForInstances(new String[0], new String[] {VENDOR}); 223 createWrapper(); 224 waitHandlerThreadFinish(); 225 verify(mRegCallbackAidl, never()).onRegistration(any(), any()); 226 verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR)); 227 verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString()); 228 verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR)); 229 } 230 231 @SmallTest 232 @Test 233 public void testNoService() throws Exception { 234 initForInstances(new String[0], new String[] {"unrelated"}); 235 try { 236 createWrapper(); 237 fail("Expect NoSuchElementException"); 238 } catch (NoSuchElementException ex) { 239 // expected 240 } 241 } 242 } 243