Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d932a0d authored by Jernej Virag's avatar Jernej Virag
Browse files

Handle slice broadcasts on BG thread

There's no point for those to run on main thread and cause ANRs - we're
just rebroadcasting broadcasts.

This CL also enhances thread safety for the code running this.

Bug: 334767208
Flag: com.android.systemui.slice_broadcast_relay_in_background
Test: newly updated SliceBroadcastRelayHandler unit test
Change-Id: I11ef3e49458c4e66e4176a5ab64e0328264f889f
parent 9ab1d3e7
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -784,3 +784,13 @@ flag {
      purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "slice_broadcast_relay_in_background"
    namespace: "systemui"
    description: "Move handling of slice broadcast relay broadcasts to background threads"
    bug: "334767208"
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}
 No newline at end of file
+40 −14
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@

package com.android.systemui;

import static com.android.systemui.Flags.sliceBroadcastRelayInBackground;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -23,13 +25,19 @@ 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 androidx.annotation.GuardedBy;
import androidx.annotation.WorkerThread;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.SliceBroadcastRelay;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;

import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;

import javax.inject.Inject;

@@ -42,14 +50,18 @@ public class SliceBroadcastRelayHandler implements CoreStartable {
    private static final String TAG = "SliceBroadcastRelay";
    private static final boolean DEBUG = false;

    @GuardedBy("mRelays")
    private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
    private final Context mContext;
    private final BroadcastDispatcher mBroadcastDispatcher;
    private final Executor mBackgroundExecutor;

    @Inject
    public SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher) {
    public SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher,
                                      @Background Executor backgroundExecutor) {
        mContext = context;
        mBroadcastDispatcher = broadcastDispatcher;
        mBackgroundExecutor = backgroundExecutor;
    }

    @Override
@@ -57,21 +69,29 @@ public class SliceBroadcastRelayHandler implements CoreStartable {
        if (DEBUG) Log.d(TAG, "Start");
        IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER);
        filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER);

        if (sliceBroadcastRelayInBackground()) {
            mBroadcastDispatcher.registerReceiver(mReceiver, filter, mBackgroundExecutor);
        } else {
            mBroadcastDispatcher.registerReceiver(mReceiver, filter);
        }
    }

    // This does not use BroadcastDispatcher as the filter may have schemas or mime types.
    @WorkerThread
    @VisibleForTesting
    void handleIntent(Intent intent) {
        if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) {
            Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
            Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI, Uri.class);
            ComponentName receiverClass =
                    intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER);
            IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER);
                    intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
                            ComponentName.class);
            IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER,
                    IntentFilter.class);
            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);
            Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI, Uri.class);
            if (DEBUG) Log.d(TAG, "Unregister " + uri);
            BroadcastRelay relay = getAndRemoveRelay(uri);
            if (relay != null) {
@@ -80,7 +100,9 @@ public class SliceBroadcastRelayHandler implements CoreStartable {
        }
    }

    @WorkerThread
    private BroadcastRelay getOrCreateRelay(Uri uri) {
        synchronized (mRelays) {
            BroadcastRelay ret = mRelays.get(uri);
            if (ret == null) {
                ret = new BroadcastRelay(uri);
@@ -88,10 +110,14 @@ public class SliceBroadcastRelayHandler implements CoreStartable {
            }
            return ret;
        }
    }

    @WorkerThread
    private BroadcastRelay getAndRemoveRelay(Uri uri) {
        synchronized (mRelays) {
            return mRelays.remove(uri);
        }
    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
@@ -102,7 +128,7 @@ public class SliceBroadcastRelayHandler implements CoreStartable {

    private static class BroadcastRelay extends BroadcastReceiver {

        private final ArraySet<ComponentName> mReceivers = new ArraySet<>();
        private final CopyOnWriteArraySet<ComponentName> mReceivers = new CopyOnWriteArraySet<>();
        private final UserHandle mUserId;
        private final Uri mUri;

+45 −4
Original line number Diff line number Diff line
@@ -31,36 +31,57 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.testing.AndroidTestingRunner;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;

import androidx.test.filters.SmallTest;

import com.android.settingslib.SliceBroadcastRelay;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@RunWith(AndroidTestingRunner.class)
import java.util.List;

@RunWith(Parameterized.class)
@SmallTest
public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {

    @Parameterized.Parameters(name = "{0}")
    public static List<FlagsParameterization> getFlags() {
        return FlagsParameterization.allCombinationsOf(
                Flags.FLAG_SLICE_BROADCAST_RELAY_IN_BACKGROUND);
    }

    private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION";
    private final FakeExecutor mBackgroundExecutor = new FakeExecutor(new FakeSystemClock());

    private SliceBroadcastRelayHandler mRelayHandler;
    private Context mSpyContext;
    @Mock
    private BroadcastDispatcher mBroadcastDispatcher;


    public SliceBroadcastRelayHandlerTest(FlagsParameterization flags) {
        mSetFlagsRule.setFlagsParameterization(flags);
    }

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mSpyContext = spy(mContext);

        mRelayHandler = new SliceBroadcastRelayHandler(mSpyContext, mBroadcastDispatcher);
        mRelayHandler = new SliceBroadcastRelayHandler(mSpyContext, mBroadcastDispatcher,
                mBackgroundExecutor);
    }

    @Test
@@ -80,6 +101,7 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
        intent.putExtra(SliceBroadcastRelay.EXTRA_URI, testUri);

        mRelayHandler.handleIntent(intent);
        mBackgroundExecutor.runAllReady();
        verify(mSpyContext).registerReceiver(any(), eq(value), anyInt());
    }

@@ -99,12 +121,14 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
        intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);

        mRelayHandler.handleIntent(intent);
        mBackgroundExecutor.runAllReady();
        ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
        verify(mSpyContext).registerReceiver(relay.capture(), eq(value), anyInt());

        intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
        intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
        mRelayHandler.handleIntent(intent);
        mBackgroundExecutor.runAllReady();
        verify(mSpyContext).unregisterReceiver(eq(relay.getValue()));
    }

@@ -119,6 +143,7 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
        Intent intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
        intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
        mRelayHandler.handleIntent(intent);
        mBackgroundExecutor.runAllReady();
        // No crash
    }

@@ -138,6 +163,7 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
        intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);

        mRelayHandler.handleIntent(intent);
        mBackgroundExecutor.runAllReady();
        ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
        verify(mSpyContext).registerReceiver(relay.capture(), eq(value), anyInt());
        relay.getValue().onReceive(mSpyContext, new Intent(TEST_ACTION));
@@ -146,8 +172,10 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
    }

    @Test
    public void testRegisteredWithDispatcher() {
    @DisableFlags(Flags.FLAG_SLICE_BROADCAST_RELAY_IN_BACKGROUND)
    public void testRegisteredWithDispatcher_onMainThread() {
        mRelayHandler.start();
        mBackgroundExecutor.runAllReady();

        verify(mBroadcastDispatcher)
                .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
@@ -155,6 +183,19 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
                .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
    }

    @Test
    @EnableFlags(Flags.FLAG_SLICE_BROADCAST_RELAY_IN_BACKGROUND)
    public void testRegisteredWithDispatcher_onBackgroundThread() {
        mRelayHandler.start();
        mBackgroundExecutor.runAllReady();

        verify(mBroadcastDispatcher)
                .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
                        eq(mBackgroundExecutor));
        verify(mSpyContext, never())
                .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
    }

    public static class Receiver extends BroadcastReceiver {
        private static BroadcastReceiver sReceiver;