1 /* 2 * Copyright 2024 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 package com.android.server.audio; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.mockito.ArgumentMatchers.any; 21 import static org.mockito.ArgumentMatchers.anyInt; 22 import static org.mockito.ArgumentMatchers.eq; 23 import static org.mockito.Mockito.any; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.never; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.media.IAudioPolicyService; 30 import android.os.Binder; 31 import android.os.IBinder; 32 import android.os.IServiceCallback; 33 import android.os.RemoteException; 34 import android.platform.test.annotations.Presubmit; 35 36 import androidx.test.ext.junit.runners.AndroidJUnit4; 37 38 import org.junit.Before; 39 import org.junit.Rule; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.mockito.ArgumentCaptor; 43 import org.mockito.ArgumentMatchers; 44 import org.mockito.Mock; 45 import org.mockito.junit.MockitoJUnit; 46 import org.mockito.junit.MockitoRule; 47 48 import java.util.concurrent.Executor; 49 import java.util.function.Consumer; 50 import java.util.function.Function; 51 52 @RunWith(AndroidJUnit4.class) 53 @Presubmit 54 public class ServiceHolderTest { 55 56 private static final String AUDIO_POLICY_SERVICE_NAME = "media.audio_policy"; 57 58 @Rule 59 public final MockitoRule mockito = MockitoJUnit.rule(); 60 61 // the actual class under test 62 private ServiceHolder<IAudioPolicyService> mServiceHolder; 63 64 @Mock private ServiceHolder.ServiceProviderFacade mServiceProviderFacade; 65 66 @Mock private IAudioPolicyService mAudioPolicyService; 67 68 @Mock private IBinder mBinder; 69 70 @Mock private Consumer<IAudioPolicyService> mTaskOne; 71 @Mock private Consumer<IAudioPolicyService> mTaskTwo; 72 73 @Before setUp()74 public void setUp() throws Exception { 75 mServiceHolder = 76 new ServiceHolder( 77 AUDIO_POLICY_SERVICE_NAME, 78 (Function<IBinder, IAudioPolicyService>) 79 binder -> { 80 if (binder == mBinder) { 81 return mAudioPolicyService; 82 } else { 83 return mock(IAudioPolicyService.class); 84 } 85 }, 86 r -> r.run(), 87 mServiceProviderFacade); 88 when(mAudioPolicyService.asBinder()).thenReturn(mBinder); 89 } 90 91 @Test testListenerRegistered_whenConstructed()92 public void testListenerRegistered_whenConstructed() { 93 verify(mServiceProviderFacade) 94 .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), ArgumentMatchers.any()); 95 } 96 97 @Test testServiceSuccessfullyPopulated_whenCallback()98 public void testServiceSuccessfullyPopulated_whenCallback() throws RemoteException { 99 initializeViaCallback(); 100 verify(mBinder).linkToDeath(any(), anyInt()); 101 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 102 } 103 104 @Test testCheckServiceCalled_whenUncached()105 public void testCheckServiceCalled_whenUncached() { 106 when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME))) 107 .thenReturn(mBinder); 108 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 109 } 110 111 @Test testCheckServiceTransmitsNull()112 public void testCheckServiceTransmitsNull() { 113 assertThat(mServiceHolder.checkService()).isEqualTo(null); 114 } 115 116 @Test testWaitForServiceCalled_whenUncached()117 public void testWaitForServiceCalled_whenUncached() { 118 when(mServiceProviderFacade.waitForService(eq(AUDIO_POLICY_SERVICE_NAME))) 119 .thenReturn(mBinder); 120 assertThat(mServiceHolder.waitForService()).isEqualTo(mAudioPolicyService); 121 } 122 123 @Test testCheckServiceNotCalled_whenCached()124 public void testCheckServiceNotCalled_whenCached() { 125 initializeViaCallback(); 126 mServiceHolder.checkService(); 127 verify(mServiceProviderFacade, never()).checkService(any()); 128 } 129 130 @Test testWaitForServiceNotCalled_whenCached()131 public void testWaitForServiceNotCalled_whenCached() { 132 initializeViaCallback(); 133 mServiceHolder.waitForService(); 134 verify(mServiceProviderFacade, never()).waitForService(any()); 135 } 136 137 @Test testStartTaskCalled_onStart()138 public void testStartTaskCalled_onStart() { 139 mServiceHolder.registerOnStartTask(mTaskOne); 140 mServiceHolder.registerOnStartTask(mTaskTwo); 141 mServiceHolder.unregisterOnStartTask(mTaskOne); 142 when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME))) 143 .thenReturn(mBinder); 144 145 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 146 147 verify(mTaskTwo).accept(eq(mAudioPolicyService)); 148 verify(mTaskOne, never()).accept(any()); 149 } 150 151 @Test testStartTaskCalled_onStartFromCallback()152 public void testStartTaskCalled_onStartFromCallback() { 153 mServiceHolder.registerOnStartTask(mTaskOne); 154 mServiceHolder.registerOnStartTask(mTaskTwo); 155 mServiceHolder.unregisterOnStartTask(mTaskOne); 156 157 initializeViaCallback(); 158 159 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 160 verify(mTaskTwo).accept(eq(mAudioPolicyService)); 161 verify(mTaskOne, never()).accept(any()); 162 } 163 164 @Test testStartTaskCalled_onRegisterAfterStarted()165 public void testStartTaskCalled_onRegisterAfterStarted() { 166 initializeViaCallback(); 167 mServiceHolder.registerOnStartTask(mTaskOne); 168 verify(mTaskOne).accept(eq(mAudioPolicyService)); 169 } 170 171 @Test testBinderDied_clearsServiceAndUnlinks()172 public void testBinderDied_clearsServiceAndUnlinks() { 173 initializeViaCallback(); 174 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 175 176 mServiceHolder.binderDied(mBinder); 177 178 verify(mBinder).unlinkToDeath(any(), anyInt()); 179 assertThat(mServiceHolder.checkService()).isEqualTo(null); 180 verify(mServiceProviderFacade).checkService(eq(AUDIO_POLICY_SERVICE_NAME)); 181 } 182 183 @Test testBinderDied_callsDeathTasks()184 public void testBinderDied_callsDeathTasks() { 185 mServiceHolder.registerOnDeathTask(mTaskOne); 186 mServiceHolder.registerOnDeathTask(mTaskTwo); 187 initializeViaCallback(); 188 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 189 mServiceHolder.unregisterOnDeathTask(mTaskOne); 190 191 mServiceHolder.binderDied(mBinder); 192 193 verify(mTaskTwo).accept(eq(mAudioPolicyService)); 194 verify(mTaskOne, never()).accept(any()); 195 } 196 197 @Test testAttemptClear_clearsServiceAndUnlinks()198 public void testAttemptClear_clearsServiceAndUnlinks() { 199 initializeViaCallback(); 200 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 201 202 mServiceHolder.attemptClear(mBinder); 203 204 verify(mBinder).unlinkToDeath(any(), anyInt()); 205 assertThat(mServiceHolder.checkService()).isEqualTo(null); 206 verify(mServiceProviderFacade).checkService(eq(AUDIO_POLICY_SERVICE_NAME)); 207 } 208 209 @Test testAttemptClear_callsDeathTasks()210 public void testAttemptClear_callsDeathTasks() { 211 mServiceHolder.registerOnDeathTask(mTaskOne); 212 mServiceHolder.registerOnDeathTask(mTaskTwo); 213 initializeViaCallback(); 214 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 215 mServiceHolder.unregisterOnDeathTask(mTaskOne); 216 217 mServiceHolder.attemptClear(mBinder); 218 219 verify(mTaskTwo).accept(eq(mAudioPolicyService)); 220 verify(mTaskOne, never()).accept(any()); 221 } 222 223 @Test testSet_whenServiceSet_isIgnored()224 public void testSet_whenServiceSet_isIgnored() { 225 mServiceHolder.registerOnStartTask(mTaskOne); 226 when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME))) 227 .thenReturn(mBinder); 228 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 229 230 verify(mTaskOne).accept(eq(mAudioPolicyService)); 231 232 // get the callback 233 ArgumentCaptor<IServiceCallback> cb = ArgumentCaptor.forClass(IServiceCallback.class); 234 verify(mServiceProviderFacade) 235 .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), cb.capture()); 236 237 // Simulate a service callback with a different instance 238 try { 239 cb.getValue().onRegistration(AUDIO_POLICY_SERVICE_NAME, new Binder()); 240 } catch (RemoteException e) { 241 throw new RuntimeException(e); 242 } 243 244 // No additional start task call (i.e. only the first verify) 245 verify(mTaskOne).accept(any()); 246 // Same instance 247 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 248 249 } 250 251 @Test testClear_whenServiceCleared_isIgnored()252 public void testClear_whenServiceCleared_isIgnored() { 253 mServiceHolder.registerOnDeathTask(mTaskOne); 254 mServiceHolder.attemptClear(mBinder); 255 verify(mTaskOne, never()).accept(any()); 256 } 257 258 @Test testClear_withDifferentCookie_isIgnored()259 public void testClear_withDifferentCookie_isIgnored() { 260 mServiceHolder.registerOnDeathTask(mTaskOne); 261 initializeViaCallback(); 262 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 263 264 // Notif for stale cookie 265 mServiceHolder.attemptClear(new Binder()); 266 267 // Service shouldn't be cleared 268 assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); 269 // No death tasks should fire 270 verify(mTaskOne, never()).accept(any()); 271 } 272 initializeViaCallback()273 private void initializeViaCallback() { 274 ArgumentCaptor<IServiceCallback> cb = ArgumentCaptor.forClass(IServiceCallback.class); 275 verify(mServiceProviderFacade) 276 .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), cb.capture()); 277 278 try { 279 cb.getValue().onRegistration(AUDIO_POLICY_SERVICE_NAME, mBinder); 280 } catch (RemoteException e) { 281 throw new RuntimeException(e); 282 } 283 } 284 } 285