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