1 /* 2 * Copyright (C) 2021 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.cluster.renderer; 18 19 20 import static android.app.ActivityOptions.KEY_PACKAGE_NAME; 21 import static android.car.Car.CAR_EXTRA_CLUSTER_ACTIVITY_STATE; 22 import static android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER; 23 import static android.car.cluster.renderer.InstrumentClusterRenderingService.EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER; 24 import static android.content.pm.PackageManager.GET_RESOLVED_FILTER; 25 import static android.content.pm.PackageManager.PERMISSION_DENIED; 26 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 27 import static android.view.KeyEvent.KEYCODE_1; 28 29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 30 31 import static com.google.common.truth.Truth.assertThat; 32 33 import static org.junit.Assert.assertThrows; 34 import static org.mockito.ArgumentMatchers.any; 35 import static org.mockito.ArgumentMatchers.eq; 36 import static org.mockito.Mockito.spy; 37 import static org.mockito.Mockito.when; 38 39 import android.app.ActivityManager; 40 import android.app.ActivityOptions; 41 import android.car.CarLibLog; 42 import android.car.cluster.ClusterActivityState; 43 import android.car.test.mocks.AbstractExtendedMockitoTestCase; 44 import android.content.ActivityNotFoundException; 45 import android.content.ComponentName; 46 import android.content.ContentResolver; 47 import android.content.Intent; 48 import android.content.pm.ActivityInfo; 49 import android.content.pm.PackageInfo; 50 import android.content.pm.PackageManager; 51 import android.content.pm.ProviderInfo; 52 import android.content.pm.ResolveInfo; 53 import android.graphics.Bitmap; 54 import android.graphics.BitmapFactory; 55 import android.graphics.Rect; 56 import android.net.Uri; 57 import android.os.Binder; 58 import android.os.Bundle; 59 import android.os.IBinder; 60 import android.os.ParcelFileDescriptor; 61 import android.os.RemoteException; 62 import android.os.UserHandle; 63 import android.view.KeyEvent; 64 65 import androidx.annotation.NonNull; 66 import androidx.annotation.Nullable; 67 import androidx.test.platform.app.InstrumentationRegistry; 68 import androidx.test.rule.ServiceTestRule; 69 70 import com.google.common.collect.ImmutableList; 71 72 import org.junit.Before; 73 import org.junit.Rule; 74 import org.junit.Test; 75 import org.junit.runner.RunWith; 76 import org.mockito.Mock; 77 import org.mockito.junit.MockitoJUnitRunner; 78 79 import java.io.FileDescriptor; 80 import java.util.List; 81 82 83 /** Unit tests for {@link InstrumentClusterRenderingService}. */ 84 @RunWith(MockitoJUnitRunner.class) 85 public final class InstrumentClusterRenderingServiceTest extends AbstractExtendedMockitoTestCase { 86 @Mock 87 private NavigationRenderer mNavigationRenderer; 88 89 @Mock 90 private ParcelFileDescriptor mParcelFileDescriptor; 91 92 @Mock 93 private FileDescriptor mMockFileDescriptor; 94 95 @Rule 96 public final ServiceTestRule mServiceRule = new ServiceTestRule(); 97 98 private TestableInstrumentClusterRenderingService mService; 99 private TestableInstrumentClusterHelper mTestableInstrumentClusterHelper; 100 private IInstrumentCluster mRendererBinder; 101 InstrumentClusterRenderingServiceTest()102 public InstrumentClusterRenderingServiceTest() { 103 super(CarLibLog.TAG_CLUSTER); 104 } 105 106 @Override onSessionBuilder(CustomMockitoSessionBuilder builder)107 protected void onSessionBuilder(CustomMockitoSessionBuilder builder) { 108 builder.spyStatic(BitmapFactory.class); 109 } 110 111 @Before setup()112 public void setup() { 113 TestableInstrumentClusterRenderingService.setNavigationRenderer(mNavigationRenderer); 114 mTestableInstrumentClusterHelper = new TestableInstrumentClusterHelper(); 115 } 116 bindService(Intent intent)117 private void bindService(Intent intent) throws Exception { 118 intent.setComponent(ComponentName.createRelative(InstrumentationRegistry 119 .getInstrumentation().getContext(), 120 TestableInstrumentClusterRenderingService.class.getName())); 121 122 IBinder binder = mServiceRule.bindService(intent); 123 mService = ((TestableInstrumentClusterRenderingService.BinderWrapper) binder).getService(); 124 mRendererBinder = 125 (IInstrumentCluster) ((TestableInstrumentClusterRenderingService.BinderWrapper) 126 binder).getParentBinder(); 127 } 128 createBindIntentWithClusterHelper()129 private Intent createBindIntentWithClusterHelper() { 130 Bundle args = new Bundle(); 131 args.putBinder(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER, 132 mTestableInstrumentClusterHelper.asBinder()); 133 return new Intent().putExtra(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER, args); 134 } 135 136 @Test bindService_success()137 public void bindService_success() throws Exception { 138 bindService(createBindIntentWithClusterHelper()); 139 140 assertThat(mRendererBinder).isNotNull(); 141 } 142 143 @Test 144 @ExpectWtf bindService_withoutClusterHelper()145 public void bindService_withoutClusterHelper() throws Exception { 146 bindService(new Intent().putExtras(new Bundle())); 147 148 assertThat(mRendererBinder).isNotNull(); 149 } 150 createActivityResolveInfo(String packageName)151 private List<ResolveInfo> createActivityResolveInfo(String packageName) { 152 ResolveInfo resolveInfo = new ResolveInfo(); 153 resolveInfo.activityInfo = new ActivityInfo(); 154 resolveInfo.activityInfo.packageName = packageName; 155 resolveInfo.activityInfo.name = "Test"; 156 return ImmutableList.of(resolveInfo); 157 } 158 159 @Test setNavigationContextOwner_launchesNavigationComponent()160 public void setNavigationContextOwner_launchesNavigationComponent() throws Exception { 161 int userId = ActivityManager.getCurrentUser(); 162 String packageName = "com.test"; 163 bindService(createBindIntentWithClusterHelper()); 164 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 165 ClusterActivityState clusterActivityState = ClusterActivityState 166 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 167 mService.setClusterActivityState(clusterActivityState); 168 169 doReturn(new String[]{packageName}) 170 .when(mService.mSpyPackageManager).getPackagesForUid(userId); 171 doReturn(PERMISSION_GRANTED) 172 .when(mService.mSpyPackageManager).checkPermission( 173 PERMISSION_CAR_DISPLAY_IN_CLUSTER, packageName); 174 doReturn(createActivityResolveInfo(packageName)).when(mService.mSpyPackageManager) 175 .queryIntentActivitiesAsUser(any(), eq(GET_RESOLVED_FILTER), 176 eq(UserHandle.of(userId))); 177 178 mRendererBinder.setNavigationContextOwner(userId, 123); 179 180 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 181 assertThat(mService.mNumOfTimesOnNavigationComponentLaunchedCalled).isEqualTo(1); 182 } 183 184 @Test setNavigationContextOwner_navigationComponentAlreadyLaunched_doesNothing()185 public void setNavigationContextOwner_navigationComponentAlreadyLaunched_doesNothing() 186 throws Exception { 187 int userId = ActivityManager.getCurrentUser(); 188 bindService(createBindIntentWithClusterHelper()); 189 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 190 ClusterActivityState clusterActivityState = ClusterActivityState 191 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 192 mService.setClusterActivityState(clusterActivityState); 193 mockPackageManagerInteraction(userId); 194 195 mRendererBinder.setNavigationContextOwner(userId, 123); 196 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 197 mRendererBinder.setNavigationContextOwner(userId, 123); 198 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 199 200 assertThat(mService.mNumOfTimesOnNavigationComponentLaunchedCalled).isEqualTo(1); 201 } 202 203 @Test setNavigationContextOwner_noPackages_doesNothing()204 public void setNavigationContextOwner_noPackages_doesNothing() throws Exception { 205 int userId = ActivityManager.getCurrentUser(); 206 bindService(createBindIntentWithClusterHelper()); 207 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 208 ClusterActivityState clusterActivityState = ClusterActivityState 209 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 210 mService.setClusterActivityState(clusterActivityState); 211 doReturn(new String[]{}).when(mService.mSpyPackageManager).getPackagesForUid(userId); 212 213 mRendererBinder.setNavigationContextOwner(userId, 123); 214 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 215 216 assertThat(mService.mNumOfTimesOnNavigationComponentLaunchedCalled).isEqualTo(0); 217 } 218 219 @Test setNavigationContextOwner_clusterPermissionMissing_doesNothing()220 public void setNavigationContextOwner_clusterPermissionMissing_doesNothing() throws Exception { 221 int userId = ActivityManager.getCurrentUser(); 222 String packageName = "com.test"; 223 bindService(createBindIntentWithClusterHelper()); 224 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 225 ClusterActivityState clusterActivityState = ClusterActivityState 226 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 227 mService.setClusterActivityState(clusterActivityState); 228 doReturn(new String[]{packageName}) 229 .when(mService.mSpyPackageManager).getPackagesForUid(userId); 230 doReturn(PERMISSION_DENIED) 231 .when(mService.mSpyPackageManager).checkPermission( 232 PERMISSION_CAR_DISPLAY_IN_CLUSTER, packageName); 233 234 mRendererBinder.setNavigationContextOwner(userId, 123); 235 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 236 237 assertThat(mService.mNumOfTimesOnNavigationComponentLaunchedCalled).isEqualTo(0); 238 } 239 240 @Test setNavigationContextOwner_failureWhenStartingNavigationActivity()241 public void setNavigationContextOwner_failureWhenStartingNavigationActivity() 242 throws Exception { 243 int userId = ActivityManager.getCurrentUser(); 244 bindService(createBindIntentWithClusterHelper()); 245 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 246 ClusterActivityState clusterActivityState = ClusterActivityState 247 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 248 mService.setClusterActivityState(clusterActivityState); 249 mTestableInstrumentClusterHelper.mRuntimeFailureOnInteraction = true; 250 mockPackageManagerInteraction(userId); 251 252 mRendererBinder.setNavigationContextOwner(userId, 123); 253 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 254 255 assertThat(mService.mNumOfTimesOnNavigationComponentLaunchedCalled).isEqualTo(0); 256 } 257 258 @Test setNavigationContextOwner_activityNotFoundWhenStartingNavigationActivity()259 public void setNavigationContextOwner_activityNotFoundWhenStartingNavigationActivity() 260 throws Exception { 261 int userId = ActivityManager.getCurrentUser(); 262 bindService(createBindIntentWithClusterHelper()); 263 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 264 ClusterActivityState clusterActivityState = ClusterActivityState 265 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 266 mService.setClusterActivityState(clusterActivityState); 267 mTestableInstrumentClusterHelper.mActivityNotFoundFailureOnInteraction = true; 268 mockPackageManagerInteraction(userId); 269 270 mRendererBinder.setNavigationContextOwner(userId, 123); 271 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 272 273 assertThat(mService.mNumOfTimesOnNavigationComponentLaunchedCalled).isEqualTo(0); 274 } 275 276 @Test updateActivityState_notVisible_releasesNavigationComponent()277 public void updateActivityState_notVisible_releasesNavigationComponent() throws Exception { 278 int userId = ActivityManager.getCurrentUser(); 279 bindService(createBindIntentWithClusterHelper()); 280 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 281 ClusterActivityState clusterActivityState = ClusterActivityState 282 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 283 mService.setClusterActivityState(clusterActivityState); 284 mockPackageManagerInteraction(userId); 285 mRendererBinder.setNavigationContextOwner(userId, 123); 286 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 287 288 ClusterActivityState clusterActivityNewState = ClusterActivityState 289 .create(/* visible= */ false, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 290 mService.setClusterActivityState(clusterActivityNewState); 291 292 assertThat(mService.mOnNavigationComponentReleasedCalled).isTrue(); 293 } 294 mockPackageManagerInteraction(int userId)295 private void mockPackageManagerInteraction(int userId) { 296 String packageName = "com.test"; 297 doReturn(new String[]{packageName}) 298 .when(mService.mSpyPackageManager).getPackagesForUid(userId); 299 doReturn(PERMISSION_GRANTED) 300 .when(mService.mSpyPackageManager).checkPermission( 301 PERMISSION_CAR_DISPLAY_IN_CLUSTER, packageName); 302 doReturn(createActivityResolveInfo(packageName)).when(mService.mSpyPackageManager) 303 .queryIntentActivitiesAsUser(any(), eq(GET_RESOLVED_FILTER), 304 eq(UserHandle.of(userId))); 305 } 306 307 @Test clusterOnKeyEvent_triggersOnKeyEventOnService()308 public void clusterOnKeyEvent_triggersOnKeyEventOnService() throws Exception { 309 bindService(createBindIntentWithClusterHelper()); 310 311 mRendererBinder.onKeyEvent(new KeyEvent(KeyEvent.FLAG_EDITOR_ACTION, KEYCODE_1)); 312 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 313 314 assertThat(mService.mOnKeyEventCalled).isTrue(); 315 } 316 317 @Test onKeyEvent_doesNothing()318 public void onKeyEvent_doesNothing() throws Exception { 319 bindService(createBindIntentWithClusterHelper()); 320 321 mService.onKeyEvent(new KeyEvent(KeyEvent.FLAG_EDITOR_ACTION, KEYCODE_1)); 322 323 assertThat(mService.mOnKeyEventCalled).isTrue(); 324 } 325 326 @Test onNavigationComponentLaunched_doesNothing()327 public void onNavigationComponentLaunched_doesNothing() throws Exception { 328 bindService(createBindIntentWithClusterHelper()); 329 330 mService.onNavigationComponentLaunched(); 331 332 assertThat(mService.mNumOfTimesOnNavigationComponentLaunchedCalled).isEqualTo(1); 333 } 334 335 @Test onNavigationComponentReleased_doesNothing()336 public void onNavigationComponentReleased_doesNothing() throws Exception { 337 bindService(createBindIntentWithClusterHelper()); 338 339 mService.onNavigationComponentReleased(); 340 341 assertThat(mService.mOnNavigationComponentReleasedCalled).isTrue(); 342 } 343 344 @Test startFixedActivityMode_callsClusterHelper()345 public void startFixedActivityMode_callsClusterHelper() throws Exception { 346 ClusterActivityState clusterActivityState = ClusterActivityState 347 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 348 Intent intent = new Intent().putExtra(CAR_EXTRA_CLUSTER_ACTIVITY_STATE, 349 clusterActivityState.toBundle()); 350 Bundle activityOptionsBundle = new Bundle(); 351 activityOptionsBundle.putString(KEY_PACKAGE_NAME, "temp.pkg"); 352 ActivityOptions activityOptions = ActivityOptions.fromBundle(activityOptionsBundle); 353 int userId = ActivityManager.getCurrentUser(); 354 355 bindService(createBindIntentWithClusterHelper()); 356 boolean succeeded = mService.startFixedActivityModeForDisplayAndUser(intent, 357 activityOptions, 358 userId); 359 360 assertThat(succeeded).isTrue(); 361 assertThat(mTestableInstrumentClusterHelper.mStartFixedActivityModeUserId).isEqualTo( 362 userId); 363 assertThat(ClusterActivityState.fromBundle( 364 mTestableInstrumentClusterHelper.mStartFixedActivityIntent 365 .getBundleExtra(CAR_EXTRA_CLUSTER_ACTIVITY_STATE)).toString()) 366 .isEqualTo(clusterActivityState.toString()); 367 assertThat(mTestableInstrumentClusterHelper.mStartFixedActivityModeActivityOptions 368 .getString(KEY_PACKAGE_NAME)) 369 .isEqualTo("temp.pkg"); 370 } 371 372 @Test startFixedActivityMode_activityStateMissingInTheIntent()373 public void startFixedActivityMode_activityStateMissingInTheIntent() throws Exception { 374 bindService(createBindIntentWithClusterHelper()); 375 ClusterActivityState clusterActivityState = ClusterActivityState 376 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 377 mService.setClusterActivityState(clusterActivityState); 378 379 Bundle activityOptionsBundle = new Bundle(); 380 activityOptionsBundle.putString(KEY_PACKAGE_NAME, "temp.pkg"); 381 ActivityOptions activityOptions = ActivityOptions.fromBundle(activityOptionsBundle); 382 int userId = ActivityManager.getCurrentUser(); 383 384 boolean succeeded = mService.startFixedActivityModeForDisplayAndUser(new Intent(), 385 activityOptions, 386 userId); 387 388 assertThat(succeeded).isTrue(); 389 assertThat(mTestableInstrumentClusterHelper.mStartFixedActivityModeUserId).isEqualTo( 390 userId); 391 assertThat(ClusterActivityState.fromBundle( 392 mTestableInstrumentClusterHelper.mStartFixedActivityIntent 393 .getBundleExtra(CAR_EXTRA_CLUSTER_ACTIVITY_STATE)).toString()) 394 .isEqualTo(clusterActivityState.toString()); 395 assertThat(mTestableInstrumentClusterHelper.mStartFixedActivityModeActivityOptions 396 .getString(KEY_PACKAGE_NAME)) 397 .isEqualTo("temp.pkg"); 398 } 399 400 @Test startFixedActivityMode_clusterHelperFailure()401 public void startFixedActivityMode_clusterHelperFailure() throws Exception { 402 mTestableInstrumentClusterHelper.mRemoteFailureOnInteraction = true; 403 bindService(createBindIntentWithClusterHelper()); 404 ClusterActivityState clusterActivityState = ClusterActivityState 405 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4)); 406 mService.setClusterActivityState(clusterActivityState); 407 408 Bundle activityOptionsBundle = new Bundle(); 409 activityOptionsBundle.putString(KEY_PACKAGE_NAME, "temp.pkg"); 410 ActivityOptions activityOptions = ActivityOptions.fromBundle(activityOptionsBundle); 411 int userId = ActivityManager.getCurrentUser(); 412 413 boolean succeeded = mService.startFixedActivityModeForDisplayAndUser(new Intent(), 414 activityOptions, 415 userId); 416 417 assertThat(succeeded).isFalse(); 418 } 419 420 @ExpectWtf 421 @Test startFixedActivityMode_clusterHelperAbsent()422 public void startFixedActivityMode_clusterHelperAbsent() throws Exception { 423 int userId = ActivityManager.getCurrentUser(); 424 bindService(new Intent().putExtras(new Bundle())); 425 426 mService.onBind(new Intent().putExtras(new Bundle())); 427 boolean succeeded = mService.startFixedActivityModeForDisplayAndUser(new Intent(), 428 ActivityOptions.fromBundle(new Bundle()), 429 userId); 430 431 assertThat(succeeded).isFalse(); 432 } 433 434 @Test stopFixedActivityMode()435 public void stopFixedActivityMode() throws Exception { 436 int displayId = 1; 437 bindService(createBindIntentWithClusterHelper()); 438 439 mService.stopFixedActivityMode(displayId); 440 441 assertThat(mTestableInstrumentClusterHelper.mStopFixedActivityModeDisplayId).isEqualTo( 442 displayId); 443 } 444 445 @ExpectWtf 446 @Test stopFixedActivityMode_clusterHelperAbsent()447 public void stopFixedActivityMode_clusterHelperAbsent() throws Exception { 448 int displayId = 1; 449 bindService(new Intent().putExtras(new Bundle())); 450 451 mService.stopFixedActivityMode(displayId); 452 453 assertThat(mTestableInstrumentClusterHelper.mStopFixedActivityModeDisplayId).isNotEqualTo( 454 displayId); 455 } 456 457 @Test stopFixedActivityMode_clusterHelperFailure()458 public void stopFixedActivityMode_clusterHelperFailure() throws Exception { 459 int displayId = 1; 460 bindService(createBindIntentWithClusterHelper()); 461 mTestableInstrumentClusterHelper.mRemoteFailureOnInteraction = true; 462 463 mService.stopFixedActivityMode(displayId); 464 } 465 466 @Test getBitmap_invalidHeight()467 public void getBitmap_invalidHeight() throws Exception { 468 bindService(createBindIntentWithClusterHelper()); 469 470 assertThrows(IllegalArgumentException.class, 471 () -> mService.getBitmap(Uri.parse("content://temp.com/tempFile"), 100, -1) 472 ); 473 } 474 475 @Test getBitmap_invalidOffLanesAlpha()476 public void getBitmap_invalidOffLanesAlpha() throws Exception { 477 bindService(createBindIntentWithClusterHelper()); 478 479 assertThrows(IllegalArgumentException.class, 480 () -> mService.getBitmap(Uri.parse("content://temp.com/tempFile"), 100, 100, 1.1f) 481 ); 482 } 483 484 @Test getBitmap_contextOwnerMissing()485 public void getBitmap_contextOwnerMissing() throws Exception { 486 bindService(createBindIntentWithClusterHelper()); 487 488 Bitmap bitmap = 489 mService.getBitmap(Uri.parse("content://temp.com/tempFile"), 100, 100); 490 491 assertThat(bitmap).isNull(); 492 } 493 494 @Test getBitmap_unknownAuthority()495 public void getBitmap_unknownAuthority() throws Exception { 496 // Arrange 497 String packageName = "com.test"; 498 int userId = ActivityManager.getCurrentUser(); 499 bindService(createBindIntentWithClusterHelper()); 500 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 501 mService.setClusterActivityState(ClusterActivityState 502 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4))); 503 504 mockPackageManagerInteraction(userId); 505 mockAuthorityForPackage(null, packageName); 506 mRendererBinder.setNavigationContextOwner(userId, 123); 507 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 508 509 // Act 510 Bitmap bitmap = 511 mService.getBitmap(Uri.parse("content://temp.com/tempFile"), 100, 100); 512 513 // Assert 514 assertThat(bitmap).isNull(); 515 } 516 517 @Test getBitmap_success()518 public void getBitmap_success() throws Exception { 519 // Arrange 520 String packageName = "com.test"; 521 int userId = ActivityManager.getCurrentUser(); 522 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 523 524 bindService(createBindIntentWithClusterHelper()); 525 mService.setClusterActivityLaunchOptions(ActivityOptions.makeBasic()); 526 mService.setClusterActivityState(ClusterActivityState 527 .create(/* visible= */ true, /* unobscuredBounds= */new Rect(1, 2, 3, 4))); 528 529 mockPackageManagerInteraction(userId); 530 mockAuthorityForPackage("temp.com", packageName); 531 mockBitmapReadingFromFileDescriptor(bitmap); 532 mRendererBinder.setNavigationContextOwner(userId, 123); 533 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 534 535 // Act 536 Bitmap bitmapReturned = 537 mService.getBitmap(Uri.parse("content://temp.com/tempFile"), 100, 100); 538 539 // Assert 540 assertThat(bitmapReturned).isEqualTo(bitmap); 541 } 542 mockBitmapReadingFromFileDescriptor(Bitmap bitmap)543 private void mockBitmapReadingFromFileDescriptor(Bitmap bitmap) throws Exception { 544 doReturn(bitmap).when(() -> BitmapFactory.decodeFileDescriptor(mMockFileDescriptor)); 545 when(mParcelFileDescriptor.getFileDescriptor()).thenReturn(mMockFileDescriptor); 546 doReturn(mParcelFileDescriptor).when(mService.mSpyContentResolver) 547 .openFileDescriptor(any(), eq("r")); 548 } 549 mockAuthorityForPackage(@ullable String authority, String packageName)550 private void mockAuthorityForPackage(@Nullable String authority, String packageName) 551 throws Exception { 552 PackageInfo packageInfo = new PackageInfo(); 553 if (authority == null) { 554 packageInfo.providers = null; 555 } else { 556 ProviderInfo providerInfo = new ProviderInfo(); 557 providerInfo.authority = authority; 558 packageInfo.providers = new ProviderInfo[]{providerInfo}; 559 } 560 561 doReturn(packageInfo).when(mService.mSpyPackageManager) 562 .getPackageInfo(packageName, 563 PackageManager.GET_PROVIDERS | PackageManager.MATCH_ANY_USER); 564 } 565 566 /** 567 * A fake InstrumentClusterHelper stub used for testing. This can't be mocked because this 568 * is sent via binder. 569 */ 570 private static final class TestableInstrumentClusterHelper extends 571 IInstrumentClusterHelper.Stub { 572 Intent mStartFixedActivityIntent = null; 573 int mStartFixedActivityModeUserId = -1; 574 Bundle mStartFixedActivityModeActivityOptions = null; 575 boolean mRemoteFailureOnInteraction = false; 576 boolean mRuntimeFailureOnInteraction = false; 577 boolean mActivityNotFoundFailureOnInteraction = false; 578 int mStopFixedActivityModeDisplayId = -1; 579 580 @Override startFixedActivityModeForDisplayAndUser(Intent intent, Bundle activityOptionsBundle, int userId)581 public boolean startFixedActivityModeForDisplayAndUser(Intent intent, 582 Bundle activityOptionsBundle, int userId) throws RemoteException { 583 if (mRemoteFailureOnInteraction) { 584 throw new RemoteException("failure"); 585 } 586 if (mRuntimeFailureOnInteraction) { 587 throw new RuntimeException("failure"); 588 } 589 if (mActivityNotFoundFailureOnInteraction) { 590 throw new ActivityNotFoundException("failure"); 591 } 592 mStartFixedActivityIntent = intent; 593 mStartFixedActivityModeActivityOptions = activityOptionsBundle; 594 mStartFixedActivityModeUserId = userId; 595 return true; 596 } 597 598 @Override stopFixedActivityMode(int displayId)599 public void stopFixedActivityMode(int displayId) throws RemoteException { 600 if (mRemoteFailureOnInteraction) { 601 throw new RemoteException("failure"); 602 } 603 if (mRuntimeFailureOnInteraction) { 604 throw new RuntimeException("failure"); 605 } 606 if (mActivityNotFoundFailureOnInteraction) { 607 throw new ActivityNotFoundException("failure"); 608 } 609 mStopFixedActivityModeDisplayId = displayId; 610 } 611 } 612 613 /** 614 * The {@link InstrumentClusterRenderingService} can't be tested directly because it has no 615 * way to provide the service object. 616 * This class helps is providng the service object using {@link BinderWrapper} and also helps 617 * to spy on the package manager. 618 */ 619 public static final class TestableInstrumentClusterRenderingService extends 620 InstrumentClusterRenderingService { 621 private static NavigationRenderer sNavigationRenderer; 622 PackageManager mSpyPackageManager; 623 ContentResolver mSpyContentResolver; 624 boolean mOnKeyEventCalled; 625 int mNumOfTimesOnNavigationComponentLaunchedCalled; 626 boolean mOnNavigationComponentReleasedCalled; 627 628 private IBinder mParentBinder; 629 private final IBinder mBinder = new BinderWrapper(); 630 setNavigationRenderer(NavigationRenderer navigationRenderer)631 static void setNavigationRenderer(NavigationRenderer navigationRenderer) { 632 sNavigationRenderer = navigationRenderer; 633 } 634 635 /** 636 * A container class that contains the service object and the binder returned from the 637 * base {@link InstrumentClusterRenderingService}. 638 */ 639 public final class BinderWrapper extends Binder { getService()640 TestableInstrumentClusterRenderingService getService() { 641 return TestableInstrumentClusterRenderingService.this; 642 } 643 getParentBinder()644 IBinder getParentBinder() { 645 return mParentBinder; 646 } 647 } 648 649 @Override onBind(Intent intent)650 public IBinder onBind(Intent intent) { 651 mParentBinder = super.onBind(intent); 652 mSpyPackageManager = spy(super.getPackageManager()); 653 mSpyContentResolver = spy(super.getContentResolver()); 654 return mBinder; 655 } 656 657 @Nullable 658 @Override getNavigationRenderer()659 public NavigationRenderer getNavigationRenderer() { 660 return sNavigationRenderer; 661 } 662 663 @Override onKeyEvent(@onNull KeyEvent keyEvent)664 public void onKeyEvent(@NonNull KeyEvent keyEvent) { 665 mOnKeyEventCalled = true; 666 super.onKeyEvent(keyEvent); 667 } 668 669 @Override onNavigationComponentLaunched()670 public void onNavigationComponentLaunched() { 671 mNumOfTimesOnNavigationComponentLaunchedCalled++; 672 super.onNavigationComponentLaunched(); 673 } 674 675 @Override onNavigationComponentReleased()676 public void onNavigationComponentReleased() { 677 mOnNavigationComponentReleasedCalled = true; 678 super.onNavigationComponentReleased(); 679 } 680 681 @Override getPackageManager()682 public PackageManager getPackageManager() { 683 return mSpyPackageManager; 684 } 685 686 @Override getContentResolver()687 public ContentResolver getContentResolver() { 688 return mSpyContentResolver; 689 } 690 } 691 } 692