From 0b5e52ccd8e8308c6c7a8d04265f4bcd2b84f052 Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Thu, 19 Apr 2018 22:14:46 -0400 Subject: [PATCH] Add way to use sysui as a broadcast relay for slices Test: runtest systemui Bug: 78139069 Change-Id: I64c4d56cca005cec7204bf45215bb7b0015f4571 --- .../settingslib/SliceBroadcastRelay.java | 63 +++++++++ packages/SystemUI/res/values/config.xml | 1 + .../systemui/SliceBroadcastRelayHandler.java | 114 +++++++++++++++ packages/SystemUI/tests/AndroidManifest.xml | 6 + .../SliceBroadcastRelayHandlerTest.java | 130 ++++++++++++++++++ 5 files changed, 314 insertions(+) create mode 100644 packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java create mode 100644 packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java diff --git a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java new file mode 100644 index 0000000000000..e92b36a45645d --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java @@ -0,0 +1,63 @@ +/* + * 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 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); + } +} diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index f49d3de4a6a4d..7eb08c4c3582c 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -350,6 +350,7 @@ com.android.systemui.globalactions.GlobalActionsComponent com.android.systemui.ScreenDecorations com.android.systemui.fingerprint.FingerprintDialogImpl + com.android.systemui.SliceBroadcastRelayHandler diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java new file mode 100644 index 0000000000000..68f583695596a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java @@ -0,0 +1,114 @@ +/* + * 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 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 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); + } + } + } +} diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index 767a24a27a26f..1be83229cf22a 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -57,6 +57,12 @@ + + + + + + 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 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