Skip to content

Commit

Permalink
Validate intents passed to start/stop/bindService.
Browse files Browse the repository at this point in the history
The Android OS normally throws an IllegalArgumentException if an
implicit Intent is passed to one of these methods by an app which
targets SDK 21 or higher. However, this logic was lost because
Robolectric shadows these methods, and only the actual implementation
has access to the private Intent validation method. Copy the
relatively simple validation check into Robolectric to achieve the
same behavior.

Existing usage of these methods in tests must be updated to use
explicit intents rather than implicit ones in order for those tests
not to crash. Prod code should also be audited to ensure that these
methods are never called with implicit intents (at least on SDK 21+).

See #5016

PiperOrigin-RevId: 250337437
  • Loading branch information
Googler authored and copybara-robolectric committed May 28, 2019
1 parent eed2995 commit 21375be
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 44 deletions.
Expand Up @@ -208,7 +208,7 @@ public void bindServiceShouldThrowIfSetToThrow() {
Shadows.shadowOf(context).setThrowInBindService(expectedException);

try {
context.bindService(new Intent(""), service, Context.BIND_AUTO_CREATE);
context.bindService(new Intent("").setPackage("package"), service, Context.BIND_AUTO_CREATE);
fail("bindService should throw SecurityException!");
} catch (SecurityException thrownException) {
assertThat(thrownException).isEqualTo(expectedException);
Expand All @@ -223,7 +223,7 @@ public void bindServiceShouldCallOnServiceConnectedWithDefaultValues_ifFlagUnset
Binder expectedBinder = new Binder();
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindService(expectedComponentName, expectedBinder);
context.bindService(new Intent(""), service, Context.BIND_AUTO_CREATE);
context.bindService(new Intent("").setPackage("package"), service, Context.BIND_AUTO_CREATE);
shadowMainLooper().idle();
assertThat(service.name).isEqualTo(expectedComponentName);
assertThat(service.service).isEqualTo(expectedBinder);
Expand All @@ -238,7 +238,7 @@ public void bindServiceShouldCallOnServiceConnectedWithDefaultValues_ifFlagSet()
Binder expectedBinder = new Binder();
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindService(expectedComponentName, expectedBinder);
context.bindService(new Intent(""), service, Context.BIND_AUTO_CREATE);
context.bindService(new Intent("").setPackage("package"), service, Context.BIND_AUTO_CREATE);
shadowMainLooper().idle();
assertThat(service.name).isEqualTo(expectedComponentName);
assertThat(service.service).isEqualTo(expectedBinder);
Expand All @@ -251,7 +251,7 @@ public void bindServiceShouldCallOnServiceConnectedWithDefaultValues_ifFlagSet()
@Test
public void bindServiceShouldCallOnServiceConnectedWithNullValues() {
TestService service = new TestService();
context.bindService(new Intent(""), service, Context.BIND_AUTO_CREATE);
context.bindService(new Intent("").setPackage("package"), service, Context.BIND_AUTO_CREATE);
assertThat(service.name).isNull();
assertThat(service.service).isNull();
}
Expand All @@ -261,7 +261,7 @@ public void bindServiceShouldCallOnServiceConnectedWhenNotPaused() {
shadowMainLooper().pause();
ComponentName expectedComponentName = new ComponentName("", "");
Binder expectedBinder = new Binder();
Intent expectedIntent = new Intent("expected");
Intent expectedIntent = new Intent("expected").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntent, expectedComponentName, expectedBinder);
Expand All @@ -284,7 +284,7 @@ public void unbindServiceShouldNotCallOnServiceDisconnected_ifFlagUnset() {
TestService service = new TestService();
ComponentName expectedComponentName = new ComponentName("", "");
Binder expectedBinder = new Binder();
Intent expectedIntent = new Intent("expected");
Intent expectedIntent = new Intent("expected").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntent, expectedComponentName, expectedBinder);
Expand All @@ -304,7 +304,7 @@ public void unbindServiceShouldCallOnServiceDisconnectedWhenNotPaused_ifFlagSet(
TestService service = new TestService();
ComponentName expectedComponentName = new ComponentName("", "");
Binder expectedBinder = new Binder();
Intent expectedIntent = new Intent("expected");
Intent expectedIntent = new Intent("expected").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntent, expectedComponentName, expectedBinder);
Expand All @@ -322,7 +322,7 @@ public void unbindServiceAddsEntryToUnboundServicesCollection() {
TestService service = new TestService();
ComponentName expectedComponentName = new ComponentName("", "");
Binder expectedBinder = new Binder();
Intent expectedIntent = new Intent("expected");
Intent expectedIntent = new Intent("expected").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntent, expectedComponentName, expectedBinder);
Expand All @@ -339,7 +339,7 @@ public void declaringActionUnbindableMakesBindServiceReturnFalse() {
TestService service = new TestService();
ComponentName expectedComponentName = new ComponentName("", "");
Binder expectedBinder = new Binder();
Intent expectedIntent = new Intent("refuseToBind");
Intent expectedIntent = new Intent("refuseToBind").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntent, expectedComponentName, expectedBinder);
Expand Down Expand Up @@ -371,7 +371,7 @@ public void declaringComponentUnbindableMakesBindServiceReturnFalse_intentWithou
TestService service = new TestService();
ComponentName expectedComponentName = new ComponentName("unbindable", "service");
Binder expectedBinder = new Binder();
Intent expectedIntent = new Intent("expected");
Intent expectedIntent = new Intent("expected").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntent, expectedComponentName, expectedBinder);
Expand All @@ -389,7 +389,7 @@ public void declaringComponentUnbindableMakesBindServiceReturnFalse_defaultCompo
TestService service = new TestService();
ComponentName expectedComponentName = new ComponentName("unbindable", "service");
Binder expectedBinder = new Binder();
Intent expectedIntent = new Intent("expected");
Intent expectedIntent = new Intent("expected").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindService(expectedComponentName, expectedBinder);
Shadows.shadowOf(context).declareComponentUnbindable(expectedComponentName);
Expand All @@ -405,10 +405,10 @@ public void bindServiceWithMultipleIntentsMapping() {
TestService service = new TestService();
ComponentName expectedComponentNameOne = new ComponentName("package", "one");
Binder expectedBinderOne = new Binder();
Intent expectedIntentOne = new Intent("expected_one");
Intent expectedIntentOne = new Intent("expected_one").setPackage("package");
ComponentName expectedComponentNameTwo = new ComponentName("package", "two");
Binder expectedBinderTwo = new Binder();
Intent expectedIntentTwo = new Intent("expected_two");
Intent expectedIntentTwo = new Intent("expected_two").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntentOne, expectedComponentNameOne, expectedBinderOne);
Expand All @@ -430,10 +430,10 @@ public void bindServiceWithMultipleIntentsMappingWithDefault() {
TestService service = new TestService();
ComponentName expectedComponentNameOne = new ComponentName("package", "one");
Binder expectedBinderOne = new Binder();
Intent expectedIntentOne = new Intent("expected_one");
Intent expectedIntentOne = new Intent("expected_one").setPackage("package");
ComponentName expectedComponentNameTwo = new ComponentName("package", "two");
Binder expectedBinderTwo = new Binder();
Intent expectedIntentTwo = new Intent("expected_two");
Intent expectedIntentTwo = new Intent("expected_two").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntentOne, expectedComponentNameOne, expectedBinderOne);
Expand All @@ -448,7 +448,8 @@ public void bindServiceWithMultipleIntentsMappingWithDefault() {
shadowMainLooper().idle();
assertThat(service.name).isEqualTo(expectedComponentNameTwo);
assertThat(service.service).isEqualTo(expectedBinderTwo);
context.bindService(new Intent("unknown"), service, Context.BIND_AUTO_CREATE);
context.bindService(
new Intent("unknown").setPackage("package"), service, Context.BIND_AUTO_CREATE);
shadowMainLooper().idle();
assertThat(service.name).isNull();
assertThat(service.service).isNull();
Expand All @@ -459,11 +460,11 @@ public void unbindServiceWithMultipleIntentsMapping() {
TestService serviceOne = new TestService();
ComponentName expectedComponentNameOne = new ComponentName("package", "one");
Binder expectedBinderOne = new Binder();
Intent expectedIntentOne = new Intent("expected_one");
Intent expectedIntentOne = new Intent("expected_one").setPackage("package");
TestService serviceTwo = new TestService();
ComponentName expectedComponentNameTwo = new ComponentName("package", "two");
Binder expectedBinderTwo = new Binder();
Intent expectedIntentTwo = new Intent("expected_two");
Intent expectedIntentTwo = new Intent("expected_two").setPackage("package");
Shadows.shadowOf(context)
.setComponentNameAndServiceForBindServiceForIntent(
expectedIntentOne, expectedComponentNameOne, expectedBinderOne);
Expand All @@ -486,7 +487,8 @@ public void unbindServiceWithMultipleIntentsMapping() {
assertThat(serviceTwo.name).isEqualTo(expectedComponentNameTwo);

TestService serviceDefault = new TestService();
context.bindService(new Intent("default"), serviceDefault, Context.BIND_AUTO_CREATE);
context.bindService(
new Intent("default").setPackage("package"), serviceDefault, Context.BIND_AUTO_CREATE);
shadowMainLooper().idle();
assertThat(serviceDefault.nameDisconnected).isNull();
context.unbindService(serviceDefault);
Expand All @@ -510,6 +512,7 @@ public void shouldHaveStoppedServiceIntentAndIndicateServiceWasntRunning() {
private Intent getSomeActionIntent(String action) {
Intent intent = new Intent();
intent.setAction(action);
intent.setPackage("package");
return intent;
}

Expand Down Expand Up @@ -676,7 +679,10 @@ public void bindServiceShouldAddServiceConnectionToListOfBoundServiceConnections
final ServiceConnection expectedServiceConnection = new EmptyServiceConnection();

assertThat(Shadows.shadowOf(context).getBoundServiceConnections()).hasSize(0);
assertThat(context.bindService(new Intent("connect"), expectedServiceConnection, 0)).isTrue();
assertThat(
context.bindService(
new Intent("connect").setPackage("dummy.package"), expectedServiceConnection, 0))
.isTrue();
assertThat(Shadows.shadowOf(context).getBoundServiceConnections()).hasSize(1);
assertThat(Shadows.shadowOf(context).getBoundServiceConnections().get(0))
.isSameInstanceAs(expectedServiceConnection);
Expand All @@ -687,7 +693,7 @@ public void bindServiceShouldAddServiceConnectionToListOfBoundServiceConnections
bindServiceShouldAddServiceConnectionToListOfBoundServiceConnectionsEvenIfServiceUnbindable() {
final ServiceConnection expectedServiceConnection = new EmptyServiceConnection();
final String unboundableAction = "refuse";
final Intent serviceIntent = new Intent(unboundableAction);
final Intent serviceIntent = new Intent(unboundableAction).setPackage("dummy.package");
Shadows.shadowOf(context).declareActionUnbindable(unboundableAction);
assertThat(Shadows.shadowOf(context).getBoundServiceConnections()).hasSize(0);
assertThat(context.bindService(serviceIntent, expectedServiceConnection, 0)).isFalse();
Expand All @@ -700,7 +706,10 @@ public void bindServiceShouldAddServiceConnectionToListOfBoundServiceConnections
public void unbindServiceShouldRemoveServiceConnectionFromListOfBoundServiceConnections() {
final ServiceConnection expectedServiceConnection = new EmptyServiceConnection();

assertThat(context.bindService(new Intent("connect"), expectedServiceConnection, 0)).isTrue();
assertThat(
context.bindService(
new Intent("connect").setPackage("dummy.package"), expectedServiceConnection, 0))
.isTrue();
assertThat(Shadows.shadowOf(context).getBoundServiceConnections()).hasSize(1);
assertThat(Shadows.shadowOf(context).getUnboundServiceConnections()).hasSize(0);
context.unbindService(expectedServiceConnection);
Expand Down
Expand Up @@ -180,7 +180,7 @@ public void createPackageContextRemoteViews() throws Exception {
@Test
@Config(minSdk = LOLLIPOP)
public void bindServiceAsUser() throws Exception {
Intent serviceIntent = new Intent();
Intent serviceIntent = new Intent().setPackage("dummy.package");
ServiceConnection serviceConnection = buildServiceConnection();
int flags = 0;

Expand All @@ -192,8 +192,35 @@ public void bindServiceAsUser() throws Exception {
assertThat(shadowOf(context).getBoundServiceConnections()).hasSize(1);
}

@Test
@Config(minSdk = LOLLIPOP)
public void bindServiceAsUser_shouldThrowOnImplicitIntent() throws Exception {
Intent serviceIntent = new Intent();
ServiceConnection serviceConnection = buildServiceConnection();
int flags = 0;

try {
context.bindServiceAsUser(serviceIntent, serviceConnection, flags, Process.myUserHandle());
fail("bindServiceAsUser should throw IllegalArgumentException!");
} catch (IllegalArgumentException e) {
// expected
}
}

@Test
public void bindService() throws Exception {
Intent serviceIntent = new Intent().setPackage("dummy.package");
ServiceConnection serviceConnection = buildServiceConnection();
int flags = 0;

assertThat(context.bindService(serviceIntent, serviceConnection, flags)).isTrue();

assertThat(shadowOf(context).getBoundServiceConnections()).hasSize(1);
}

@Test
public void bindService_shouldAllowImplicitIntentPreLollipop() throws Exception {
context.getApplicationInfo().targetSdkVersion = KITKAT;
Intent serviceIntent = new Intent();
ServiceConnection serviceConnection = buildServiceConnection();
int flags = 0;
Expand All @@ -203,17 +230,64 @@ public void bindService() throws Exception {
assertThat(shadowOf(context).getBoundServiceConnections()).hasSize(1);
}

@Test
public void bindService_shouldThrowOnImplicitIntentOnLollipop() throws Exception {
Intent serviceIntent = new Intent();
ServiceConnection serviceConnection = buildServiceConnection();
int flags = 0;

try {
context.bindService(serviceIntent, serviceConnection, flags);
fail("bindService should throw IllegalArgumentException!");
} catch (IllegalArgumentException e) {
// expected
}
}

@Test
public void bindService_unbindable() throws Exception {
String action = "foo-action";
Intent serviceIntent = new Intent(action);
Intent serviceIntent = new Intent(action).setPackage("dummy.package");
ServiceConnection serviceConnection = buildServiceConnection();
int flags = 0;
shadowOf(context).declareActionUnbindable(action);

assertThat(context.bindService(serviceIntent, serviceConnection, flags)).isFalse();
}

@Test
public void startService_shouldAllowImplicitIntentPreLollipop() throws Exception {
context.getApplicationInfo().targetSdkVersion = KITKAT;
context.startService(new Intent("dummy_action"));
assertThat(shadowOf(context).getNextStartedService().getAction()).isEqualTo("dummy_action");
}

@Test
public void startService_shouldThrowOnImplicitIntentOnLollipop() throws Exception {
try {
context.startService(new Intent("dummy_action"));
fail("startService should throw IllegalArgumentException!");
} catch (IllegalArgumentException e) {
// expected
}
}

@Test
public void stopService_shouldAllowImplicitIntentPreLollipop() throws Exception {
context.getApplicationInfo().targetSdkVersion = KITKAT;
context.stopService(new Intent("dummy_action"));
}

@Test
public void stopService_shouldThrowOnImplicitIntentOnLollipop() throws Exception {
try {
context.stopService(new Intent("dummy_action"));
fail("stopService should throw IllegalArgumentException!");
} catch (IllegalArgumentException e) {
// expected
}
}

@Test
@Config(minSdk = JELLY_BEAN_MR1)
public void sendBroadcastAsUser_sendBroadcast() throws IntentSender.SendIntentException {
Expand Down
Expand Up @@ -42,7 +42,7 @@ public void createConfigurationContext() {
@Test
@Config(minSdk = VERSION_CODES.O)
public void startForegroundService() {
Intent intent = new Intent();
Intent intent = new Intent().setPackage("dummy.package");
context.startForegroundService(intent);
assertThat(ShadowApplication.getInstance().getNextStartedService()).isEqualTo(intent);
}
Expand Down
Expand Up @@ -634,7 +634,8 @@ private void assertSameInstanceEveryTime(String serviceName) {

@Test
public void bindServiceDelegatesToShadowApplication() {
contextWrapper.bindService(new Intent("foo"), new TestService(), Context.BIND_AUTO_CREATE);
contextWrapper.bindService(
new Intent("foo").setPackage("dummy.package"), new TestService(), Context.BIND_AUTO_CREATE);
assertEquals(
"foo",
shadowOf((Application) ApplicationProvider.getApplicationContext())
Expand Down

0 comments on commit 21375be

Please sign in to comment.