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

Commit 39bf7dd5 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Fix dialog leak in RequestPermissionActivity

Dialog still show when activity destroyed will cause leak.

Dismiss dialog when activity onDestroy to fix this issue.

Fix: 279522922
Test: Manually with "Don't keep activities"
Test: Robolectric Test
Change-Id: I445f4b160020823a6f6e2883055218c1224e2c48
parent 08b2b68e
Loading
Loading
Loading
Loading
+49 −40
Original line number Original line Diff line number Diff line
@@ -72,6 +72,7 @@ public class RequestPermissionActivity extends Activity implements
    private int mRequest;
    private int mRequest;


    private AlertDialog mDialog;
    private AlertDialog mDialog;
    private AlertDialog mRequestDialog;


    private BroadcastReceiver mReceiver;
    private BroadcastReceiver mReceiver;


@@ -96,12 +97,12 @@ public class RequestPermissionActivity extends Activity implements
        if (mRequest == REQUEST_DISABLE) {
        if (mRequest == REQUEST_DISABLE) {
            switch (btState) {
            switch (btState) {
                case BluetoothAdapter.STATE_OFF:
                case BluetoothAdapter.STATE_OFF:
                case BluetoothAdapter.STATE_TURNING_OFF: {
                case BluetoothAdapter.STATE_TURNING_OFF:
                    proceedAndFinish();
                    proceedAndFinish();
                } break;
                    break;

                case BluetoothAdapter.STATE_ON:
                case BluetoothAdapter.STATE_ON:
                case BluetoothAdapter.STATE_TURNING_ON: {
                case BluetoothAdapter.STATE_TURNING_ON:
                    mRequestDialog =
                            RequestPermissionHelper.INSTANCE.requestDisable(this, mAppLabel,
                            RequestPermissionHelper.INSTANCE.requestDisable(this, mAppLabel,
                                    () -> {
                                    () -> {
                                        onDisableConfirmed();
                                        onDisableConfirmed();
@@ -111,18 +112,20 @@ public class RequestPermissionActivity extends Activity implements
                                        cancelAndFinish();
                                        cancelAndFinish();
                                        return Unit.INSTANCE;
                                        return Unit.INSTANCE;
                                    });
                                    });
                } break;
                    if (mRequestDialog != null) {

                        mRequestDialog.show();
                default: {
                    }
                    break;
                default:
                    Log.e(TAG, "Unknown adapter state: " + btState);
                    Log.e(TAG, "Unknown adapter state: " + btState);
                    cancelAndFinish();
                    cancelAndFinish();
                } break;
                    break;
            }
            }
        } else {
        } else {
            switch (btState) {
            switch (btState) {
                case BluetoothAdapter.STATE_OFF:
                case BluetoothAdapter.STATE_OFF:
                case BluetoothAdapter.STATE_TURNING_OFF:
                case BluetoothAdapter.STATE_TURNING_OFF:
                case BluetoothAdapter.STATE_TURNING_ON: {
                case BluetoothAdapter.STATE_TURNING_ON:
                    /*
                    /*
                     * Strictly speaking STATE_TURNING_ON belong with STATE_ON;
                     * Strictly speaking STATE_TURNING_ON belong with STATE_ON;
                     * however, BT may not be ready when the user clicks yes and we
                     * however, BT may not be ready when the user clicks yes and we
@@ -131,7 +134,8 @@ public class RequestPermissionActivity extends Activity implements
                     * case via the broadcast receiver.
                     * case via the broadcast receiver.
                     */
                     */


                    // Start the helper activity to ask the user about enabling bt AND discovery
                    // Show the helper dialog to ask the user about enabling bt AND discovery
                    mRequestDialog =
                            RequestPermissionHelper.INSTANCE.requestEnable(this, mAppLabel,
                            RequestPermissionHelper.INSTANCE.requestEnable(this, mAppLabel,
                                    mRequest == REQUEST_ENABLE_DISCOVERABLE ? mTimeout : -1,
                                    mRequest == REQUEST_ENABLE_DISCOVERABLE ? mTimeout : -1,
                                    () -> {
                                    () -> {
@@ -142,9 +146,11 @@ public class RequestPermissionActivity extends Activity implements
                                        cancelAndFinish();
                                        cancelAndFinish();
                                        return Unit.INSTANCE;
                                        return Unit.INSTANCE;
                                    });
                                    });
                } break;
                    if (mRequestDialog != null) {

                        mRequestDialog.show();
                case BluetoothAdapter.STATE_ON: {
                    }
                    break;
                case BluetoothAdapter.STATE_ON:
                    if (mRequest == REQUEST_ENABLE) {
                    if (mRequest == REQUEST_ENABLE) {
                        // Nothing to do. Already enabled.
                        // Nothing to do. Already enabled.
                        proceedAndFinish();
                        proceedAndFinish();
@@ -152,12 +158,11 @@ public class RequestPermissionActivity extends Activity implements
                        // Ask the user about enabling discovery mode
                        // Ask the user about enabling discovery mode
                        createDialog();
                        createDialog();
                    }
                    }
                } break;
                    break;

                default:
                default: {
                    Log.e(TAG, "Unknown adapter state: " + btState);
                    Log.e(TAG, "Unknown adapter state: " + btState);
                    cancelAndFinish();
                    cancelAndFinish();
                } break;
                    break;
            }
            }
        }
        }
    }
    }
