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