[Settings] Code refactor for BroadcastReceiver under Lifecycle
This is an implementation of BroadcastReceiver which supported by
LifecycleCallbackConverter.
Registration of BroadcastReceiver only take place when Lifecycle in
RESUME status.
Bug: 229689535
Test: unit test
Change-Id: Ia2af82d5cbb391034627e5259a9e0c8683a0c5a1
(cherry picked from commit c2030898ef2540d5619c66d5fd4066f2267712bd)
diff --git a/src/com/android/settings/network/helper/LifecycleCallbackIntentReceiver.java b/src/com/android/settings/network/helper/LifecycleCallbackIntentReceiver.java
new file mode 100644
index 0000000..8aaa53e
--- /dev/null
+++ b/src/com/android/settings/network/helper/LifecycleCallbackIntentReceiver.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 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.settings.network.helper;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.Lifecycle;
+import java.util.function.Consumer;
+
+/**
+ * A {@link BroadcastReceiver} for {@link Intent}.
+ *
+ * This is {@link BroadcastReceiver} supported by {@link LifecycleCallbackConverter},
+ * and only register when state is either START or RESUME.
+ */
+@VisibleForTesting
+public class LifecycleCallbackIntentReceiver extends LifecycleCallbackConverter<Intent> {
+ private static final String TAG = "LifecycleCallbackIntentReceiver";
+
+ @VisibleForTesting
+ protected final BroadcastReceiver mReceiver;
+
+ private final Runnable mRegisterCallback;
+ private final Runnable mUnRegisterCallback;
+
+ /**
+ * Constructor
+ * @param lifecycle {@link Lifecycle} to monitor
+ * @param context for this BroadcastReceiver
+ * @param filter the IntentFilter for BroadcastReceiver
+ * @param broadcastPermission for permission when listening
+ * @param scheduler for running in background thread
+ * @param resultCallback for the Intent from BroadcastReceiver
+ */
+ @VisibleForTesting
+ public LifecycleCallbackIntentReceiver(@NonNull Lifecycle lifecycle,
+ @NonNull Context context, @NonNull IntentFilter filter,
+ String broadcastPermission, Handler scheduler,
+ @NonNull Consumer<Intent> resultCallback) {
+ super(lifecycle, resultCallback);
+
+ // BroadcastReceiver
+ mReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (isInitialStickyBroadcast()) {
+ return;
+ }
+ final String action = intent.getAction();
+ if ((action == null) || (action.length() <= 0)) {
+ return;
+ }
+ postResult(intent);
+ }
+ };
+
+ // Register operation
+ mRegisterCallback = () -> {
+ Intent initIntent = context.registerReceiver(mReceiver,
+ filter, broadcastPermission, scheduler);
+ if (initIntent != null) {
+ postResult(initIntent);
+ }
+ };
+
+ // Un-Register operation
+ mUnRegisterCallback = () -> {
+ context.unregisterReceiver(mReceiver);
+ };
+ }
+
+ @Override
+ public void setCallbackActive(boolean isActive) {
+ super.setCallbackActive(isActive);
+ Runnable op = (isActive) ? mRegisterCallback : mUnRegisterCallback;
+ op.run();
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ if (isCallbackActive()) {
+ setCallbackActive(false);
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackIntentReceiverTest.java b/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackIntentReceiverTest.java
new file mode 100644
index 0000000..c85937d
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackIntentReceiverTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 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.settings.network.helper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class LifecycleCallbackIntentReceiverTest implements LifecycleOwner {
+
+ private final LifecycleRegistry mRegistry = LifecycleRegistry.createUnsafe(this);
+
+ private static final String TEST_SCHEDULER_HANDLER = "testScheduler";
+ private static final String TEST_INTENT_ACTION = "testAction";
+ private static final String TEST_INTENT_PERMISSION = "testPermission";
+
+ private Context mContext;
+ private Intent mIntent;
+ private IntentFilter mIntentFilter;
+ private Handler mHandler;
+ private TestConsumer mConsumer;
+
+ private TestObj mTarget;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+
+ mIntentFilter = new IntentFilter(TEST_INTENT_ACTION);
+ mIntent = new Intent(TEST_INTENT_ACTION);
+
+ HandlerThread thread = new HandlerThread(TEST_SCHEDULER_HANDLER);
+ thread.start();
+
+ mHandler = new Handler(thread.getLooper());
+ mConsumer = new TestConsumer();
+
+ mTarget = new TestObj(getLifecycle(), mContext,
+ mIntentFilter, TEST_INTENT_PERMISSION,
+ mHandler, mConsumer);
+ }
+
+ public Lifecycle getLifecycle() {
+ return mRegistry;
+ }
+
+ @Test
+ public void receiver_register_whenActive() {
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+
+ assertThat(mTarget.getCallbackActiveCount(true)
+ + mTarget.getCallbackActiveCount(false)).isEqualTo(0);
+
+ mTarget.mReceiver.onReceive(mContext, mIntent);
+
+ assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
+
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+
+ assertThat(mTarget.getCallbackActiveCount(true)).isEqualTo(1);
+ assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
+
+ mTarget.mReceiver.onReceive(mContext, mIntent);
+
+ assertThat(mConsumer.getCallbackCount()).isEqualTo(1);
+ assertThat(mConsumer.getData()).isEqualTo(mIntent);
+ }
+
+ @Test
+ public void receiver_unregister_whenInActive() {
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+
+ assertThat(mTarget.getCallbackActiveCount(false)).isEqualTo(1);
+
+ mTarget.mReceiver.onReceive(mContext, mIntent);
+
+ assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void receiver_register_whenReActive() {
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+
+ assertThat(mTarget.getCallbackActiveCount(true)).isEqualTo(2);
+
+ mTarget.mReceiver.onReceive(mContext, mIntent);
+
+ assertThat(mConsumer.getCallbackCount()).isEqualTo(1);
+ assertThat(mConsumer.getData()).isEqualTo(mIntent);
+ }
+
+ @Test
+ public void receiver_close_whenDestroy() {
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
+
+ assertThat(mTarget.getCallbackActiveCount(false)).isEqualTo(1);
+
+ mTarget.mReceiver.onReceive(mContext, mIntent);
+
+ assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
+ }
+
+ public static class TestConsumer implements Consumer<Intent> {
+ long mNumberOfCallback;
+ Intent mLatestData;
+
+ public TestConsumer() {}
+
+ public void accept(Intent data) {
+ mLatestData = data;
+ mNumberOfCallback ++;
+ }
+
+ protected long getCallbackCount() {
+ return mNumberOfCallback;
+ }
+
+ protected Intent getData() {
+ return mLatestData;
+ }
+ }
+
+ public static class TestObj extends LifecycleCallbackIntentReceiver {
+ long mCallbackActiveCount;
+ long mCallbackInActiveCount;
+
+ public TestObj(Lifecycle lifecycle, Context context, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, Consumer<Intent> resultCallback) {
+ super(lifecycle, context, filter, broadcastPermission, scheduler, resultCallback);
+ }
+
+ @Override
+ public void setCallbackActive(boolean isActive) {
+ if (isActive) {
+ mCallbackActiveCount ++;
+ } else {
+ mCallbackInActiveCount ++;
+ }
+ super.setCallbackActive(isActive);
+ }
+
+ protected long getCallbackActiveCount(boolean forActive) {
+ return forActive ? mCallbackActiveCount : mCallbackInActiveCount;
+ }
+ }
+}