@@ -275,10 +280,6 @@ public class RequestPermissionActivity extends Activity implements
            }
            }
        }
        }


        if (mDialog != null) {
            mDialog.dismiss();
        }

        setResult(returnCode);
        setResult(returnCode);
        finish();
        finish();
    }
    }
@@ -365,6 +366,14 @@ public class RequestPermissionActivity extends Activity implements
            unregisterReceiver(mReceiver);
            unregisterReceiver(mReceiver);
            mReceiver = null;
            mReceiver = null;
        }
        }
        if (mDialog != null && mDialog.isShowing()) {
            mDialog.dismiss();
            mDialog = null;
        }
        if (mRequestDialog != null && mRequestDialog.isShowing()) {
            mRequestDialog.dismiss();
            mRequestDialog = null;
        }
    }
    }


    @Override
    @Override
+8 −8
Original line number Original line Diff line number Diff line
@@ -30,20 +30,20 @@ object RequestPermissionHelper {
        timeout: Int,
        timeout: Int,
        onAllow: () -> Unit,
        onAllow: () -> Unit,
        onDeny: () -> Unit,
        onDeny: () -> Unit,
    ) {
    ): AlertDialog? {
        if (context.resources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
        if (context.resources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
            // Don't even show the dialog if configured this way
            // Don't even show the dialog if configured this way
            onAllow()
            onAllow()
            return
            return null
        }
        }
        AlertDialog.Builder(context).apply {
        return AlertDialog.Builder(context).apply {
            setMessage(context.getEnableMessage(timeout, appLabel))
            setMessage(context.getEnableMessage(timeout, appLabel))
            setPositiveButton(R.string.allow) { _, _ ->
            setPositiveButton(R.string.allow) { _, _ ->
                if (context.isDisallowBluetooth()) onDeny() else onAllow()
                if (context.isDisallowBluetooth()) onDeny() else onAllow()
            }
            }
            setNegativeButton(R.string.deny) { _, _ -> onDeny() }
            setNegativeButton(R.string.deny) { _, _ -> onDeny() }
            setOnCancelListener { onDeny() }
            setOnCancelListener { onDeny() }
        }.show()
        }.create()
    }
    }


    fun requestDisable(
    fun requestDisable(
@@ -51,18 +51,18 @@ object RequestPermissionHelper {
        appLabel: CharSequence?,
        appLabel: CharSequence?,
        onAllow: () -> Unit,
        onAllow: () -> Unit,
        onDeny: () -> Unit,
        onDeny: () -> Unit,
    ) {
    ): AlertDialog? {
        if (context.resources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
        if (context.resources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
            // Don't even show the dialog if configured this way
            // Don't even show the dialog if configured this way
            onAllow()
            onAllow()
            return
            return null
        }
        }
        AlertDialog.Builder(context).apply {
        return AlertDialog.Builder(context).apply {
            setMessage(context.getDisableMessage(appLabel))
            setMessage(context.getDisableMessage(appLabel))
            setPositiveButton(R.string.allow) { _, _ -> onAllow() }
            setPositiveButton(R.string.allow) { _, _ -> onAllow() }
            setNegativeButton(R.string.deny) { _, _ -> onDeny() }
            setNegativeButton(R.string.deny) { _, _ -> onDeny() }
            setOnCancelListener { onDeny() }
            setOnCancelListener { onDeny() }
        }.show()
        }.create()
    }
    }
}
}


+102 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 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.bluetooth

import android.bluetooth.BluetoothAdapter
import android.content.Intent
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.android.controller.ActivityController
import org.robolectric.annotation.Config
import org.robolectric.shadow.api.Shadow
import org.robolectric.shadows.ShadowBluetoothAdapter

