• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.car;
18 
19 import static android.car.CarProjectionManager.ProjectionAccessPointCallback.ERROR_GENERIC;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.junit.Assert.fail;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.inOrder;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.verify;
31 
32 import android.car.CarProjectionManager.ProjectionAccessPointCallback;
33 import android.car.testapi.CarProjectionController;
34 import android.car.testapi.FakeCar;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.net.MacAddress;
38 import android.net.wifi.SoftApConfiguration;
39 import android.util.ArraySet;
40 
41 import androidx.test.core.app.ApplicationProvider;
42 
43 import org.junit.Before;
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.ArgumentCaptor;
48 import org.mockito.Captor;
49 import org.mockito.InOrder;
50 import org.mockito.Spy;
51 import org.mockito.junit.MockitoJUnit;
52 import org.mockito.junit.MockitoRule;
53 import org.robolectric.RobolectricTestRunner;
54 import org.robolectric.annotation.internal.DoNotInstrument;
55 
56 import java.util.Arrays;
57 import java.util.Collections;
58 import java.util.Set;
59 import java.util.concurrent.CountDownLatch;
60 import java.util.concurrent.Executor;
61 import java.util.concurrent.TimeUnit;
62 
63 @RunWith(RobolectricTestRunner.class)
64 @DoNotInstrument
65 public class CarProjectionManagerTest {
66     @Rule
67     public MockitoRule rule = MockitoJUnit.rule();
68 
69     @Captor
70     private ArgumentCaptor<Intent> mIntentArgumentCaptor;
71 
72     @Spy
73     private final Context mContext = ApplicationProvider.getApplicationContext();
74 
75     private static final int DEFAULT_TIMEOUT_MS = 1000;
76 
77     /** An {@link Executor} that immediately runs its callbacks synchronously. */
78     private static final Executor DIRECT_EXECUTOR = Runnable::run;
79 
80     private CarProjectionManager mProjectionManager;
81     private CarProjectionController mController;
82     private ApCallback mApCallback;
83 
84     @Before
setUp()85     public void setUp() {
86         FakeCar fakeCar = FakeCar.createFakeCar(mContext);
87         mController = fakeCar.getCarProjectionController();
88         mProjectionManager =
89                 (CarProjectionManager) fakeCar.getCar().getCarManager(Car.PROJECTION_SERVICE);
90         assertThat(mProjectionManager).isNotNull();
91 
92         mApCallback = new ApCallback();
93     }
94 
95     @Test
startAp_fail()96     public void startAp_fail() throws InterruptedException {
97         mController.setSoftApConfiguration(null);
98 
99         mProjectionManager.startProjectionAccessPoint(mApCallback);
100         mApCallback.mFailed.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
101         assertThat(mApCallback.mFailureReason).isEqualTo(ERROR_GENERIC);
102     }
103 
104     @Test
startAp_success()105     public void startAp_success() throws InterruptedException {
106         SoftApConfiguration config = new SoftApConfiguration.Builder()
107                 .setSsid("Hello")
108                 .setBssid(MacAddress.fromString("AA:BB:CC:CC:DD:EE"))
109                 .setPassphrase("password", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
110                 .build();
111         mController.setSoftApConfiguration(config);
112 
113         mProjectionManager.startProjectionAccessPoint(mApCallback);
114         mApCallback.mStarted.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
115         assertThat(mApCallback.mSoftApConfiguration).isEqualTo(config);
116     }
117 
118     @Test
registerProjectionRunner()119     public void registerProjectionRunner() throws CarNotConnectedException {
120         Intent intent = new Intent("my_action");
121         intent.setPackage("my.package");
122         mProjectionManager.registerProjectionRunner(intent);
123 
124         verify(mContext).bindService(mIntentArgumentCaptor.capture(), any(),
125                 eq(Context.BIND_AUTO_CREATE));
126         assertThat(mIntentArgumentCaptor.getValue()).isEqualTo(intent);
127     }
128 
129     @Test
keyEventListener_registerMultipleEventListeners()130     public void keyEventListener_registerMultipleEventListeners() {
131         CarProjectionManager.ProjectionKeyEventHandler eventHandler1 =
132                 mock(CarProjectionManager.ProjectionKeyEventHandler.class);
133         CarProjectionManager.ProjectionKeyEventHandler eventHandler2 =
134                 mock(CarProjectionManager.ProjectionKeyEventHandler.class);
135 
136         mProjectionManager.addKeyEventHandler(
137                 Collections.singleton(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP),
138                 DIRECT_EXECUTOR,
139                 eventHandler1);
140         mProjectionManager.addKeyEventHandler(
141                 new ArraySet<>(
142                         Arrays.asList(
143                                 CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP,
144                                 CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN)),
145                 DIRECT_EXECUTOR,
146                 eventHandler2);
147 
148         mController.fireKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
149         verify(eventHandler1).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
150         verify(eventHandler2).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
151 
152         mController.fireKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN);
153         verify(eventHandler1, never())
154                 .onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN);
155         verify(eventHandler2).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN);
156 
157         mController.fireKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN);
158         verify(eventHandler1, never()).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN);
159         verify(eventHandler2, never()).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN);
160     }
161 
162     @Test
keyEventHandler_canRegisterAllEvents()163     public void keyEventHandler_canRegisterAllEvents() {
164         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
165                 mock(CarProjectionManager.ProjectionKeyEventHandler.class);
166 
167         Set<Integer> events = new ArraySet<>(CarProjectionManager.NUM_KEY_EVENTS);
168         for (int evt = 0; evt < CarProjectionManager.NUM_KEY_EVENTS; evt++) {
169             events.add(evt);
170         }
171 
172         mProjectionManager.addKeyEventHandler(events, DIRECT_EXECUTOR, eventHandler);
173 
174         for (int evt : events) {
175             mController.fireKeyEvent(evt);
176             verify(eventHandler).onKeyEvent(evt);
177         }
178     }
179 
180     @Test
keyEventHandler_eventsOutOfRange_throw()181     public void keyEventHandler_eventsOutOfRange_throw() {
182         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
183                 mock(CarProjectionManager.ProjectionKeyEventHandler.class);
184 
185         try {
186             mProjectionManager.addKeyEventHandler(Collections.singleton(-1), eventHandler);
187             fail();
188         } catch (IllegalArgumentException expected) { }
189 
190         try {
191             mProjectionManager.addKeyEventHandler(
192                     Collections.singleton(CarProjectionManager.NUM_KEY_EVENTS), eventHandler);
193             fail();
194         } catch (IllegalArgumentException expected) { }
195     }
196 
197     @Test
keyEventHandler_whenRegisteredAgain_replacesEventList()198     public void keyEventHandler_whenRegisteredAgain_replacesEventList() {
199         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
200                 mock(CarProjectionManager.ProjectionKeyEventHandler.class);
201         InOrder inOrder = inOrder(eventHandler);
202 
203         mProjectionManager.addKeyEventHandler(
204                 Collections.singleton(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP),
205                 DIRECT_EXECUTOR,
206                 eventHandler);
207         mController.fireKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
208         inOrder.verify(eventHandler)
209                 .onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
210 
211         mProjectionManager.addKeyEventHandler(
212                 Collections.singleton(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN),
213                 DIRECT_EXECUTOR,
214                 eventHandler);
215         mController.fireKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
216         inOrder.verify(eventHandler, never())
217                 .onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
218     }
219 
220     @Test
keyEventHandler_removed_noLongerFires()221     public void keyEventHandler_removed_noLongerFires() {
222         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
223                 mock(CarProjectionManager.ProjectionKeyEventHandler.class);
224 
225         mProjectionManager.addKeyEventHandler(
226                 Collections.singleton(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP),
227                 DIRECT_EXECUTOR,
228                 eventHandler);
229         mProjectionManager.removeKeyEventHandler(eventHandler);
230 
231         mController.fireKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
232         verify(eventHandler, never())
233                 .onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
234     }
235 
236     @Test
keyEventHandler_withAlternateExecutor_usesExecutor()237     public void keyEventHandler_withAlternateExecutor_usesExecutor() {
238         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
239                 mock(CarProjectionManager.ProjectionKeyEventHandler.class);
240         Executor executor = mock(Executor.class);
241         ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
242 
243         mProjectionManager.addKeyEventHandler(
244                 Collections.singleton(
245                         CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP),
246                 executor,
247                 eventHandler);
248 
249         mController.fireKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP);
250         verify(eventHandler, never()).onKeyEvent(anyInt());
251         verify(executor).execute(runnableCaptor.capture());
252 
253         runnableCaptor.getValue().run();
254         verify(eventHandler)
255                 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP);
256     }
257 
258     private static class ApCallback extends ProjectionAccessPointCallback {
259         CountDownLatch mStarted = new CountDownLatch(1);
260         CountDownLatch mFailed = new CountDownLatch(1);
261         int mFailureReason = -1;
262         SoftApConfiguration mSoftApConfiguration;
263 
264         @Override
onStarted(SoftApConfiguration softApConfiguration)265         public void onStarted(SoftApConfiguration softApConfiguration) {
266             mSoftApConfiguration = softApConfiguration;
267             mStarted.countDown();
268         }
269 
270         @Override
onStopped()271         public void onStopped() {
272         }
273 
274         @Override
onFailed(int reason)275         public void onFailed(int reason) {
276             mFailureReason = reason;
277             mFailed.countDown();
278         }
279     }
280 }
281