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 android.car.CarAppFocusManager.OnAppFocusChangedListener;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.ArgumentMatchers.anyBoolean;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.Mockito.anyInt;
26 import static org.mockito.Mockito.timeout;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29 
30 import android.car.Car;
31 import android.car.CarAppFocusManager;
32 import android.car.CarAppFocusManager.OnAppFocusChangedListener;
33 import android.car.CarAppFocusManager.OnAppFocusOwnershipCallback;
34 import android.content.Context;
35 
36 import androidx.test.filters.MediumTest;
37 import androidx.test.runner.AndroidJUnit4;
38 
39 import com.android.car.internal.StaticBinderInterface;
40 
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.mockito.Mock;
44 
45 @RunWith(AndroidJUnit4.class)
46 @MediumTest
47 public class CarAppFocusManagerTest extends MockedCarTestBase {
48 
49     private CarAppFocusManager mCarAppFocusManager;
50     private AppFocusService mAppFocusService;
51 
52     private static final int DEFAULT_FOREGROUND_ID = -1;
53 
54     private int mForegroundPid = DEFAULT_FOREGROUND_ID;
55     private int mForegroundUid = DEFAULT_FOREGROUND_ID;
56 
57     private static final int APP1_UID = 1041;
58     private static final int APP1_PID = 1043;
59     private static final int APP2_UID = 1072;
60     private static final int APP2_PID = 1074;
61     private static final int APP3_UID = 1111;
62     private static final int APP3_PID = 2222;
63 
64     private static final int DEFAULT_TIMEOUT_MS = 1000;
65 
66     @Mock private Context mContext;
67     @Mock private StaticBinderInterface mMockBinder;
68     @Mock private OnAppFocusOwnershipCallback mApp1Callback;
69     @Mock private OnAppFocusChangedListener mApp1Listener;
70     @Mock private OnAppFocusOwnershipCallback mApp2Callback;
71     @Mock private OnAppFocusChangedListener mApp2Listener;
72     @Mock private OnAppFocusOwnershipCallback mApp3Callback;
73     @Mock private OnAppFocusChangedListener mApp3Listener;
74     @Mock private SystemActivityMonitoringService mSystemActivityMonitoringService;
75 
76     @Override
configureFakeSystemInterface()77     protected void configureFakeSystemInterface() {
78         mAppFocusService = new AppFocusService(
79                 mContext, mSystemActivityMonitoringService, mMockBinder);
80         setAppFocusService(mAppFocusService);
81     }
82 
83     @Override
setUp()84     public void setUp() throws Exception {
85         super.setUp();
86 
87         mCarAppFocusManager =
88                 (CarAppFocusManager) getCar().getCarManager(Car.APP_FOCUS_SERVICE);
89         when(mSystemActivityMonitoringService.isInForeground(anyInt(), anyInt()))
90                 .thenAnswer((inv) -> {
91                     int pid = inv.getArgument(0);
92                     int uid = inv.getArgument(1);
93                     return (mForegroundPid == DEFAULT_FOREGROUND_ID || mForegroundPid == pid)
94                             && (mForegroundUid == DEFAULT_FOREGROUND_ID || mForegroundUid == uid);
95                 });
96         // Simulate an unprivileged app.
97         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER))
98                 .thenReturn(1);
99     }
100 
101     @Test
defaultState_noFocusesHeld()102     public void defaultState_noFocusesHeld() {
103         assertThat(mCarAppFocusManager.getActiveAppTypes()).isEmpty();
104     }
105 
106     @Test
requestNavFocus_noCurrentFocus_requestShouldSucceed()107     public void requestNavFocus_noCurrentFocus_requestShouldSucceed() {
108         int result = mCarAppFocusManager.requestAppFocus(
109                 CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, mApp1Callback);
110         assertThat(result).isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
111     }
112 
113     @Test
requestNavFocus_noCurrentFocus_callbackIsRun()114     public void requestNavFocus_noCurrentFocus_callbackIsRun() {
115         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
116                 mApp1Callback);
117 
118         verify(mApp1Callback, timeout(DEFAULT_TIMEOUT_MS))
119                 .onAppFocusOwnershipGranted(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
120     }
121 
122     @Test
requestNavFocus_noCurrentFocus_holdsOwnership()123     public void requestNavFocus_noCurrentFocus_holdsOwnership() {
124         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
125                 mApp1Callback);
126 
127         assertThat(
128                 mCarAppFocusManager
129                         .isOwningFocus(mApp1Callback, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION))
130                 .isTrue();
131     }
132 
133     @Test
requestNavFocus_noCurrentFocus_onlyNavActive()134     public void requestNavFocus_noCurrentFocus_onlyNavActive() {
135         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
136                 mApp1Callback);
137 
138         assertThat(mCarAppFocusManager.getActiveAppTypes())
139                 .isEqualTo(new int[] {CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION});
140     }
141 
setCallingApp(int uid, int pid)142     private void setCallingApp(int uid, int pid) {
143         when(mMockBinder.getCallingUid()).thenReturn(uid);
144         when(mMockBinder.getCallingPid()).thenReturn(pid);
145     }
146 
app2GainsFocus_app1BroughtToForeground()147     private void app2GainsFocus_app1BroughtToForeground() {
148         setCallingApp(APP2_UID, APP2_PID);
149         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
150                 mApp2Callback);
151         mForegroundUid = APP1_UID;
152         mForegroundPid = APP1_PID;
153         setCallingApp(APP2_UID, APP1_PID);
154     }
155 
156     @Test
requestNavFocus_currentOwnerInBackground_requestShouldSucceed()157     public void requestNavFocus_currentOwnerInBackground_requestShouldSucceed() {
158         app2GainsFocus_app1BroughtToForeground();
159 
160         assertThat(
161                 mCarAppFocusManager
162                         .requestAppFocus(
163                                 CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, mApp1Callback))
164                 .isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
165     }
166 
167     @Test
requestNavFocus_currentOwnerInBackground_callbackIsRun()168     public void requestNavFocus_currentOwnerInBackground_callbackIsRun() {
169         app2GainsFocus_app1BroughtToForeground();
170         mCarAppFocusManager
171                 .requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, mApp1Callback);
172 
173         verify(mApp1Callback, timeout(DEFAULT_TIMEOUT_MS))
174                 .onAppFocusOwnershipGranted(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
175     }
176 
177     @Test
requestNavFocus_currentOwnerInBackground_holdsOwnership()178     public void requestNavFocus_currentOwnerInBackground_holdsOwnership() {
179         app2GainsFocus_app1BroughtToForeground();
180         mCarAppFocusManager
181                 .requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, mApp1Callback);
182 
183         assertThat(
184                 mCarAppFocusManager
185                         .isOwningFocus(mApp1Callback, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION))
186                 .isTrue();
187     }
188 
189     @Test
requestNavFocus_currentOwnerInForeground_requestFails()190     public void requestNavFocus_currentOwnerInForeground_requestFails() {
191         setCallingApp(APP2_UID, APP2_PID);
192         mForegroundUid = APP2_UID;
193         mForegroundPid = APP2_PID;
194         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
195                 mApp2Callback);
196         setCallingApp(APP1_UID, APP1_PID);
197 
198         assertThat(
199                 mCarAppFocusManager
200                         .requestAppFocus(
201                                 CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, mApp1Callback))
202                 .isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_FAILED);
203     }
204 
205     @Test
requestAppFocus_callingAppNotified()206     public void requestAppFocus_callingAppNotified() {
207         setCallingApp(APP1_UID, APP1_PID);
208         mCarAppFocusManager
209                 .addFocusListener(mApp1Listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
210         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
211                 mApp1Callback);
212 
213         verify(mApp1Listener, timeout(DEFAULT_TIMEOUT_MS))
214                 .onAppFocusChanged(eq(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION), anyBoolean());
215     }
216 
217     @Test
requestAppFocus_otherAppNotified()218     public void requestAppFocus_otherAppNotified() {
219         setCallingApp(APP2_UID, APP2_PID);
220         mCarAppFocusManager
221                 .addFocusListener(mApp2Listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
222         setCallingApp(APP1_UID, APP1_PID);
223         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
224                 mApp1Callback);
225 
226         verify(mApp2Listener, timeout(DEFAULT_TIMEOUT_MS))
227                 .onAppFocusChanged(eq(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION), eq(true));
228     }
229 
230     @Test
requestAppFocus_focusLost_otherAppRequest_callbackRun()231     public void requestAppFocus_focusLost_otherAppRequest_callbackRun() {
232         setCallingApp(APP2_UID, APP2_PID);
233         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
234                 mApp2Callback);
235         setCallingApp(APP1_UID, APP1_PID);
236         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
237                 mApp1Callback);
238 
239         verify(mApp2Callback, timeout(DEFAULT_TIMEOUT_MS))
240                 .onAppFocusOwnershipLost(eq(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
241     }
242 
243     @Test
abandonAppFocus_callingAppNotified()244     public void abandonAppFocus_callingAppNotified() {
245         setCallingApp(APP1_UID, APP1_PID);
246         mCarAppFocusManager
247                 .addFocusListener(mApp1Listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
248         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
249                 mApp1Callback);
250         mCarAppFocusManager
251                 .abandonAppFocus(mApp1Callback, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
252 
253         verify(mApp1Listener, timeout(DEFAULT_TIMEOUT_MS))
254                 .onAppFocusChanged(eq(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION), eq(false));
255     }
256 
257     @Test
abandonAppFocus_otherAppNotified()258     public void abandonAppFocus_otherAppNotified() {
259         setCallingApp(APP2_UID, APP2_PID);
260         mCarAppFocusManager
261                 .addFocusListener(mApp2Listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
262         setCallingApp(APP1_UID, APP1_PID);
263         mCarAppFocusManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
264                 mApp1Callback);
265         mCarAppFocusManager
266                 .abandonAppFocus(mApp1Callback, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
267 
268         verify(mApp2Listener, timeout(DEFAULT_TIMEOUT_MS))
269                 .onAppFocusChanged(eq(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION), eq(false));
270     }
271 
272     @Test
gainAppFocus_multipleListenersRegistered_bothUnownedTrigger()273     public void gainAppFocus_multipleListenersRegistered_bothUnownedTrigger() {
274         setCallingApp(APP1_UID, APP1_PID);
275         mCarAppFocusManager
276                 .addFocusListener(mApp1Listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
277         setCallingApp(APP2_UID, APP2_PID);
278         mCarAppFocusManager
279                 .addFocusListener(mApp2Listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
280         setCallingApp(APP3_UID, APP3_PID);
281         mCarAppFocusManager
282                 .addFocusListener(mApp3Listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
283         mCarAppFocusManager
284                 .requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, mApp3Callback);
285 
286         verify(mApp1Listener, timeout(DEFAULT_TIMEOUT_MS))
287                 .onAppFocusChanged(eq(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION), eq(true));
288         verify(mApp2Listener, timeout(DEFAULT_TIMEOUT_MS))
289                 .onAppFocusChanged(eq(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION), eq(true));
290         verify(mApp3Listener, timeout(DEFAULT_TIMEOUT_MS))
291                 .onAppFocusChanged(eq(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION), eq(true));
292     }
293 }
294