@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowAlertDialogCompat::class, ShadowBluetoothAdapter::class])
class RequestPermissionActivityTest {
    private lateinit var activityController: ActivityController<RequestPermissionActivity>
    private lateinit var bluetoothAdapter: ShadowBluetoothAdapter

    @Before
    fun setUp() {
        bluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter())
    }

    @After
    fun tearDown() {
        activityController.pause().stop().destroy()
        ShadowAlertDialogCompat.reset()
    }

    @Test
    fun requestEnable_whenBluetoothIsOff_showConfirmDialog() {
        bluetoothAdapter.setState(BluetoothAdapter.STATE_OFF)

        createActivity(action = BluetoothAdapter.ACTION_REQUEST_ENABLE)

        val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
        val shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog)
        assertThat(shadowDialog.message.toString())
            .isEqualTo("An app wants to turn on Bluetooth")
    }

    @Test
    fun requestEnable_whenBluetoothIsOn_doNothing() {
        bluetoothAdapter.setState(BluetoothAdapter.STATE_ON)

        createActivity(action = BluetoothAdapter.ACTION_REQUEST_ENABLE)

        val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
        assertThat(dialog).isNull()
    }

    @Test
    fun requestDisable_whenBluetoothIsOff_doNothing() {
        bluetoothAdapter.setState(BluetoothAdapter.STATE_OFF)

        createActivity(action = BluetoothAdapter.ACTION_REQUEST_DISABLE)

        val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
        assertThat(dialog).isNull()
    }

    @Test
    fun requestDisable_whenBluetoothIsOn_showConfirmDialog() {
        bluetoothAdapter.setState(BluetoothAdapter.STATE_ON)

        createActivity(action = BluetoothAdapter.ACTION_REQUEST_DISABLE)

        val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
        val shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog)
        assertThat(shadowDialog.message.toString())
            .isEqualTo("An app wants to turn off Bluetooth")
    }

    private fun createActivity(action: String) {
        activityController =
            ActivityController.of(RequestPermissionActivity(), Intent(action)).apply {
                create()
                start()
                postCreate(null)
                resume()
            }
    }
}
 No newline at end of file
