• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 android.car.apitest;
17 
18 import static android.car.CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED;
19 import static android.car.CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.junit.Assert.assertThrows;
24 
25 import android.car.Car;
26 import android.car.CarAppFocusManager;
27 import android.car.test.ApiCheckerRule.Builder;
28 import android.content.Context;
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.Process;
32 import android.test.suitebuilder.annotation.MediumTest;
33 import android.util.Log;
34 
35 import androidx.test.filters.FlakyTest;
36 import androidx.test.filters.RequiresDevice;
37 
38 import org.junit.After;
39 import org.junit.Before;
40 import org.junit.Test;
41 
42 import java.util.concurrent.Semaphore;
43 import java.util.concurrent.TimeUnit;
44 
45 @MediumTest
46 public final class CarAppFocusManagerTest extends CarApiTestBase {
47     private static final String TAG = CarAppFocusManagerTest.class.getSimpleName();
48 
49     private static final long NEGATIVE_CASE_WAIT_TIMEOUT_MS = 100L;
50 
51     private CarAppFocusManager mManager;
52 
53     private final LooperThread mEventThread = new LooperThread();
54 
55     // TODO(b/242350638): add missing annotations, remove (on child bug of 242350638)
56     @Override
configApiCheckerRule(Builder builder)57     protected void configApiCheckerRule(Builder builder) {
58         Log.w(TAG, "Disabling API requirements check");
59         builder.disableAnnotationsCheck();
60     }
61 
62     @Before
setUp()63     public void setUp() throws Exception {
64         mManager = (CarAppFocusManager) getCar().getCarManager(Car.APP_FOCUS_SERVICE);
65         assertThat(mManager).isNotNull();
66         abandonAllAppFocuses();
67 
68         mEventThread.start();
69         mEventThread.waitForReadyState();
70     }
71 
72     @After
tearDown()73     public void tearDown() throws Exception {
74         abandonAllAppFocuses();
75     }
76 
abandonAllAppFocuses()77     private void abandonAllAppFocuses() throws Exception {
78         // Request all application focuses and abandon them to ensure no active context is present
79         // when test starts and ends.
80         int[] activeTypes =  mManager.getActiveAppTypes();
81         FocusOwnershipCallback owner = new FocusOwnershipCallback(/* assertEventThread= */ false);
82         for (int i = 0; i < activeTypes.length; i++) {
83             mManager.requestAppFocus(activeTypes[i], owner);
84             owner.waitForOwnershipGrantAndAssert(NEGATIVE_CASE_WAIT_TIMEOUT_MS, activeTypes[i]);
85             mManager.abandonAppFocus(owner, activeTypes[i]);
86             owner.waitForOwnershipLossAndAssert(
87                     NEGATIVE_CASE_WAIT_TIMEOUT_MS, activeTypes[i]);
88         }
89     }
90 
91     @Test
testSetActiveNullListener()92     public void testSetActiveNullListener() throws Exception {
93         assertThrows(IllegalArgumentException.class,
94                 () -> mManager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, null));
95     }
96 
97     @Test
testRegisterNull()98     public void testRegisterNull() throws Exception {
99         assertThrows(IllegalArgumentException.class, () -> mManager.addFocusListener(null, 0));
100     }
101 
102     @Test
testRegisterUnregister()103     public void testRegisterUnregister() throws Exception {
104         FocusChangedListener listener = new FocusChangedListener();
105         FocusChangedListener listener2 = new FocusChangedListener();
106         mManager.addFocusListener(listener, 1);
107         mManager.addFocusListener(listener2, 1);
108         mManager.removeFocusListener(listener);
109         mManager.removeFocusListener(listener2);
110         mManager.removeFocusListener(listener2);  // Double-unregister is OK
111     }
112 
113     @Test
testRegisterUnregisterSpecificApp()114     public void testRegisterUnregisterSpecificApp() throws Exception {
115         FocusChangedListener listener1 = new FocusChangedListener();
116         FocusChangedListener listener2 = new FocusChangedListener();
117 
118         CarAppFocusManager manager = createManager();
119         manager.addFocusListener(listener1, APP_FOCUS_TYPE_NAVIGATION);
120         manager.addFocusListener(listener2, APP_FOCUS_TYPE_NAVIGATION);
121 
122         manager.removeFocusListener(listener1, APP_FOCUS_TYPE_NAVIGATION);
123 
124         assertThat(manager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, new FocusOwnershipCallback()))
125                 .isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
126 
127         // Unregistred from nav app, no events expected.
128         assertThat(listener1.waitForFocusChangeAndAssert(
129                 NEGATIVE_CASE_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION,
130                 true)).isFalse();
131         assertThat(listener2.waitForFocusChangeAndAssert(
132                 DEFAULT_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
133 
134         manager.removeFocusListener(listener2, APP_FOCUS_TYPE_NAVIGATION);
135         // Used a new FocusOwnershipCallback to generate a new focus change event.
136         assertThat(manager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, new FocusOwnershipCallback()))
137                 .isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
138         assertThat(listener2.waitForFocusChangeAndAssert(
139                 NEGATIVE_CASE_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION,
140                 true)).isFalse();
141 
142         manager.removeFocusListener(listener2, 2);
143         manager.removeFocusListener(listener2, 2);    // Double-unregister is OK
144     }
145 
146     @Test
147     @FlakyTest
testFocusChange()148     public void testFocusChange() throws Exception {
149         CarAppFocusManager manager1 = createManager();
150         CarAppFocusManager manager2 = createManager();
151         assertThat(manager2).isNotNull();
152 
153         assertThat(manager1.getActiveAppTypes()).asList().isEmpty();
154         FocusChangedListener change1 = new FocusChangedListener();
155         FocusChangedListener change2 = new FocusChangedListener();
156         FocusOwnershipCallback owner1 = new FocusOwnershipCallback();
157         FocusOwnershipCallback owner2 = new FocusOwnershipCallback();
158         manager1.addFocusListener(change1, APP_FOCUS_TYPE_NAVIGATION);
159         manager2.addFocusListener(change2, APP_FOCUS_TYPE_NAVIGATION);
160 
161 
162         assertThat(manager1.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, owner1))
163                 .isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
164         assertThat(owner1.waitForOwnershipGrantAndAssert(
165         DEFAULT_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
166         int expectedFocus  = APP_FOCUS_TYPE_NAVIGATION;
167         assertThat(manager1.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
168         assertThat(manager2.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
169         assertThat(manager1.isOwningFocus(owner1, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
170         assertThat(manager2.isOwningFocus(owner2, APP_FOCUS_TYPE_NAVIGATION)).isFalse();
171         assertThat(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
172         APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
173         assertThat(change1.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
174         APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
175 
176         expectedFocus = APP_FOCUS_TYPE_NAVIGATION;
177         assertThat(manager1.isOwningFocus(owner1, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
178         assertThat(manager2.isOwningFocus(owner2, APP_FOCUS_TYPE_NAVIGATION)).isFalse();
179         assertThat(manager1.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
180         assertThat(manager2.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
181 
182         // this should be no-op
183         change1.reset();
184         change2.reset();
185         assertThat(manager1.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, owner1))
186                 .isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
187         assertThat(owner1.waitForOwnershipGrantAndAssert(
188                 DEFAULT_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
189 
190         assertThat(manager1.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
191         assertThat(manager2.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
192         assertThat(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
193                 APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
194         assertThat(change1.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
195                 APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
196 
197         assertThat(manager2.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, owner2))
198                 .isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
199         assertThat(owner2.waitForOwnershipGrantAndAssert(
200                 DEFAULT_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
201 
202         assertThat(manager1.isOwningFocus(owner1, APP_FOCUS_TYPE_NAVIGATION)).isFalse();
203         assertThat(manager2.isOwningFocus(owner2, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
204         assertThat(manager1.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
205         assertThat(manager2.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
206         assertThat(owner1.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
207                 APP_FOCUS_TYPE_NAVIGATION)).isTrue();
208 
209         // no-op as it is not owning it
210         change1.reset();
211         change2.reset();
212         manager1.abandonAppFocus(owner1, APP_FOCUS_TYPE_NAVIGATION);
213         assertThat(manager1.isOwningFocus(owner1, APP_FOCUS_TYPE_NAVIGATION)).isFalse();
214         assertThat(manager2.isOwningFocus(owner2, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
215         assertThat(manager1.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
216         assertThat(manager2.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
217 
218         change1.reset();
219         change2.reset();
220         assertThat(manager1.isOwningFocus(owner1, APP_FOCUS_TYPE_NAVIGATION)).isFalse();
221         assertThat(manager2.isOwningFocus(owner2, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
222         expectedFocus = APP_FOCUS_TYPE_NAVIGATION;
223         assertThat(manager1.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
224         assertThat(manager2.getActiveAppTypes()).asList().containsExactly(expectedFocus).inOrder();
225 
226         change1.reset();
227         change2.reset();
228         manager2.abandonAppFocus(owner2, APP_FOCUS_TYPE_NAVIGATION);
229         assertThat(manager1.isOwningFocus(owner1, APP_FOCUS_TYPE_NAVIGATION)).isFalse();
230         assertThat(manager2.isOwningFocus(owner2, APP_FOCUS_TYPE_NAVIGATION)).isFalse();
231         assertThat(manager1.getActiveAppTypes()).asList().isEmpty();
232         assertThat(manager2.getActiveAppTypes()).asList().isEmpty();
233         assertThat(change1.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
234                 APP_FOCUS_TYPE_NAVIGATION, false)).isTrue();
235 
236         manager1.removeFocusListener(change1);
237         manager2.removeFocusListener(change2);
238     }
239 
240     @RequiresDevice
241     @Test
testFilter()242     public void testFilter() throws Exception {
243         CarAppFocusManager manager1 = createManager(getContext(), mEventThread);
244         CarAppFocusManager manager2 = createManager(getContext(), mEventThread);
245 
246         assertThat(manager1.getActiveAppTypes()).asList().isEmpty();
247         assertThat(manager2.getActiveAppTypes()).asList().isEmpty();
248 
249         FocusChangedListener listener1 = new FocusChangedListener();
250         FocusChangedListener listener2 = new FocusChangedListener();
251         FocusOwnershipCallback owner = new FocusOwnershipCallback();
252         manager1.addFocusListener(listener1, APP_FOCUS_TYPE_NAVIGATION);
253         manager2.addFocusListener(listener2, APP_FOCUS_TYPE_NAVIGATION);
254 
255         assertThat(manager1.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, owner))
256                 .isEqualTo(APP_FOCUS_REQUEST_SUCCEEDED);
257         assertThat(owner.waitForOwnershipGrantAndAssert(
258                 DEFAULT_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
259 
260         assertThat(listener1.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
261                 APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
262         assertThat(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
263                 APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
264 
265         listener1.reset();
266         listener2.reset();
267         manager1.abandonAppFocus(owner, APP_FOCUS_TYPE_NAVIGATION);
268         assertThat(listener1.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
269         APP_FOCUS_TYPE_NAVIGATION, false)).isTrue();
270         assertThat(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
271         APP_FOCUS_TYPE_NAVIGATION, false)).isTrue();
272     }
273 
274     @Test
testGetAppTypeOwner()275     public void testGetAppTypeOwner() throws Exception {
276         CarAppFocusManager manager = createManager(getContext(), mEventThread);
277 
278         assertThat(manager.getAppTypeOwner(APP_FOCUS_TYPE_NAVIGATION)).isNull();
279 
280         FocusOwnershipCallback owner = new FocusOwnershipCallback();
281         assertThat(manager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, owner))
282                 .isEqualTo(APP_FOCUS_REQUEST_SUCCEEDED);
283 
284         String[] myPackages = getContext().getPackageManager().getPackagesForUid(Process.myUid());
285         assertThat(manager.getAppTypeOwner(APP_FOCUS_TYPE_NAVIGATION))
286                 .containsExactlyElementsIn(myPackages);
287 
288         manager.abandonAppFocus(owner, APP_FOCUS_TYPE_NAVIGATION);
289 
290         assertThat(manager.getAppTypeOwner(APP_FOCUS_TYPE_NAVIGATION)).isNull();
291     }
292 
createManager()293     private CarAppFocusManager createManager() throws InterruptedException {
294         return createManager(getContext(), mEventThread);
295     }
296 
createManager(Context context, LooperThread eventThread)297     private static CarAppFocusManager createManager(Context context,
298             LooperThread eventThread) throws InterruptedException {
299         Car car = createCar(context, eventThread);
300         CarAppFocusManager manager = (CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
301         assertThat(manager).isNotNull();
302         return manager;
303     }
304 
createCar(Context context, LooperThread eventThread)305     private static Car createCar(Context context, LooperThread eventThread)
306             throws InterruptedException {
307         DefaultServiceConnectionListener connectionListener =
308                 new DefaultServiceConnectionListener();
309         Car car = Car.createCar(context, connectionListener, eventThread.mHandler);
310         assertThat(car).isNotNull();
311         car.connect();
312         connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
313         return car;
314     }
315 
316     @RequiresDevice
317     @Test
testMultipleChangeListenersPerManager()318     public void testMultipleChangeListenersPerManager() throws Exception {
319         CarAppFocusManager manager = createManager();
320         FocusChangedListener listener = new FocusChangedListener();
321         FocusChangedListener listener2 = new FocusChangedListener();
322         FocusOwnershipCallback owner = new FocusOwnershipCallback();
323         manager.addFocusListener(listener, APP_FOCUS_TYPE_NAVIGATION);
324         manager.addFocusListener(listener2, APP_FOCUS_TYPE_NAVIGATION);
325 
326         assertThat(manager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, owner))
327                 .isEqualTo(APP_FOCUS_REQUEST_SUCCEEDED);
328         assertThat(owner.waitForOwnershipGrantAndAssert(
329                 DEFAULT_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION)).isTrue();
330 
331         assertThat(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
332                 APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
333         assertThat(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
334                 APP_FOCUS_TYPE_NAVIGATION, true)).isTrue();
335 
336         listener.reset();
337         listener2.reset();
338         manager.abandonAppFocus(owner, APP_FOCUS_TYPE_NAVIGATION);
339         assertThat(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
340                 APP_FOCUS_TYPE_NAVIGATION, false)).isTrue();
341         assertThat(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
342                 APP_FOCUS_TYPE_NAVIGATION, false)).isTrue();
343     }
344 
345     private final class FocusChangedListener
346             implements CarAppFocusManager.OnAppFocusChangedListener {
347         private volatile int mLastChangeAppType;
348         private volatile boolean mLastChangeAppActive;
349         private volatile Semaphore mChangeWait = new Semaphore(0);
350 
waitForFocusChangeAndAssert(long timeoutMs, int expectedAppType, boolean expectedAppActive)351         boolean waitForFocusChangeAndAssert(long timeoutMs, int expectedAppType,
352                 boolean expectedAppActive) throws Exception {
353             if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
354                 return false;
355             }
356             assertThat(mLastChangeAppType).isEqualTo(expectedAppType);
357             assertThat(mLastChangeAppActive).isEqualTo(expectedAppActive);
358             return true;
359         }
360 
reset()361         void reset() {
362             mLastChangeAppType = 0;
363             mLastChangeAppActive = false;
364             mChangeWait.drainPermits();
365         }
366 
367         @Override
onAppFocusChanged(int appType, boolean active)368         public void onAppFocusChanged(int appType, boolean active) {
369             assertEventThread();
370             mLastChangeAppType = appType;
371             mLastChangeAppActive = active;
372             mChangeWait.release();
373         }
374     }
375 
376     private final class FocusOwnershipCallback
377             implements CarAppFocusManager.OnAppFocusOwnershipCallback {
378         private int mLastLossEvent;
379         private final Semaphore mLossEventWait = new Semaphore(0);
380         private int mLastGrantEvent;
381         private final Semaphore mGrantEventWait = new Semaphore(0);
382         private final boolean mAssertEventThread;
383 
FocusOwnershipCallback(boolean assertEventThread)384         private FocusOwnershipCallback(boolean assertEventThread) {
385             mAssertEventThread = assertEventThread;
386         }
387 
FocusOwnershipCallback()388         private FocusOwnershipCallback() {
389             this(true);
390         }
391 
waitForOwnershipLossAndAssert(long timeoutMs, int expectedAppType)392         boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedAppType)
393                 throws Exception {
394             if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
395                 return false;
396             }
397             assertThat(mLastLossEvent).isEqualTo(expectedAppType);
398             return true;
399         }
400 
waitForOwnershipGrantAndAssert(long timeoutMs, int expectedAppType)401         boolean waitForOwnershipGrantAndAssert(long timeoutMs, int expectedAppType)
402                 throws Exception {
403             if (!mGrantEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
404                 return false;
405             }
406             assertThat(mLastGrantEvent).isEqualTo(expectedAppType);
407             return true;
408         }
409 
410         @Override
onAppFocusOwnershipLost(int appType)411         public void onAppFocusOwnershipLost(int appType) {
412             Log.i(TAG, "onAppFocusOwnershipLost " + appType);
413             if (mAssertEventThread) {
414                 assertEventThread();
415             }
416             mLastLossEvent = appType;
417             mLossEventWait.release();
418         }
419 
420         @Override
onAppFocusOwnershipGranted(int appType)421         public void onAppFocusOwnershipGranted(int appType) {
422             Log.i(TAG, "onAppFocusOwnershipGranted " + appType);
423             mLastGrantEvent = appType;
424             mGrantEventWait.release();
425         }
426     }
427 
assertEventThread()428     private void assertEventThread() {
429         assertThat(Thread.currentThread()).isSameInstanceAs(mEventThread);
430     }
431 
432     private static final class LooperThread extends Thread {
433 
434         private final Object mReadySync = new Object();
435 
436         volatile Handler mHandler;
437 
438         @Override
run()439         public void run() {
440             Looper.prepare();
441             mHandler = new Handler();
442 
443             synchronized (mReadySync) {
444                 mReadySync.notifyAll();
445             }
446 
447             Looper.loop();
448         }
449 
waitForReadyState()450         void waitForReadyState() throws InterruptedException {
451             synchronized (mReadySync) {
452                 mReadySync.wait(DEFAULT_WAIT_TIMEOUT_MS);
453             }
454         }
455     }
456 }
457