Loading packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.settingslib; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Process; import android.os.UserHandle; /** * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices. */ public class SliceBroadcastRelay { public static final String ACTION_REGISTER = "com.android.settingslib.action.REGISTER_SLICE_RECEIVER"; public static final String ACTION_UNREGISTER = "com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER"; public static final String SYSTEMUI_PACKAGE = "com.android.systemui"; public static final String EXTRA_URI = "uri"; public static final String EXTRA_RECEIVER = "receiver"; public static final String EXTRA_FILTER = "filter"; public static void registerReceiver(Context context, Uri registerKey, Class<? extends BroadcastReceiver> receiver, IntentFilter filter) { Intent registerBroadcast = new Intent(ACTION_REGISTER); registerBroadcast.setPackage(SYSTEMUI_PACKAGE); registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey, Process.myUserHandle().getIdentifier())); registerBroadcast.putExtra(EXTRA_RECEIVER, new ComponentName(context.getPackageName(), receiver.getName())); registerBroadcast.putExtra(EXTRA_FILTER, filter); context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM); } public static void unregisterReceivers(Context context, Uri registerKey) { Intent registerBroadcast = new Intent(ACTION_UNREGISTER); registerBroadcast.setPackage(SYSTEMUI_PACKAGE); registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey, Process.myUserHandle().getIdentifier())); context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM); } } packages/SystemUI/res/values/config.xml +1 −0 Original line number Diff line number Diff line Loading @@ -350,6 +350,7 @@ <item>com.android.systemui.globalactions.GlobalActionsComponent</item> <item>com.android.systemui.ScreenDecorations</item> <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item> <item>com.android.systemui.SliceBroadcastRelayHandler</item> </string-array> <!-- SystemUI vender service, used in config_systemUIServiceComponents. --> Loading packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java 0 → 100644 +114 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.systemui; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.SliceBroadcastRelay; /** * Allows settings to register certain broadcasts to launch the settings app for pinned slices. * @see SliceBroadcastRelay */ public class SliceBroadcastRelayHandler extends SystemUI { private static final String TAG = "SliceBroadcastRelay"; private static final boolean DEBUG = false; private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>(); @Override public void start() { if (DEBUG) Log.d(TAG, "Start"); IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER); filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER); mContext.registerReceiver(mReceiver, filter); } @VisibleForTesting void handleIntent(Intent intent) { if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) { Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI); ComponentName receiverClass = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER); IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER); if (DEBUG) Log.d(TAG, "Register " + uri + " " + receiverClass + " " + filter); getOrCreateRelay(uri).register(mContext, receiverClass, filter); } else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) { Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI); if (DEBUG) Log.d(TAG, "Unregister " + uri); getAndRemoveRelay(uri).unregister(mContext); } } private BroadcastRelay getOrCreateRelay(Uri uri) { BroadcastRelay ret = mRelays.get(uri); if (ret == null) { ret = new BroadcastRelay(uri); mRelays.put(uri, ret); } return ret; } private BroadcastRelay getAndRemoveRelay(Uri uri) { return mRelays.remove(uri); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { handleIntent(intent); } }; private static class BroadcastRelay extends BroadcastReceiver { private final ArraySet<ComponentName> mReceivers = new ArraySet<>(); private final UserHandle mUserId; public BroadcastRelay(Uri uri) { mUserId = new UserHandle(ContentProvider.getUserIdFromUri(uri)); } public void register(Context context, ComponentName receiver, IntentFilter filter) { mReceivers.add(receiver); context.registerReceiver(this, filter); } public void unregister(Context context) { context.unregisterReceiver(this); } @Override public void onReceive(Context context, Intent intent) { intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); for (ComponentName receiver : mReceivers) { intent.setComponent(receiver); if (DEBUG) Log.d(TAG, "Forwarding " + receiver + " " + intent + " " + mUserId); context.sendBroadcastAsUser(intent, mUserId); } } } } packages/SystemUI/tests/AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,12 @@ <service android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService" android:process=":killable" /> <receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver"> <intent-filter> <action android:name="com.android.systemui.action.TEST_ACTION" /> </intent-filter> </receiver> </application> <instrumentation android:name="android.testing.TestableInstrumentation" Loading packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java 0 → 100644 +130 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.systemui; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import com.android.settingslib.SliceBroadcastRelay; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @RunWith(AndroidTestingRunner.class) @SmallTest public class SliceBroadcastRelayHandlerTest extends SysuiTestCase { private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION"; @Test public void testRegister() { Uri testUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority("something") .path("test") .build(); SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler(); relayHandler.mContext = spy(mContext); Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER); intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER, new ComponentName(mContext.getPackageName(), Receiver.class.getName())); IntentFilter value = new IntentFilter(TEST_ACTION); intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value); relayHandler.handleIntent(intent); verify(relayHandler.mContext).registerReceiver(any(), eq(value)); } @Test public void testUnregister() { Uri testUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority("something") .path("test") .build(); SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler(); relayHandler.mContext = spy(mContext); Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER); intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER, new ComponentName(mContext.getPackageName(), Receiver.class.getName())); IntentFilter value = new IntentFilter(TEST_ACTION); intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value); relayHandler.handleIntent(intent); ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value)); intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER); intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); relayHandler.handleIntent(intent); verify(relayHandler.mContext).unregisterReceiver(eq(relay.getValue())); } @Test public void testRelay() { Receiver.sReceiver = mock(BroadcastReceiver.class); Uri testUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority("something") .path("test") .build(); SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler(); relayHandler.mContext = spy(mContext); Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER); intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER, new ComponentName(mContext.getPackageName(), Receiver.class.getName())); IntentFilter value = new IntentFilter(TEST_ACTION); intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value); relayHandler.handleIntent(intent); ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value)); relay.getValue().onReceive(relayHandler.mContext, new Intent(TEST_ACTION)); verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any()); } public static class Receiver extends BroadcastReceiver { private static BroadcastReceiver sReceiver; @Override public void onReceive(Context context, Intent intent) { if (sReceiver != null) sReceiver.onReceive(context, intent); } } } No newline at end of file Loading
packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.settingslib; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Process; import android.os.UserHandle; /** * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices. */ public class SliceBroadcastRelay { public static final String ACTION_REGISTER = "com.android.settingslib.action.REGISTER_SLICE_RECEIVER"; public static final String ACTION_UNREGISTER = "com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER"; public static final String SYSTEMUI_PACKAGE = "com.android.systemui"; public static final String EXTRA_URI = "uri"; public static final String EXTRA_RECEIVER = "receiver"; public static final String EXTRA_FILTER = "filter"; public static void registerReceiver(Context context, Uri registerKey, Class<? extends BroadcastReceiver> receiver, IntentFilter filter) { Intent registerBroadcast = new Intent(ACTION_REGISTER); registerBroadcast.setPackage(SYSTEMUI_PACKAGE); registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey, Process.myUserHandle().getIdentifier())); registerBroadcast.putExtra(EXTRA_RECEIVER, new ComponentName(context.getPackageName(), receiver.getName())); registerBroadcast.putExtra(EXTRA_FILTER, filter); context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM); } public static void unregisterReceivers(Context context, Uri registerKey) { Intent registerBroadcast = new Intent(ACTION_UNREGISTER); registerBroadcast.setPackage(SYSTEMUI_PACKAGE); registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey, Process.myUserHandle().getIdentifier())); context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM); } }
packages/SystemUI/res/values/config.xml +1 −0 Original line number Diff line number Diff line Loading @@ -350,6 +350,7 @@ <item>com.android.systemui.globalactions.GlobalActionsComponent</item> <item>com.android.systemui.ScreenDecorations</item> <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item> <item>com.android.systemui.SliceBroadcastRelayHandler</item> </string-array> <!-- SystemUI vender service, used in config_systemUIServiceComponents. --> Loading
packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java 0 → 100644 +114 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.systemui; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.SliceBroadcastRelay; /** * Allows settings to register certain broadcasts to launch the settings app for pinned slices. * @see SliceBroadcastRelay */ public class SliceBroadcastRelayHandler extends SystemUI { private static final String TAG = "SliceBroadcastRelay"; private static final boolean DEBUG = false; private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>(); @Override public void start() { if (DEBUG) Log.d(TAG, "Start"); IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER); filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER); mContext.registerReceiver(mReceiver, filter); } @VisibleForTesting void handleIntent(Intent intent) { if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) { Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI); ComponentName receiverClass = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER); IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER); if (DEBUG) Log.d(TAG, "Register " + uri + " " + receiverClass + " " + filter); getOrCreateRelay(uri).register(mContext, receiverClass, filter); } else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) { Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI); if (DEBUG) Log.d(TAG, "Unregister " + uri); getAndRemoveRelay(uri).unregister(mContext); } } private BroadcastRelay getOrCreateRelay(Uri uri) { BroadcastRelay ret = mRelays.get(uri); if (ret == null) { ret = new BroadcastRelay(uri); mRelays.put(uri, ret); } return ret; } private BroadcastRelay getAndRemoveRelay(Uri uri) { return mRelays.remove(uri); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { handleIntent(intent); } }; private static class BroadcastRelay extends BroadcastReceiver { private final ArraySet<ComponentName> mReceivers = new ArraySet<>(); private final UserHandle mUserId; public BroadcastRelay(Uri uri) { mUserId = new UserHandle(ContentProvider.getUserIdFromUri(uri)); } public void register(Context context, ComponentName receiver, IntentFilter filter) { mReceivers.add(receiver); context.registerReceiver(this, filter); } public void unregister(Context context) { context.unregisterReceiver(this); } @Override public void onReceive(Context context, Intent intent) { intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); for (ComponentName receiver : mReceivers) { intent.setComponent(receiver); if (DEBUG) Log.d(TAG, "Forwarding " + receiver + " " + intent + " " + mUserId); context.sendBroadcastAsUser(intent, mUserId); } } } }
packages/SystemUI/tests/AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,12 @@ <service android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService" android:process=":killable" /> <receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver"> <intent-filter> <action android:name="com.android.systemui.action.TEST_ACTION" /> </intent-filter> </receiver> </application> <instrumentation android:name="android.testing.TestableInstrumentation" Loading
packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java 0 → 100644 +130 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.systemui; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import com.android.settingslib.SliceBroadcastRelay; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @RunWith(AndroidTestingRunner.class) @SmallTest public class SliceBroadcastRelayHandlerTest extends SysuiTestCase { private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION"; @Test public void testRegister() { Uri testUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority("something") .path("test") .build(); SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler(); relayHandler.mContext = spy(mContext); Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER); intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER, new ComponentName(mContext.getPackageName(), Receiver.class.getName())); IntentFilter value = new IntentFilter(TEST_ACTION); intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value); relayHandler.handleIntent(intent); verify(relayHandler.mContext).registerReceiver(any(), eq(value)); } @Test public void testUnregister() { Uri testUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority("something") .path("test") .build(); SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler(); relayHandler.mContext = spy(mContext); Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER); intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER, new ComponentName(mContext.getPackageName(), Receiver.class.getName())); IntentFilter value = new IntentFilter(TEST_ACTION); intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value); relayHandler.handleIntent(intent); ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value)); intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER); intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); relayHandler.handleIntent(intent); verify(relayHandler.mContext).unregisterReceiver(eq(relay.getValue())); } @Test public void testRelay() { Receiver.sReceiver = mock(BroadcastReceiver.class); Uri testUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority("something") .path("test") .build(); SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler(); relayHandler.mContext = spy(mContext); Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER); intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER, new ComponentName(mContext.getPackageName(), Receiver.class.getName())); IntentFilter value = new IntentFilter(TEST_ACTION); intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value); relayHandler.handleIntent(intent); ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value)); relay.getValue().onReceive(relayHandler.mContext, new Intent(TEST_ACTION)); verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any()); } public static class Receiver extends BroadcastReceiver { private static BroadcastReceiver sReceiver; @Override public void onReceive(Context context, Intent intent) { if (sReceiver != null) sReceiver.onReceive(context, intent); } } } No newline at end of file