1 /* 2 * Copyright (C) 2017 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.tv; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.robolectric.Shadows.shadowOf; 22 23 import android.app.Activity; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.ServiceInfo; 29 import android.media.tv.TvInputInfo; 30 import android.os.Build.VERSION; 31 import android.os.Build.VERSION_CODES; 32 import android.support.annotation.Nullable; 33 34 import androidx.test.core.app.ApplicationProvider; 35 36 import com.android.tv.common.CommonConstants; 37 import com.android.tv.common.dagger.ApplicationModule; 38 import com.android.tv.common.flags.impl.DefaultLegacyFlags; 39 import com.android.tv.common.flags.impl.SettableFlagsModule; 40 import com.android.tv.common.flags.proto.TypedFeatures.StringListParam; 41 import com.android.tv.common.util.CommonUtils; 42 import com.android.tv.data.ChannelDataManager; 43 import com.android.tv.data.epg.EpgFetcher; 44 import com.android.tv.features.TvFeatures; 45 import com.android.tv.modules.TvSingletonsModule; 46 import com.android.tv.testing.FakeTvInputManager; 47 import com.android.tv.testing.FakeTvInputManagerHelper; 48 import com.android.tv.testing.TestSingletonApp; 49 import com.android.tv.testing.constants.ConfigConstants; 50 import com.android.tv.tunerinputcontroller.BuiltInTunerManager; 51 import com.android.tv.util.AsyncDbTask.DbExecutor; 52 import com.android.tv.util.SetupUtils; 53 import com.android.tv.util.TvInputManagerHelper; 54 55 import com.google.android.tv.partner.support.EpgContract; 56 import com.google.common.base.Optional; 57 58 import dagger.Component; 59 import dagger.Module; 60 import dagger.Provides; 61 import dagger.android.AndroidInjectionModule; 62 import dagger.android.AndroidInjector; 63 import dagger.android.DispatchingAndroidInjector; 64 import dagger.android.HasAndroidInjector; 65 66 import org.junit.After; 67 import org.junit.Before; 68 import org.junit.Test; 69 import org.junit.runner.RunWith; 70 import org.mockito.ArgumentMatchers; 71 import org.mockito.Mockito; 72 import org.robolectric.Robolectric; 73 import org.robolectric.RobolectricTestRunner; 74 import org.robolectric.android.controller.ActivityController; 75 import org.robolectric.android.util.concurrent.RoboExecutorService; 76 import org.robolectric.annotation.Config; 77 import org.robolectric.shadows.ShadowActivity; 78 79 import java.util.concurrent.Executor; 80 81 import javax.inject.Inject; 82 import javax.inject.Singleton; 83 84 /** Tests for {@link SetupPassthroughActivity}. */ 85 @RunWith(RobolectricTestRunner.class) 86 @Config(sdk = ConfigConstants.SDK, application = SetupPassthroughActivityTest.MyTestApp.class) 87 public class SetupPassthroughActivityTest { 88 89 private static final int REQUEST_START_SETUP_ACTIVITY = 200; 90 91 private MyTestApp testSingletonApp; 92 93 private TvInputInfo testInput; 94 95 @Before setup()96 public void setup() { 97 testInput = createMockInput("com.example/.Input"); 98 testSingletonApp = (MyTestApp) ApplicationProvider.getApplicationContext(); 99 testSingletonApp.flagsModule.legacyFlags = 100 DefaultLegacyFlags.builder().enableQaFeatures(true).build(); 101 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager(); 102 } 103 104 @After after()105 public void after() { 106 TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.resetForTests(); 107 } 108 109 @Test create_emptyIntent()110 public void create_emptyIntent() { 111 SetupPassthroughActivity activity = 112 Robolectric.buildActivity(SetupPassthroughActivity.class, new Intent()) 113 .create() 114 .get(); 115 ShadowActivity.IntentForResult shadowIntent = 116 shadowOf(activity).getNextStartedActivityForResult(); 117 // Since there is no inputs, the next activity should not be started. 118 assertThat(shadowIntent).isNull(); 119 assertThat(activity.isFinishing()).isTrue(); 120 } 121 122 @Test create_noInputs()123 public void create_noInputs() { 124 SetupPassthroughActivity activity = createSetupActivityFor("com.example/.Input"); 125 ShadowActivity.IntentForResult shadowIntent = 126 shadowOf(activity).getNextStartedActivityForResult(); 127 // Since there is no inputs, the next activity should not be started. 128 assertThat(shadowIntent).isNull(); 129 assertThat(activity.isFinishing()).isTrue(); 130 } 131 132 @Test create_inputNotFound()133 public void create_inputNotFound() { 134 testSingletonApp.tvInputManagerHelper = new FakeTvInputManagerHelper(testSingletonApp); 135 testSingletonApp.tvInputManagerHelper.start(); 136 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 137 SetupPassthroughActivity activity = createSetupActivityFor("com.example/.Other"); 138 ShadowActivity.IntentForResult shadowIntent = 139 shadowOf(activity).getNextStartedActivityForResult(); 140 // Since the input is not found, the next activity should not be started. 141 assertThat(shadowIntent).isNull(); 142 assertThat(activity.isFinishing()).isTrue(); 143 } 144 145 @Test create_validInput()146 public void create_validInput() { 147 testSingletonApp.tvInputManagerHelper.start(); 148 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 149 SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId()); 150 151 ShadowActivity.IntentForResult shadowIntent = 152 shadowOf(activity).getNextStartedActivityForResult(); 153 assertThat(shadowIntent).isNotNull(); 154 assertThat(shadowIntent.options).isNull(); 155 assertThat(shadowIntent.intent.getExtras()).isNotNull(); 156 assertThat(shadowIntent.requestCode).isEqualTo(REQUEST_START_SETUP_ACTIVITY); 157 assertThat(activity.isFinishing()).isFalse(); 158 } 159 160 @Test create_trustedCallingPackage()161 public void create_trustedCallingPackage() { 162 testSingletonApp.tvInputManagerHelper.start(); 163 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 164 165 ActivityController<SetupPassthroughActivity> activityController = 166 Robolectric.buildActivity( 167 SetupPassthroughActivity.class, 168 CommonUtils.createSetupIntent(new Intent(), testInput.getId())); 169 SetupPassthroughActivity activity = activityController.get(); 170 ShadowActivity shadowActivity = shadowOf(activity); 171 shadowActivity.setCallingActivity(createTrustedComponent()); 172 activityController.create(); 173 174 ShadowActivity.IntentForResult shadowIntent = 175 shadowActivity.getNextStartedActivityForResult(); 176 assertThat(shadowIntent).isNotNull(); 177 assertThat(shadowIntent.options).isNull(); 178 assertThat(shadowIntent.intent.getExtras()).isNotNull(); 179 assertThat(shadowIntent.requestCode).isEqualTo(REQUEST_START_SETUP_ACTIVITY); 180 assertThat(activity.isFinishing()).isFalse(); 181 } 182 183 @Test create_nonTrustedCallingPackage()184 public void create_nonTrustedCallingPackage() { 185 testSingletonApp.tvInputManagerHelper.start(); 186 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 187 188 ActivityController<SetupPassthroughActivity> activityController = 189 Robolectric.buildActivity( 190 SetupPassthroughActivity.class, 191 CommonUtils.createSetupIntent(new Intent(), testInput.getId())); 192 SetupPassthroughActivity activity = activityController.get(); 193 ShadowActivity shadowActivity = shadowOf(activity); 194 shadowActivity.setCallingActivity( 195 new ComponentName("com.notTrusted", "com.notTrusted.MyClass")); 196 activityController.create(); 197 198 ShadowActivity.IntentForResult shadowIntent = 199 shadowActivity.getNextStartedActivityForResult(); 200 // Since the calling activity is not trusted, the next activity should not be started. 201 assertThat(shadowIntent).isNull(); 202 assertThat(activity.isFinishing()).isTrue(); 203 } 204 205 @Test create_nullCallingPackage()206 public void create_nullCallingPackage() { 207 testSingletonApp.tvInputManagerHelper.start(); 208 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 209 210 ActivityController<SetupPassthroughActivity> activityController = 211 Robolectric.buildActivity( 212 SetupPassthroughActivity.class, 213 CommonUtils.createSetupIntent(new Intent(), testInput.getId())); 214 SetupPassthroughActivity activity = activityController.get(); 215 ShadowActivity shadowActivity = shadowOf(activity); 216 shadowActivity.setCallingActivity(null); 217 activityController.create(); 218 219 ShadowActivity.IntentForResult shadowIntent = 220 shadowActivity.getNextStartedActivityForResult(); 221 // Since the calling activity is null, the next activity should not be started. 222 assertThat(shadowIntent).isNull(); 223 assertThat(activity.isFinishing()).isTrue(); 224 } 225 226 @Test onActivityResult_canceled()227 public void onActivityResult_canceled() { 228 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 229 SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId()); 230 231 activity.onActivityResult(0, Activity.RESULT_CANCELED, null); 232 assertThat(activity.isFinishing()).isTrue(); 233 assertThat(shadowOf(activity).getResultCode()).isEqualTo(Activity.RESULT_CANCELED); 234 } 235 236 @Test onActivityResult_ok()237 public void onActivityResult_ok() { 238 TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext()); 239 testSingletonApp.setupUtils = setupUtils; 240 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 241 SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId()); 242 activity.onActivityResult(REQUEST_START_SETUP_ACTIVITY, Activity.RESULT_OK, null); 243 244 assertThat(testSingletonApp.epgFetcher.fetchStarted).isFalse(); 245 assertThat(setupUtils.finishedId).isEqualTo("com.example/.Input"); 246 assertThat(activity.isFinishing()).isFalse(); 247 248 setupUtils.finishedRunnable.run(); 249 assertThat(activity.isFinishing()).isTrue(); 250 assertThat(shadowOf(activity).getResultCode()).isEqualTo(Activity.RESULT_OK); 251 } 252 253 @Test onActivityResult_3rdPartyEpg_ok()254 public void onActivityResult_3rdPartyEpg_ok() { 255 TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.enableForTest(); 256 TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext()); 257 testSingletonApp.setupUtils = setupUtils; 258 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 259 testSingletonApp 260 .getCloudEpgFlags() 261 .setThirdPartyEpgInput( 262 StringListParam.newBuilder().addElement(testInput.getId()).build()); 263 SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId()); 264 Intent data = new Intent(); 265 data.putExtra(EpgContract.EXTRA_USE_CLOUD_EPG, true); 266 data.putExtra(TvInputInfo.EXTRA_INPUT_ID, testInput.getId()); 267 activity.onActivityResult(REQUEST_START_SETUP_ACTIVITY, Activity.RESULT_OK, data); 268 269 assertThat(testSingletonApp.epgFetcher.fetchStarted).isTrue(); 270 assertThat(setupUtils.finishedId).isEqualTo("com.example/.Input"); 271 assertThat(activity.isFinishing()).isFalse(); 272 273 setupUtils.finishedRunnable.run(); 274 assertThat(activity.isFinishing()).isTrue(); 275 assertThat(shadowOf(activity).getResultCode()).isEqualTo(Activity.RESULT_OK); 276 } 277 278 @Test onActivityResult_3rdPartyEpg_notAllowed()279 public void onActivityResult_3rdPartyEpg_notAllowed() { 280 TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.enableForTest(); 281 TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext()); 282 testSingletonApp.setupUtils = setupUtils; 283 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 284 SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId()); 285 Intent data = new Intent(); 286 data.putExtra(EpgContract.EXTRA_USE_CLOUD_EPG, true); 287 data.putExtra(TvInputInfo.EXTRA_INPUT_ID, testInput.getId()); 288 activity.onActivityResult(REQUEST_START_SETUP_ACTIVITY, Activity.RESULT_OK, data); 289 290 assertThat(testSingletonApp.epgFetcher.fetchStarted).isFalse(); 291 assertThat(setupUtils.finishedId).isEqualTo("com.example/.Input"); 292 assertThat(activity.isFinishing()).isFalse(); 293 294 setupUtils.finishedRunnable.run(); 295 assertThat(activity.isFinishing()).isTrue(); 296 assertThat(shadowOf(activity).getResultCode()).isEqualTo(Activity.RESULT_OK); 297 } 298 299 @Test onActivityResult_3rdPartyEpg_disabled()300 public void onActivityResult_3rdPartyEpg_disabled() { 301 TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.disableForTests(); 302 TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext()); 303 testSingletonApp.setupUtils = setupUtils; 304 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1); 305 testSingletonApp 306 .getCloudEpgFlags() 307 .setThirdPartyEpgInput( 308 StringListParam.newBuilder().addElement(testInput.getId()).build()); 309 testSingletonApp.dbExecutor = new RoboExecutorService(); 310 SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId()); 311 Intent data = new Intent(); 312 data.putExtra(EpgContract.EXTRA_USE_CLOUD_EPG, true); 313 data.putExtra(TvInputInfo.EXTRA_INPUT_ID, testInput.getId()); 314 activity.onActivityResult(REQUEST_START_SETUP_ACTIVITY, Activity.RESULT_OK, data); 315 316 assertThat(testSingletonApp.epgFetcher.fetchStarted).isFalse(); 317 assertThat(setupUtils.finishedId).isEqualTo("com.example/.Input"); 318 assertThat(activity.isFinishing()).isFalse(); 319 320 setupUtils.finishedRunnable.run(); 321 assertThat(activity.isFinishing()).isTrue(); 322 assertThat(shadowOf(activity).getResultCode()).isEqualTo(Activity.RESULT_OK); 323 } 324 325 @Test onActivityResult_ok_tvInputInfo_null()326 public void onActivityResult_ok_tvInputInfo_null() { 327 TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext()); 328 testSingletonApp.setupUtils = setupUtils; 329 FakeTvInputManager tvInputManager = 330 testSingletonApp.tvInputManagerHelper.getFakeTvInputManager(); 331 SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId()); 332 activity.onActivityResult(REQUEST_START_SETUP_ACTIVITY, Activity.RESULT_OK, null); 333 334 assertThat(tvInputManager.getTvInputInfo(testInput.getId())).isEqualTo(null); 335 assertThat(testSingletonApp.epgFetcher.fetchStarted).isFalse(); 336 assertThat(activity.isFinishing()).isTrue(); 337 338 assertThat(shadowOf(activity).getResultCode()).isEqualTo(Activity.RESULT_OK); 339 } 340 createSetupActivityFor(String inputId)341 private SetupPassthroughActivity createSetupActivityFor(String inputId) { 342 ActivityController<SetupPassthroughActivity> activityController = 343 Robolectric.buildActivity( 344 SetupPassthroughActivity.class, 345 CommonUtils.createSetupIntent(new Intent(), inputId)); 346 SetupPassthroughActivity activity = activityController.get(); 347 ShadowActivity shadowActivity = shadowOf(activity); 348 shadowActivity.setCallingActivity(createTrustedComponent()); 349 activityController.create(); 350 return activity; 351 } 352 createMockInput(String inputId)353 private TvInputInfo createMockInput(String inputId) { 354 TvInputInfo tvInputInfo = Mockito.mock(TvInputInfo.class); 355 ServiceInfo serviceInfo = new ServiceInfo(); 356 ApplicationInfo applicationInfo = new ApplicationInfo(); 357 Mockito.when(tvInputInfo.getId()).thenReturn(inputId); 358 serviceInfo.packageName = inputId.substring(0, inputId.indexOf('/')); 359 serviceInfo.applicationInfo = applicationInfo; 360 applicationInfo.flags = 0; 361 Mockito.when(tvInputInfo.getServiceInfo()).thenReturn(serviceInfo); 362 Mockito.when(tvInputInfo.loadLabel(ArgumentMatchers.any())).thenReturn("testLabel"); 363 if (VERSION.SDK_INT >= VERSION_CODES.N) { 364 Mockito.when(tvInputInfo.loadCustomLabel(ArgumentMatchers.any())) 365 .thenReturn("testCustomLabel"); 366 } 367 return tvInputInfo; 368 } 369 createTrustedComponent()370 private static ComponentName createTrustedComponent() { 371 return new ComponentName(CommonConstants.BASE_PACKAGE, "com.example.MyClass"); 372 } 373 374 /** 375 * Test SetupUtils. 376 * 377 * <p>SetupUtils has lots of DB and threading interactions, that make it hard to test. This 378 * bypasses all of that. 379 */ 380 private static class TestSetupUtils extends SetupUtils { 381 382 public String finishedId; 383 public Runnable finishedRunnable; 384 TestSetupUtils(Context context)385 private TestSetupUtils(Context context) { 386 super(context, Optional.absent()); 387 } 388 389 @Override onTvInputSetupFinished(String inputId, @Nullable Runnable postRunnable)390 public void onTvInputSetupFinished(String inputId, @Nullable Runnable postRunnable) { 391 finishedId = inputId; 392 finishedRunnable = postRunnable; 393 } 394 } 395 396 /** Test app for {@link SetupPassthroughActivityTest} */ 397 public static class MyTestApp extends TestSingletonApp implements HasAndroidInjector { 398 399 @Inject DispatchingAndroidInjector<Object> dispatchingAndroidInjector; 400 401 @Override onCreate()402 public void onCreate() { 403 404 super.onCreate(); 405 // Inject afterwards so we can use objects created in super. 406 // Note TestSingletonApp does not do injection so it is safe 407 applicationInjector().inject(this); 408 } 409 410 @Override androidInjector()411 public AndroidInjector<Object> androidInjector() { 412 return dispatchingAndroidInjector; 413 } 414 applicationInjector()415 protected AndroidInjector<MyTestApp> applicationInjector() { 416 417 return DaggerSetupPassthroughActivityTest_TestComponent.builder() 418 .applicationModule(new ApplicationModule(this)) 419 .tvSingletonsModule(new TvSingletonsModule(this)) 420 .testModule(new TestModule(this)) 421 .settableFlagsModule(flagsModule) 422 .build(); 423 } 424 } 425 426 /** Dagger component for {@link SetupPassthroughActivityTest}. */ 427 @Singleton 428 @Component( 429 modules = { 430 AndroidInjectionModule.class, 431 TestModule.class, 432 }) 433 interface TestComponent extends AndroidInjector<MyTestApp> {} 434 435 @Module( 436 includes = { 437 SetupPassthroughActivity.Module.class, 438 ApplicationModule.class, 439 TvSingletonsModule.class, 440 SettableFlagsModule.class, 441 }) 442 /** Module for {@link MyTestApp} */ 443 static class TestModule { 444 445 private final MyTestApp myTestApp; 446 TestModule(MyTestApp test)447 TestModule(MyTestApp test) { 448 myTestApp = test; 449 } 450 451 @Provides providesBuiltInTunerManager()452 Optional<BuiltInTunerManager> providesBuiltInTunerManager() { 453 return Optional.absent(); 454 } 455 456 @Provides providesTvInputManagerHelper()457 TvInputManagerHelper providesTvInputManagerHelper() { 458 return myTestApp.tvInputManagerHelper; 459 } 460 461 @Provides providesTestSetupUtils()462 SetupUtils providesTestSetupUtils() { 463 return myTestApp.setupUtils; 464 } 465 466 @Provides 467 @DbExecutor providesDbExecutor()468 Executor providesDbExecutor() { 469 return myTestApp.dbExecutor; 470 } 471 472 @Provides providesChannelDataManager()473 ChannelDataManager providesChannelDataManager() { 474 return myTestApp.getChannelDataManager(); 475 } 476 477 @Provides providesEpgFetcher()478 EpgFetcher providesEpgFetcher() { 479 return myTestApp.epgFetcher; 480 } 481 } 482 } 483