1 /*
2  * Copyright (C) 2019 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.car;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNotSame;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertSame;
24 import static org.mockito.Mockito.doThrow;
25 import static org.mockito.Mockito.reset;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.verifyNoMoreInteractions;
29 import static org.mockito.Mockito.when;
30 
31 import android.car.Car;
32 import android.car.vms.IVmsPublisherClient;
33 import android.car.vms.IVmsPublisherService;
34 import android.car.vms.IVmsSubscriberClient;
35 import android.car.vms.VmsLayer;
36 import android.car.vms.VmsLayersOffering;
37 import android.car.vms.VmsSubscriptionState;
38 import android.content.Context;
39 import android.content.pm.PackageManager;
40 import android.os.Binder;
41 import android.os.IBinder;
42 import android.os.RemoteException;
43 
44 import androidx.test.filters.SmallTest;
45 
46 import com.android.car.stats.CarStatsService;
47 import com.android.car.stats.VmsClientLogger;
48 import com.android.car.vms.VmsBrokerService;
49 import com.android.car.vms.VmsClientManager;
50 
51 import org.junit.After;
52 import org.junit.Before;
53 import org.junit.Rule;
54 import org.junit.Test;
55 import org.mockito.ArgumentCaptor;
56 import org.mockito.Captor;
57 import org.mockito.Mock;
58 import org.mockito.junit.MockitoJUnit;
59 import org.mockito.junit.MockitoRule;
60 
61 import java.util.Arrays;
62 import java.util.Collections;
63 import java.util.HashSet;
64 
65 @SmallTest
66 public class VmsPublisherServiceTest {
67     private static final VmsSubscriptionState SUBSCRIPTION_STATE = new VmsSubscriptionState(12345,
68             Collections.emptySet(), Collections.emptySet());
69     private static final VmsLayersOffering OFFERING = new VmsLayersOffering(Collections.emptySet(),
70             54321);
71     private static final VmsLayer LAYER = new VmsLayer(1, 2, 3);
72 
73     private static final int PUBLISHER_ID = 54321;
74     private static final byte[] PAYLOAD = new byte[]{1, 2, 3, 4};
75 
76     private static final int PUBLISHER_UID = 10100;
77     private static final int SUBSCRIBER_UID = 10101;
78     private static final int SUBSCRIBER_UID2 = 10102;
79     private static final int NO_SUBSCRIBERS_UID = -1;
80 
81     @Rule
82     public MockitoRule mMockitoRule = MockitoJUnit.rule();
83     @Mock
84     private Context mContext;
85     @Mock
86     private CarStatsService mStatsService;
87     @Mock
88     private VmsBrokerService mBrokerService;
89     @Captor
90     private ArgumentCaptor<VmsBrokerService.PublisherListener> mProxyCaptor;
91     @Mock
92     private VmsClientManager mClientManager;
93 
94     @Mock
95     private VmsClientLogger mPublisherLog;
96     @Mock
97     private VmsClientLogger mSubscriberLog;
98     @Mock
99     private VmsClientLogger mSubscriberLog2;
100     @Mock
101     private VmsClientLogger mNoSubscribersLog;
102 
103     @Mock
104     private IVmsSubscriberClient mSubscriberClient;
105     @Mock
106     private IVmsSubscriberClient mSubscriberClient2;
107 
108     private VmsPublisherService mPublisherService;
109     private MockPublisherClient mPublisherClient;
110     private MockPublisherClient mPublisherClient2;
111 
112     @Before
setUp()113     public void setUp() {
114         mPublisherService = new VmsPublisherService(mContext, mStatsService, mBrokerService,
115                 mClientManager, () -> PUBLISHER_UID);
116         verify(mClientManager).setPublisherService(mPublisherService);
117 
118         when(mClientManager.getSubscriberUid(mSubscriberClient)).thenReturn(SUBSCRIBER_UID);
119         when(mClientManager.getSubscriberUid(mSubscriberClient2)).thenReturn(SUBSCRIBER_UID2);
120 
121         when(mStatsService.getVmsClientLogger(PUBLISHER_UID)).thenReturn(mPublisherLog);
122         when(mStatsService.getVmsClientLogger(SUBSCRIBER_UID)).thenReturn(mSubscriberLog);
123         when(mStatsService.getVmsClientLogger(SUBSCRIBER_UID2)).thenReturn(mSubscriberLog2);
124         when(mStatsService.getVmsClientLogger(NO_SUBSCRIBERS_UID)).thenReturn(mNoSubscribersLog);
125 
126         mPublisherClient = new MockPublisherClient();
127         mPublisherClient2 = new MockPublisherClient();
128         when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER, PUBLISHER_ID))
129                 .thenReturn(new HashSet<>(Arrays.asList(mSubscriberClient, mSubscriberClient2)));
130     }
131 
132     @After
tearDown()133     public void tearDown() {
134         verifyNoMoreInteractions(mPublisherLog, mSubscriberLog, mSubscriberLog2, mNoSubscribersLog);
135     }
136 
137     @Test
testInit()138     public void testInit() {
139         mPublisherService.init();
140     }
141 
142     @Test
testOnClientConnected()143     public void testOnClientConnected() {
144         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
145         mPublisherService.onClientConnected("SomeOtherClient", mPublisherClient2);
146         verify(mBrokerService, times(2)).addPublisherListener(mProxyCaptor.capture());
147 
148         assertNotNull(mPublisherClient.mPublisherService);
149         assertSame(mPublisherClient.mPublisherService, mProxyCaptor.getAllValues().get(0));
150 
151         assertNotNull(mPublisherClient2.mPublisherService);
152         assertSame(mPublisherClient2.mPublisherService, mProxyCaptor.getAllValues().get(1));
153         assertNotSame(mPublisherClient2.mPublisherService, mPublisherClient.mPublisherService);
154     }
155 
156     @Test
testOnClientDisconnected()157     public void testOnClientDisconnected() {
158         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
159         mPublisherService.onClientConnected("SomeOtherClient", mPublisherClient2);
160         verify(mBrokerService, times(2)).addPublisherListener(mProxyCaptor.capture());
161 
162         reset(mClientManager, mBrokerService);
163         mPublisherService.onClientDisconnected("SomeClient");
164 
165         verify(mBrokerService).removeDeadPublisher(mPublisherClient.mToken);
166         verify(mBrokerService).removePublisherListener(mProxyCaptor.getAllValues().get(0));
167         verifyNoMoreInteractions(mBrokerService);
168     }
169 
170     @Test
testSetLayersOffering()171     public void testSetLayersOffering() throws Exception {
172         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
173 
174         mPublisherClient.mPublisherService.setLayersOffering(mPublisherClient.mToken, OFFERING);
175         verify(mBrokerService).setPublisherLayersOffering(mPublisherClient.mToken, OFFERING);
176     }
177 
178     @Test(expected = SecurityException.class)
testSetLayersOffering_InvalidToken()179     public void testSetLayersOffering_InvalidToken() throws Exception {
180         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
181 
182         mPublisherClient.mPublisherService.setLayersOffering(new Binder(), OFFERING);
183     }
184 
185     @Test(expected = SecurityException.class)
testSetLayersOffering_Disconnected()186     public void testSetLayersOffering_Disconnected() throws Exception {
187         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
188         mPublisherService.onClientDisconnected("SomeClient");
189 
190         mPublisherClient.mPublisherService.setLayersOffering(mPublisherClient.mToken, OFFERING);
191     }
192 
193     @Test(expected = SecurityException.class)
testSetLayersOffering_PermissionDenied()194     public void testSetLayersOffering_PermissionDenied() throws Exception {
195         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
196         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_VMS_PUBLISHER)).thenReturn(
197                 PackageManager.PERMISSION_DENIED);
198 
199         mPublisherClient.mPublisherService.setLayersOffering(mPublisherClient.mToken, OFFERING);
200     }
201 
202     @Test
testPublish()203     public void testPublish() throws Exception {
204         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
205 
206         mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
207                 PAYLOAD);
208         verify(mSubscriberClient).onVmsMessageReceived(LAYER, PAYLOAD);
209         verify(mSubscriberClient2).onVmsMessageReceived(LAYER, PAYLOAD);
210 
211         verify(mPublisherLog).logPacketSent(LAYER, PAYLOAD.length);
212         verify(mSubscriberLog).logPacketReceived(LAYER, PAYLOAD.length);
213         verify(mSubscriberLog2).logPacketReceived(LAYER, PAYLOAD.length);
214     }
215 
216     @Test
testPublishNullLayerAndNullPayload()217     public void testPublishNullLayerAndNullPayload() throws Exception {
218         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
219 
220         // We just want to ensure that no exceptions are thrown here.
221         mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, null, PUBLISHER_ID,
222                 null);
223     }
224 
225     @Test
testPublish_NoSubscribers()226     public void testPublish_NoSubscribers() throws Exception {
227         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
228         when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER, PUBLISHER_ID))
229                 .thenReturn(Collections.emptySet());
230 
231         mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
232                 PAYLOAD);
233 
234         verify(mPublisherLog).logPacketSent(LAYER, PAYLOAD.length);
235         verify(mNoSubscribersLog).logPacketDropped(LAYER, PAYLOAD.length);
236     }
237 
238     @Test
testPublish_ClientError()239     public void testPublish_ClientError() throws Exception {
240         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
241         doThrow(new RemoteException()).when(mSubscriberClient).onVmsMessageReceived(LAYER, PAYLOAD);
242 
243         mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
244                 PAYLOAD);
245         verify(mSubscriberClient).onVmsMessageReceived(LAYER, PAYLOAD);
246         verify(mSubscriberClient2).onVmsMessageReceived(LAYER, PAYLOAD);
247 
248         verify(mPublisherLog).logPacketSent(LAYER, PAYLOAD.length);
249         verify(mSubscriberLog).logPacketDropped(LAYER, PAYLOAD.length);
250         verify(mSubscriberLog2).logPacketReceived(LAYER, PAYLOAD.length);
251     }
252 
253     @Test(expected = SecurityException.class)
testPublish_InvalidToken()254     public void testPublish_InvalidToken() throws Exception {
255         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
256 
257         mPublisherClient.mPublisherService.publish(new Binder(), LAYER, PUBLISHER_ID, PAYLOAD);
258     }
259 
260     @Test(expected = SecurityException.class)
testPublish_Disconnected()261     public void testPublish_Disconnected() throws Exception {
262         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
263         mPublisherService.onClientDisconnected("SomeClient");
264 
265         mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
266                 PAYLOAD);
267     }
268 
269     @Test(expected = SecurityException.class)
testPublish_PermissionDenied()270     public void testPublish_PermissionDenied() throws Exception {
271         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
272         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_VMS_PUBLISHER)).thenReturn(
273                 PackageManager.PERMISSION_DENIED);
274 
275         mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
276                 PAYLOAD);
277     }
278 
279     @Test
testGetSubscriptions()280     public void testGetSubscriptions() throws Exception {
281         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
282         when(mBrokerService.getSubscriptionState()).thenReturn(SUBSCRIPTION_STATE);
283 
284         assertEquals(SUBSCRIPTION_STATE, mPublisherClient.mPublisherService.getSubscriptions());
285     }
286 
287     @Test(expected = SecurityException.class)
testGetSubscriptions_Disconnected()288     public void testGetSubscriptions_Disconnected() throws Exception {
289         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
290         mPublisherService.onClientDisconnected("SomeClient");
291 
292         mPublisherClient.mPublisherService.getSubscriptions();
293     }
294 
295     @Test(expected = SecurityException.class)
testGetSubscriptions_PermissionDenied()296     public void testGetSubscriptions_PermissionDenied() throws Exception {
297         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
298         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_VMS_PUBLISHER)).thenReturn(
299                 PackageManager.PERMISSION_DENIED);
300 
301         mPublisherClient.mPublisherService.getSubscriptions();
302     }
303 
304     @Test
testGetPublisherId()305     public void testGetPublisherId() throws Exception {
306         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
307         when(mBrokerService.getPublisherId(PAYLOAD)).thenReturn(PUBLISHER_ID);
308 
309         assertEquals(PUBLISHER_ID, mPublisherClient.mPublisherService.getPublisherId(PAYLOAD));
310     }
311 
312     @Test(expected = SecurityException.class)
testGetPublisherId_Disconnected()313     public void testGetPublisherId_Disconnected() throws Exception {
314         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
315         mPublisherService.onClientDisconnected("SomeClient");
316 
317         mPublisherClient.mPublisherService.getPublisherId(PAYLOAD);
318     }
319 
320     @Test(expected = SecurityException.class)
testGetPublisherId_PermissionDenied()321     public void testGetPublisherId_PermissionDenied() throws Exception {
322         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
323         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_VMS_PUBLISHER)).thenReturn(
324                 PackageManager.PERMISSION_DENIED);
325 
326         mPublisherClient.mPublisherService.getPublisherId(PAYLOAD);
327     }
328 
329     @Test
testOnSubscriptionChange()330     public void testOnSubscriptionChange() {
331         mPublisherService.onClientConnected("SomeClient", mPublisherClient);
332         mPublisherService.onClientConnected("SomeOtherClient", mPublisherClient2);
333         verify(mBrokerService, times(2)).addPublisherListener(mProxyCaptor.capture());
334 
335         mProxyCaptor.getAllValues().get(0).onSubscriptionChange(SUBSCRIPTION_STATE);
336 
337         assertEquals(SUBSCRIPTION_STATE, mPublisherClient.mSubscriptionState);
338         assertNull(mPublisherClient2.mSubscriptionState);
339     }
340 
341     @Test
testRelease()342     public void testRelease() {
343         mPublisherService.release();
344     }
345 
346     private class MockPublisherClient extends IVmsPublisherClient.Stub {
347         private IBinder mToken;
348         private IVmsPublisherService mPublisherService;
349         private VmsSubscriptionState mSubscriptionState;
350 
351         @Override
setVmsPublisherService(IBinder token, IVmsPublisherService service)352         public void setVmsPublisherService(IBinder token, IVmsPublisherService service) {
353             assertNotNull(token);
354             assertNotNull(service);
355             if (mToken != null) {
356                 throw new IllegalArgumentException("Publisher service set multiple times");
357             }
358             this.mToken = token;
359             this.mPublisherService = service;
360         }
361 
362         @Override
onVmsSubscriptionChange(VmsSubscriptionState subscriptionState)363         public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
364             assertNotNull(subscriptionState);
365             this.mSubscriptionState = subscriptionState;
366         }
367     }
368 }
369