Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/java/android/ext/settings/ExtSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ public class ExtSettings {
public static final BoolSetting DISALLOW_DELAYED_LOCKING_ON_USER_STOP = new BoolSetting(
Setting.Scope.PER_USER, Settings.Secure.DISALLOW_DELAYED_LOCKING_ON_USER_STOP, false);

public static final BoolSetting REQUIRE_AUTHENTICATION_TO_DISABLE_AIRPLANE_MODE =
new BoolSetting(Setting.Scope.GLOBAL,
Settings.Global.REQUIRE_AUTHENTICATION_TO_DISABLE_AIRPLANE_MODE, false);

private ExtSettings() {}

public static Function<Context, Boolean> defaultBool(@BoolRes int res) {
Expand Down
5 changes: 5 additions & 0 deletions core/java/android/provider/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -14509,6 +14509,11 @@ public static final class Global extends NameValueTable {
@Protected(read = KnownSystemPackage.SYSTEM_UI, readWrite = KnownSystemPackage.SETTINGS)
public static final String BATTERY_CHARGE_LIMIT = "battery_charge_limit";

/** @hide */
@Protected(read = KnownSystemPackage.SYSTEM_UI, readWrite = KnownSystemPackage.SETTINGS)
public static final String REQUIRE_AUTHENTICATION_TO_DISABLE_AIRPLANE_MODE =
"require_authentication_to_disable_airplane_mode";

/** @hide */
@Protected(restrictReads = false, readWrite = {KnownSystemPackage.SETTINGS,
KnownSystemPackage.SETUP_WIZARD})
Expand Down
3 changes: 3 additions & 0 deletions core/proto/android/providers/settings/global.proto
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ message GlobalSettingsProto {
// are included in the comma-separated list.
optional SettingProto radios = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto toggleable_radios = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto require_authentication_to_disable_airplane_mode = 4 [
(android.privacy).dest = DEST_AUTOMATIC
];
}
optional AirplaneMode airplane_mode = 5;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ private static void dumpProtoGlobalSettingsLocked(
dumpSetting(s, p,
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
GlobalSettingsProto.AirplaneMode.TOGGLEABLE_RADIOS);
dumpSetting(s, p,
Settings.Global.REQUIRE_AUTHENTICATION_TO_DISABLE_AIRPLANE_MODE,
GlobalSettingsProto.AirplaneMode.REQUIRE_AUTHENTICATION_TO_DISABLE_AIRPLANE_MODE);
p.end(airplaneModeToken);

dumpSetting(s, p,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
package com.android.systemui.qs.tiles

import android.net.ConnectivityManager
import android.os.CancellationSignal
import android.os.Handler
import android.provider.Settings.Global
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
Expand All @@ -28,6 +30,7 @@ import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeAuthenticationInteractor
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
Expand Down Expand Up @@ -63,6 +66,7 @@ class AirplaneModeTileTest : SysuiTestCase() {
@Mock private lateinit var mLazyConnectivityManager: Lazy<ConnectivityManager>
@Mock private lateinit var mConnectivityManager: ConnectivityManager
@Mock private lateinit var mGlobalSettings: GlobalSettings
@Mock private lateinit var mAuthenticationInteractor: AirplaneModeAuthenticationInteractor
@Mock private lateinit var mUserTracker: UserTracker
@Mock private lateinit var mUiEventLogger: QsEventLogger
private lateinit var mTestableLooper: TestableLooper
Expand Down Expand Up @@ -92,6 +96,7 @@ class AirplaneModeTileTest : SysuiTestCase() {
mLazyConnectivityManager,
mGlobalSettings,
mUserTracker,
mAuthenticationInteractor,
)
}

Expand Down Expand Up @@ -129,6 +134,67 @@ class AirplaneModeTileTest : SysuiTestCase() {
verify(mConnectivityManager, times(0)).setAirplaneMode(any())
}

@Test
fun handleClick_toDisable_runsAfterAuthentication() {
mTile.state.value = true
Mockito.`when`(mGlobalSettings.getInt(Global.AIRPLANE_MODE_ON, 0)).thenReturn(1)
Mockito.doAnswer { invocation ->
invocation.getArgument<Runnable>(0).run()
null
}
.`when`(mAuthenticationInteractor)
.runAfterAuthentication(any())

mTile.handleClick(null)
mTestableLooper.processAllMessages()

verify(mAuthenticationInteractor).runAfterAuthentication(any())
verify(mConnectivityManager).setAirplaneMode(false)
}

@Test
fun handleClick_authenticatedAfterAirplaneModeAlreadyDisabled_doesNotWriteAgain() {
mTile.state.value = true
Mockito.`when`(mGlobalSettings.getInt(Global.AIRPLANE_MODE_ON, 0)).thenReturn(0)
Mockito.doAnswer { invocation ->
invocation.getArgument<Runnable>(0).run()
null
}
.`when`(mAuthenticationInteractor)
.runAfterAuthentication(any())

mTile.handleClick(null)
mTestableLooper.processAllMessages()

verify(mConnectivityManager, times(0)).setAirplaneMode(false)
}

@Test
fun handleClick_toEnable_doesNotAuthenticate() {
mTile.state.value = false

mTile.handleClick(null)
mTestableLooper.processAllMessages()

verify(mAuthenticationInteractor, times(0)).runAfterAuthentication(any())
verify(mConnectivityManager).setAirplaneMode(true)
}

@Test
fun destroy_withAuthenticationPending_cancelsPrompt() {
val signal = Mockito.mock(CancellationSignal::class.java)
mTile.state.value = true
Mockito.`when`(mGlobalSettings.getInt(Global.AIRPLANE_MODE_ON, 0)).thenReturn(1)
Mockito.`when`(mAuthenticationInteractor.runAfterAuthentication(any())).thenReturn(signal)

mTile.handleClick(null)
mTestableLooper.processAllMessages()
mTile.destroy()
mTestableLooper.processAllMessages()

verify(mAuthenticationInteractor).cancelAuthentication(signal)
}

private fun createExpectedIcon(resId: Int): QSTile.Icon {
return DrawableIconWithRes(mContext.getDrawable(resId), resId)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHa
import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.longClick
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeAuthenticationInteractor
import com.android.systemui.statusbar.pipeline.airplane.data.repository.airplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
Expand All @@ -34,6 +35,10 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
Expand All @@ -42,9 +47,14 @@ class AirplaneModeTileUserActionInteractorTest : SysuiTestCase() {

private val airplaneModeRepository = kosmos.airplaneModeRepository
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val authenticationInteractor = mock<AirplaneModeAuthenticationInteractor>()

private val underTest =
AirplaneModeTileUserActionInteractor(kosmos.airplaneModeInteractor, inputHandler)
AirplaneModeTileUserActionInteractor(
kosmos.airplaneModeInteractor,
authenticationInteractor,
inputHandler,
)

@Test
fun handleClickInEcmMode() = runTest {
Expand Down Expand Up @@ -73,6 +83,36 @@ class AirplaneModeTileUserActionInteractorTest : SysuiTestCase() {
assertThat(airplaneModeRepository.isAirplaneMode.value).isTrue()
}

@Test
fun handleClickToDisable_authenticationSucceeds_disablesAirplaneMode() = runTest {
airplaneModeRepository.setIsAirplaneMode(true)
whenever(authenticationInteractor.authenticateIfRequired()).thenReturn(true)

underTest.handleInput(click(AirplaneModeTileModel(true)))

assertThat(airplaneModeRepository.isAirplaneMode.value).isFalse()
}

@Test
fun handleClickToDisable_authenticationCancelled_keepsAirplaneModeEnabled() = runTest {
airplaneModeRepository.setIsAirplaneMode(true)
whenever(authenticationInteractor.authenticateIfRequired()).thenReturn(false)

underTest.handleInput(click(AirplaneModeTileModel(true)))

assertThat(airplaneModeRepository.isAirplaneMode.value).isTrue()
}

@Test
fun handleClickToEnable_doesNotAuthenticate() = runTest {
airplaneModeRepository.setIsAirplaneMode(false)

underTest.handleInput(click(AirplaneModeTileModel(false)))

verify(authenticationInteractor, never()).authenticateIfRequired()
assertThat(airplaneModeRepository.isAirplaneMode.value).isTrue()
}

@Test
fun handleLongClick() = runTest {
underTest.handleInput(longClick(AirplaneModeTileModel(false)))
Expand Down
Loading