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