+16 −8
Original line number Original line Diff line number Diff line
@@ -49,13 +49,14 @@ class RequestPermissionHelperTest {
    @Test
    @Test
    fun requestEnable_withAppLabelAndNoTimeout_hasCorrectMessage() {
    fun requestEnable_withAppLabelAndNoTimeout_hasCorrectMessage() {
        val activity = activityController.get()
        val activity = activityController.get()

        RequestPermissionHelper.requestEnable(
        RequestPermissionHelper.requestEnable(
            context = activity,
            context = activity,
            appLabel = "App Label",
            appLabel = "App Label",
            timeout = -1,
            timeout = -1,
            onAllow = {},
            onAllow = {},
            onDeny = {},
            onDeny = {},
        )
        )!!.show()


        assertLatestMessageIs("App Label wants to turn on Bluetooth")
        assertLatestMessageIs("App Label wants to turn on Bluetooth")
    }
    }
@@ -63,13 +64,14 @@ class RequestPermissionHelperTest {
    @Test
    @Test
    fun requestEnable_withAppLabelAndZeroTimeout_hasCorrectMessage() {
    fun requestEnable_withAppLabelAndZeroTimeout_hasCorrectMessage() {
        val activity = activityController.get()
        val activity = activityController.get()

        RequestPermissionHelper.requestEnable(
        RequestPermissionHelper.requestEnable(
            context = activity,
            context = activity,
            appLabel = "App Label",
            appLabel = "App Label",
            timeout = 0,
            timeout = 0,
            onAllow = {},
            onAllow = {},
            onDeny = {},
            onDeny = {},
        )
        )!!.show()


        assertLatestMessageIs(
        assertLatestMessageIs(
            "App Label wants to turn on Bluetooth and make your phone visible to other devices. " +
            "App Label wants to turn on Bluetooth and make your phone visible to other devices. " +
@@ -80,13 +82,14 @@ class RequestPermissionHelperTest {
    @Test
    @Test
    fun requestEnable_withAppLabelAndNormalTimeout_hasCorrectMessage() {
    fun requestEnable_withAppLabelAndNormalTimeout_hasCorrectMessage() {
        val activity = activityController.get()
        val activity = activityController.get()

        RequestPermissionHelper.requestEnable(
        RequestPermissionHelper.requestEnable(
            context = activity,
            context = activity,
            appLabel = "App Label",
            appLabel = "App Label",
            timeout = 120,
            timeout = 120,
            onAllow = {},
            onAllow = {},
            onDeny = {},
            onDeny = {},
        )
        )!!.show()


        assertLatestMessageIs(
        assertLatestMessageIs(
            "App Label wants to turn on Bluetooth and make your phone visible to other devices " +
            "App Label wants to turn on Bluetooth and make your phone visible to other devices " +
@@ -97,13 +100,14 @@ class RequestPermissionHelperTest {
    @Test
    @Test
    fun requestEnable_withNoAppLabelAndNoTimeout_hasCorrectMessage() {
    fun requestEnable_withNoAppLabelAndNoTimeout_hasCorrectMessage() {
        val activity = activityController.get()
        val activity = activityController.get()

        RequestPermissionHelper.requestEnable(
        RequestPermissionHelper.requestEnable(
            context = activity,
            context = activity,
            appLabel = null,
            appLabel = null,
            timeout = -1,
            timeout = -1,
            onAllow = {},
            onAllow = {},
            onDeny = {},
            onDeny = {},
        )
        )!!.show()


        assertLatestMessageIs("An app wants to turn on Bluetooth")
        assertLatestMessageIs("An app wants to turn on Bluetooth")
    }
    }
@@ -111,13 +115,14 @@ class RequestPermissionHelperTest {
    @Test
    @Test
    fun requestEnable_withNoAppLabelAndZeroTimeout_hasCorrectMessage() {
    fun requestEnable_withNoAppLabelAndZeroTimeout_hasCorrectMessage() {
        val activity = activityController.get()
        val activity = activityController.get()

        RequestPermissionHelper.requestEnable(
        RequestPermissionHelper.requestEnable(
            context = activity,
            context = activity,
            appLabel = null,
            appLabel = null,
            timeout = 0,
            timeout = 0,
            onAllow = {},
            onAllow = {},
            onDeny = {},
            onDeny = {},
        )
        )!!.show()


        assertLatestMessageIs(
        assertLatestMessageIs(
            "An app wants to turn on Bluetooth and make your phone visible to other devices. " +
            "An app wants to turn on Bluetooth and make your phone visible to other devices. " +
@@ -128,13 +133,14 @@ class RequestPermissionHelperTest {
    @Test
    @Test
    fun requestEnable_withNoAppLabelAndNormalTimeout_hasCorrectMessage() {
    fun requestEnable_withNoAppLabelAndNormalTimeout_hasCorrectMessage() {
        val activity = activityController.get()
        val activity = activityController.get()

        RequestPermissionHelper.requestEnable(
        RequestPermissionHelper.requestEnable(
            context = activity,
            context = activity,
            appLabel = null,
            appLabel = null,
            timeout = 120,
            timeout = 120,
            onAllow = {},
            onAllow = {},
            onDeny = {},
            onDeny = {},
        )
        )!!.show()


        assertLatestMessageIs(
        assertLatestMessageIs(
            "An app wants to turn on Bluetooth and make your phone visible to other devices for " +
            "An app wants to turn on Bluetooth and make your phone visible to other devices for " +
@@ -177,12 +183,13 @@ class RequestPermissionHelperTest {
    @Test
    @Test
    fun requestDisable_withAppLabel_hasCorrectMessage() {
    fun requestDisable_withAppLabel_hasCorrectMessage() {
        val activity = activityController.get()
        val activity = activityController.get()

        RequestPermissionHelper.requestDisable(
        RequestPermissionHelper.requestDisable(
            context = activity,
            context = activity,
            appLabel = "App Label",
            appLabel = "App Label",
            onAllow = {},
            onAllow = {},
            onDeny = {},
            onDeny = {},
        )
        )!!.show()


        assertLatestMessageIs("App Label wants to turn off Bluetooth")
        assertLatestMessageIs("App Label wants to turn off Bluetooth")
    }
    }
@@ -190,12 +197,13 @@ class RequestPermissionHelperTest {
    @Test
    @Test
    fun requestDisable_withNoAppLabel_hasCorrectMessage() {
    fun requestDisable_withNoAppLabel_hasCorrectMessage() {
        val activity = activityController.get()
        val activity = activityController.get()

        RequestPermissionHelper.requestDisable(
        RequestPermissionHelper.requestDisable(
            context = activity,
            context = activity,
            appLabel = null,
            appLabel = null,
            onAllow = {},
            onAllow = {},
            onDeny = {},
            onDeny = {},
        )
        )!!.show()


        assertLatestMessageIs("An app wants to turn off Bluetooth")
        assertLatestMessageIs("An app wants to turn off Bluetooth")
    }
    }