1 /* 2 * Copyright (C) 2016 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.cts.normalapp; 18 19 import static org.hamcrest.CoreMatchers.hasItems; 20 import static org.hamcrest.CoreMatchers.is; 21 import static org.hamcrest.CoreMatchers.notNullValue; 22 import static org.hamcrest.CoreMatchers.nullValue; 23 import static org.junit.Assert.assertThat; 24 import static org.junit.Assert.fail; 25 26 import android.content.ActivityNotFoundException; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.ContentResolver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.ResolveInfo; 34 import android.database.Cursor; 35 import android.net.Uri; 36 import android.provider.Settings.Secure; 37 import android.support.test.InstrumentationRegistry; 38 import android.support.test.runner.AndroidJUnit4; 39 40 import com.android.cts.util.TestResult; 41 42 import org.junit.After; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 import java.util.List; 48 import java.util.concurrent.SynchronousQueue; 49 import java.util.concurrent.TimeUnit; 50 51 @RunWith(AndroidJUnit4.class) 52 public class ClientTest { 53 /** Action to start normal test activities */ 54 private static final String ACTION_START_NORMAL_ACTIVITY = 55 "com.android.cts.ephemeraltest.START_NORMAL"; 56 /** Action to start normal, exposed test activities */ 57 private static final String ACTION_START_EXPOSED_ACTIVITY = 58 "com.android.cts.ephemeraltest.START_EXPOSED"; 59 /** Action to start ephemeral test activities */ 60 private static final String ACTION_START_EPHEMERAL_ACTIVITY = 61 "com.android.cts.ephemeraltest.START_EPHEMERAL"; 62 /** Action to query for test activities */ 63 private static final String ACTION_QUERY = 64 "com.android.cts.ephemeraltest.QUERY"; 65 private static final String EXTRA_ACTIVITY_NAME = 66 "com.android.cts.ephemeraltest.EXTRA_ACTIVITY_NAME"; 67 private static final String EXTRA_ACTIVITY_RESULT = 68 "com.android.cts.ephemeraltest.EXTRA_ACTIVITY_RESULT"; 69 70 private BroadcastReceiver mReceiver; 71 private final SynchronousQueue<TestResult> mResultQueue = new SynchronousQueue<>(); 72 73 @Before 74 public void setUp() throws Exception { 75 final IntentFilter filter = 76 new IntentFilter("com.android.cts.ephemeraltest.START_ACTIVITY"); 77 filter.addCategory(Intent.CATEGORY_DEFAULT); 78 mReceiver = new ActivityBroadcastReceiver(mResultQueue); 79 InstrumentationRegistry.getContext() 80 .registerReceiver(mReceiver, filter, Context.RECEIVER_VISIBLE_TO_INSTANT_APPS); 81 } 82 83 @After 84 public void tearDown() throws Exception { 85 InstrumentationRegistry.getContext().unregisterReceiver(mReceiver); 86 } 87 88 @Test 89 public void testQuery() throws Exception { 90 // query activities without flags 91 { 92 final Intent queryIntent = new Intent(ACTION_QUERY); 93 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 94 .getPackageManager().queryIntentActivities(queryIntent, 0 /*flags*/); 95 if (resolveInfo == null || resolveInfo.size() == 0) { 96 fail("didn't resolve any intents"); 97 } 98 assertThat(resolveInfo.size(), is(2)); 99 assertThat(resolveInfo.get(0).activityInfo.packageName, 100 is("com.android.cts.normalapp")); 101 assertThat(resolveInfo.get(0).activityInfo.name, 102 is("com.android.cts.normalapp.ExposedActivity")); 103 assertThat(resolveInfo.get(0).isInstantAppAvailable, 104 is(false)); 105 assertThat(resolveInfo.get(1).activityInfo.packageName, 106 is("com.android.cts.normalapp")); 107 assertThat(resolveInfo.get(1).activityInfo.name, 108 is("com.android.cts.normalapp.NormalActivity")); 109 assertThat(resolveInfo.get(1).isInstantAppAvailable, 110 is(false)); 111 } 112 113 // query activities asking for ephemeral apps [we should only get normal apps] 114 { 115 final Intent queryIntent = new Intent(ACTION_QUERY); 116 final int MATCH_EPHEMERAL = 0x00800000; 117 118 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 119 .getPackageManager().queryIntentActivities(queryIntent, MATCH_EPHEMERAL); 120 if (resolveInfo == null || resolveInfo.size() == 0) { 121 fail("didn't resolve any intents"); 122 } 123 assertThat(resolveInfo.size(), is(2)); 124 assertThat(resolveInfo.get(0).activityInfo.packageName, 125 is("com.android.cts.normalapp")); 126 assertThat(resolveInfo.get(0).activityInfo.name, 127 is("com.android.cts.normalapp.ExposedActivity")); 128 assertThat(resolveInfo.get(0).isInstantAppAvailable, 129 is(false)); 130 assertThat(resolveInfo.get(1).activityInfo.packageName, 131 is("com.android.cts.normalapp")); 132 assertThat(resolveInfo.get(1).activityInfo.name, 133 is("com.android.cts.normalapp.NormalActivity")); 134 assertThat(resolveInfo.get(1).isInstantAppAvailable, 135 is(false)); 136 } 137 138 // query activities; directed package 139 { 140 final Intent queryIntent = new Intent(ACTION_QUERY); 141 queryIntent.setPackage("com.android.cts.ephemeralapp1"); 142 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 143 .getPackageManager().queryIntentActivities(queryIntent, 0 /*flags*/); 144 assertThat(resolveInfo.size(), is(0)); 145 } 146 147 // query activities; directed component 148 { 149 final Intent queryIntent = new Intent(ACTION_QUERY); 150 queryIntent.setComponent( 151 new ComponentName("com.android.cts.ephemeralapp1", 152 "com.android.cts.ephemeralapp1.EphemeralActivity")); 153 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 154 .getPackageManager().queryIntentActivities(queryIntent, 0 /*flags*/); 155 assertThat(resolveInfo.size(), is(0)); 156 } 157 158 // query services without flags 159 { 160 final Intent queryIntent = new Intent(ACTION_QUERY); 161 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 162 .getPackageManager().queryIntentServices(queryIntent, 0 /*flags*/); 163 if (resolveInfo == null || resolveInfo.size() == 0) { 164 fail("didn't resolve any intents"); 165 } 166 assertThat(resolveInfo.size(), is(2)); 167 assertThat(resolveInfo.get(0).serviceInfo.packageName, 168 is("com.android.cts.normalapp")); 169 assertThat(resolveInfo.get(0).serviceInfo.name, 170 is("com.android.cts.normalapp.ExposedService")); 171 assertThat(resolveInfo.get(0).isInstantAppAvailable, 172 is(false)); 173 assertThat(resolveInfo.get(1).serviceInfo.packageName, 174 is("com.android.cts.normalapp")); 175 assertThat(resolveInfo.get(1).serviceInfo.name, 176 is("com.android.cts.normalapp.NormalService")); 177 assertThat(resolveInfo.get(1).isInstantAppAvailable, 178 is(false)); 179 } 180 181 // query services asking for ephemeral apps [we should only get normal apps] 182 { 183 final Intent queryIntent = new Intent(ACTION_QUERY); 184 final int MATCH_EPHEMERAL = 0x00800000; 185 186 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 187 .getPackageManager().queryIntentServices(queryIntent, MATCH_EPHEMERAL); 188 if (resolveInfo == null || resolveInfo.size() == 0) { 189 fail("didn't resolve any intents"); 190 } 191 assertThat(resolveInfo.size(), is(2)); 192 assertThat(resolveInfo.get(0).serviceInfo.packageName, 193 is("com.android.cts.normalapp")); 194 assertThat(resolveInfo.get(0).serviceInfo.name, 195 is("com.android.cts.normalapp.ExposedService")); 196 assertThat(resolveInfo.get(0).isInstantAppAvailable, 197 is(false)); 198 assertThat(resolveInfo.get(1).serviceInfo.packageName, 199 is("com.android.cts.normalapp")); 200 assertThat(resolveInfo.get(1).serviceInfo.name, 201 is("com.android.cts.normalapp.NormalService")); 202 assertThat(resolveInfo.get(1).isInstantAppAvailable, 203 is(false)); 204 } 205 206 // query services; directed package 207 { 208 final Intent queryIntent = new Intent(ACTION_QUERY); 209 queryIntent.setPackage("com.android.cts.ephemeralapp1"); 210 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 211 .getPackageManager().queryIntentServices(queryIntent, 0 /*flags*/); 212 assertThat(resolveInfo.size(), is(0)); 213 } 214 215 // query services; directed component 216 { 217 final Intent queryIntent = new Intent(ACTION_QUERY); 218 queryIntent.setComponent( 219 new ComponentName("com.android.cts.ephemeralapp1", 220 "com.android.cts.ephemeralapp1.EphemeralService")); 221 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 222 .getPackageManager().queryIntentServices(queryIntent, 0 /*flags*/); 223 assertThat(resolveInfo.size(), is(0)); 224 } 225 226 // query content providers without flags 227 { 228 final Intent queryIntent = new Intent(ACTION_QUERY); 229 final List<ResolveInfo> resolveInfo = InstrumentationRegistry 230 .getContext().getPackageManager().queryIntentContentProviders( 231 queryIntent, 0 /*flags*/); 232 if (resolveInfo == null || resolveInfo.size() == 0) { 233 fail("didn't resolve any intents"); 234 } 235 assertThat(resolveInfo.size(), is(2)); 236 assertThat(resolveInfo.get(0).providerInfo.packageName, 237 is("com.android.cts.normalapp")); 238 assertThat(resolveInfo.get(0).providerInfo.name, 239 is("com.android.cts.normalapp.ExposedProvider")); 240 assertThat(resolveInfo.get(1).providerInfo.packageName, 241 is("com.android.cts.normalapp")); 242 assertThat(resolveInfo.get(1).providerInfo.name, 243 is("com.android.cts.normalapp.NormalProvider")); 244 assertThat(resolveInfo.get(1).isInstantAppAvailable, 245 is(false)); 246 } 247 248 // query content providers asking for ephemeral apps [we should only get normal apps] 249 { 250 final Intent queryIntent = new Intent(ACTION_QUERY); 251 final int MATCH_EPHEMERAL = 0x00800000; 252 253 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 254 .getPackageManager().queryIntentContentProviders( 255 queryIntent, MATCH_EPHEMERAL); 256 if (resolveInfo == null || resolveInfo.size() == 0) { 257 fail("didn't resolve any intents"); 258 } 259 assertThat(resolveInfo.size(), is(2)); 260 assertThat(resolveInfo.get(0).providerInfo.packageName, 261 is("com.android.cts.normalapp")); 262 assertThat(resolveInfo.get(0).providerInfo.name, 263 is("com.android.cts.normalapp.ExposedProvider")); 264 assertThat(resolveInfo.get(1).providerInfo.packageName, 265 is("com.android.cts.normalapp")); 266 assertThat(resolveInfo.get(1).providerInfo.name, 267 is("com.android.cts.normalapp.NormalProvider")); 268 assertThat(resolveInfo.get(1).isInstantAppAvailable, 269 is(false)); 270 } 271 272 // query content providers; directed package 273 { 274 final Intent queryIntent = new Intent(ACTION_QUERY); 275 queryIntent.setPackage("com.android.cts.ephemeralapp1"); 276 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 277 .getPackageManager().queryIntentContentProviders(queryIntent, 0 /*flags*/); 278 assertThat(resolveInfo.size(), is(0)); 279 } 280 281 // query content providers; directed component 282 { 283 final Intent queryIntent = new Intent(ACTION_QUERY); 284 queryIntent.setComponent( 285 new ComponentName("com.android.cts.ephemeralapp1", 286 "com.android.cts.ephemeralapp1.EphemeralProvider")); 287 final List<ResolveInfo> resolveInfo = InstrumentationRegistry.getContext() 288 .getPackageManager().queryIntentContentProviders(queryIntent, 0 /*flags*/); 289 assertThat(resolveInfo.size(), is(0)); 290 } 291 } 292 293 @Test 294 public void testStartNormal() throws Exception { 295 // start the normal activity 296 { 297 final Intent startNormalIntent = new Intent(ACTION_START_NORMAL_ACTIVITY) 298 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 299 InstrumentationRegistry.getContext().startActivity(startNormalIntent, null /*options*/); 300 final TestResult testResult = getResult(); 301 assertThat(testResult.getPackageName(), 302 is("com.android.cts.normalapp")); 303 assertThat(testResult.getComponentName(), 304 is("NormalActivity")); 305 assertThat(testResult.getException(), 306 is("android.content.pm.PackageManager$NameNotFoundException")); 307 } 308 309 // start the normal activity; directed package 310 { 311 final Intent startNormalIntent = new Intent(ACTION_START_NORMAL_ACTIVITY) 312 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 313 startNormalIntent.setPackage("com.android.cts.normalapp"); 314 InstrumentationRegistry.getContext().startActivity(startNormalIntent, null /*options*/); 315 final TestResult testResult = getResult(); 316 assertThat(testResult.getPackageName(), 317 is("com.android.cts.normalapp")); 318 assertThat(testResult.getComponentName(), 319 is("NormalActivity")); 320 assertThat(testResult.getException(), 321 is("android.content.pm.PackageManager$NameNotFoundException")); 322 } 323 324 // start the normal activity; directed component 325 { 326 final Intent startNormalIntent = new Intent(ACTION_START_NORMAL_ACTIVITY) 327 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 328 startNormalIntent.setComponent(new ComponentName( 329 "com.android.cts.normalapp", "com.android.cts.normalapp.NormalActivity")); 330 InstrumentationRegistry.getContext().startActivity(startNormalIntent, null /*options*/); 331 final TestResult testResult = getResult(); 332 assertThat(testResult.getPackageName(), 333 is("com.android.cts.normalapp")); 334 assertThat(testResult.getComponentName(), 335 is("NormalActivity")); 336 assertThat(testResult.getException(), 337 is("android.content.pm.PackageManager$NameNotFoundException")); 338 } 339 340 // connect to the normal provider 341 { 342 final String provider = "content://com.android.cts.normalapp.provider/table"; 343 final Cursor testCursor = InstrumentationRegistry 344 .getContext().getContentResolver().query( 345 Uri.parse(provider), 346 null /*projection*/, 347 null /*selection*/, 348 null /*selectionArgs*/, 349 null /*sortOrder*/); 350 assertThat(testCursor, is(notNullValue())); 351 assertThat(testCursor.getCount(), is(1)); 352 assertThat(testCursor.getColumnCount(), is(2)); 353 assertThat(testCursor.moveToFirst(), is(true)); 354 assertThat(testCursor.getInt(0), is(1)); 355 assertThat(testCursor.getString(1), is("NormalProvider")); 356 } 357 } 358 359 @Test 360 public void testStartEphemeral() throws Exception { 361 // start the ephemeral activity; no EXTERNAL flag 362 try { 363 final Intent startEphemeralIntent = new Intent(ACTION_START_EPHEMERAL_ACTIVITY) 364 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 365 InstrumentationRegistry.getContext().startActivity( 366 startEphemeralIntent, null /*options*/); 367 final TestResult testResult = getResult(); 368 fail(); 369 } catch (ActivityNotFoundException expected) { 370 } 371 372 // start the ephemeral activity; EXTERNAL flag 373 { 374 final Intent startEphemeralIntent = new Intent(ACTION_START_EPHEMERAL_ACTIVITY) 375 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MATCH_EXTERNAL); 376 InstrumentationRegistry.getContext().startActivity( 377 startEphemeralIntent, null /*options*/); 378 final TestResult testResult = getResult(); 379 assertThat("com.android.cts.ephemeralapp1", is(testResult.getPackageName())); 380 assertThat(ACTION_START_EPHEMERAL_ACTIVITY, is(testResult.getIntent().getAction())); 381 } 382 383 384 // start the ephemeral activity; directed package, no EXTERNAL flag 385 try { 386 final Intent startEphemeralIntent = new Intent(ACTION_START_EPHEMERAL_ACTIVITY) 387 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 388 startEphemeralIntent.setPackage("com.android.cts.ephemeralapp1"); 389 InstrumentationRegistry.getContext().startActivity( 390 startEphemeralIntent, null /*options*/); 391 final TestResult testResult = getResult(); 392 fail(); 393 } catch (ActivityNotFoundException expected) { 394 } 395 396 // start the ephemeral activity; directed package, includes EXTERNAL flag 397 { 398 final Intent startEphemeralIntent = new Intent(ACTION_START_EPHEMERAL_ACTIVITY) 399 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MATCH_EXTERNAL); 400 startEphemeralIntent.setPackage("com.android.cts.ephemeralapp1"); 401 InstrumentationRegistry.getContext().startActivity( 402 startEphemeralIntent, null /*options*/); 403 final TestResult testResult = getResult(); 404 assertThat("com.android.cts.ephemeralapp1", is(testResult.getPackageName())); 405 assertThat(ACTION_START_EPHEMERAL_ACTIVITY, is(testResult.getIntent().getAction())); 406 } 407 408 // start the ephemeral activity; directed component 409 try { 410 final Intent startEphemeralIntent = new Intent(ACTION_START_EPHEMERAL_ACTIVITY) 411 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 412 startEphemeralIntent.setComponent( 413 new ComponentName("com.android.cts.ephemeralapp1", 414 "com.android.cts.ephemeralapp1.EphemeralActivity")); 415 InstrumentationRegistry.getContext().startActivity( 416 startEphemeralIntent, null /*options*/); 417 final TestResult testResult = getResult(); 418 fail(); 419 } catch (ActivityNotFoundException expected) { 420 } 421 422 // start the ephemeral activity; using VIEW/BROWSABLE 423 { 424 final Intent startViewIntent = new Intent(Intent.ACTION_VIEW) 425 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 426 startViewIntent.addCategory(Intent.CATEGORY_BROWSABLE); 427 startViewIntent.setData(Uri.parse("https://cts.google.com/ephemeral")); 428 InstrumentationRegistry.getContext().startActivity( 429 startViewIntent, null /*options*/); 430 final TestResult testResult = getResult(); 431 assertThat("com.android.cts.ephemeralapp1", is(testResult.getPackageName())); 432 assertThat("EphemeralActivity", is(testResult.getComponentName())); 433 assertThat(Intent.ACTION_VIEW, is(testResult.getIntent().getAction())); 434 assertThat(testResult.getIntent().getCategories(), hasItems(Intent.CATEGORY_BROWSABLE)); 435 assertThat("https://cts.google.com/ephemeral", 436 is(testResult.getIntent().getData().toString())); 437 } 438 439 final ContentResolver contentResolver = 440 InstrumentationRegistry.getContext().getContentResolver(); 441 final int originalSetting = Secure.getInt(contentResolver, Secure.INSTANT_APPS_ENABLED, 1); 442 Secure.putInt(contentResolver, Secure.INSTANT_APPS_ENABLED, 0); 443 try { 444 // start the ephemeral activity; using VIEW/BROWSABLE with setting disabled 445 try { 446 final Intent startViewIntent = new Intent(Intent.ACTION_VIEW) 447 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 448 startViewIntent.addCategory(Intent.CATEGORY_BROWSABLE); 449 startViewIntent.setData(Uri.parse("https://cts.google.com/ephemeral")); 450 InstrumentationRegistry.getContext().startActivity( 451 startViewIntent, null /*options*/); 452 final TestResult testResult = getResult(); 453 fail(); 454 } catch (TestResultNotFoundException expected) { 455 // we shouldn't resolve to our instant app 456 } 457 458 // start the ephemeral activity; EXTERNAL flag with setting disabled 459 { 460 final Intent startEphemeralIntent = new Intent(ACTION_START_EPHEMERAL_ACTIVITY) 461 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 462 | Intent.FLAG_ACTIVITY_MATCH_EXTERNAL); 463 InstrumentationRegistry.getContext().startActivity( 464 startEphemeralIntent, null /*options*/); 465 final TestResult testResult = getResult(); 466 assertThat("com.android.cts.ephemeralapp1", is(testResult.getPackageName())); 467 assertThat(ACTION_START_EPHEMERAL_ACTIVITY, is(testResult.getIntent().getAction())); 468 } 469 470 } finally { 471 Secure.putInt(contentResolver, Secure.INSTANT_APPS_ENABLED, originalSetting); 472 } 473 474 // connect to the instant app provider 475 { 476 final String provider = "content://com.android.cts.ephemeralapp1.provider/table"; 477 final Cursor testCursor = contentResolver.query( 478 Uri.parse(provider), 479 null /*projection*/, 480 null /*selection*/, 481 null /*selectionArgs*/, 482 null /*sortOrder*/); 483 assertThat(testCursor, is(nullValue())); 484 } 485 } 486 487 private TestResult getResult() { 488 final TestResult result; 489 try { 490 result = mResultQueue.poll(5, TimeUnit.SECONDS); 491 } catch (InterruptedException e) { 492 throw new RuntimeException(e); 493 } 494 if (result == null) { 495 throw new TestResultNotFoundException( 496 "Activity didn't receive a Result in 5 seconds"); 497 } 498 return result; 499 } 500 501 private static class TestResultNotFoundException extends IllegalStateException { 502 public TestResultNotFoundException(String description) { 503 super(description); 504 } 505 } 506 507 private static class ActivityBroadcastReceiver extends BroadcastReceiver { 508 private final SynchronousQueue<TestResult> mQueue; 509 public ActivityBroadcastReceiver(SynchronousQueue<TestResult> queue) { 510 mQueue = queue; 511 } 512 513 @Override 514 public void onReceive(Context context, Intent intent) { 515 try { 516 mQueue.offer(intent.getParcelableExtra(TestResult.EXTRA_TEST_RESULT), 517 5, TimeUnit.SECONDS); 518 } catch (InterruptedException e) { 519 throw new RuntimeException(e); 520 } 521 } 522 } 523 } 524