From 6776da1cadaf73d19fc859e7e19574313ee8cc2c Mon Sep 17 00:00:00 2001 From: Doris Ling Date: Thu, 31 May 2018 14:56:28 -0700 Subject: [PATCH] Do not turn on NFC automatically when airplane mode is off. - besides checking the value for AIRPLANE_MODE_TOGGLEABLE_RADIOS, also check the value for AIRPLANE_MODE_RADIOS to determine if we should turn off NFC when airplane mode is on. - when user turns off airplane mode, only re-enable the NFC preference and do not enable NFC by default. - remove listening to airplane mode directly from the android beam preference controller, as it is already listening to Nfc state, which handles the beam state already. Change-Id: Id41fef15fb2de873729d20d7f53b1a88b164cf95 Fixes: 80217047 Test: make RunSettingsRoboTests --- .../nfc/AndroidBeamPreferenceController.java | 16 --- .../android/settings/nfc/BaseNfcEnabler.java | 2 +- .../settings/nfc/NfcAirplaneModeObserver.java | 11 +- src/com/android/settings/nfc/NfcEnabler.java | 17 ++- .../settings/nfc/NfcPreferenceController.java | 19 +++- .../nfc/NfcAirplaneModeObserverTest.java | 54 +++++++-- .../android/settings/nfc/NfcEnablerTest.java | 106 ++++++++++++++++++ .../nfc/NfcPreferenceControllerTest.java | 60 ++++++++-- 8 files changed, 235 insertions(+), 50 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java diff --git a/src/com/android/settings/nfc/AndroidBeamPreferenceController.java b/src/com/android/settings/nfc/AndroidBeamPreferenceController.java index 12ab1b756dc..f99c47c188a 100644 --- a/src/com/android/settings/nfc/AndroidBeamPreferenceController.java +++ b/src/com/android/settings/nfc/AndroidBeamPreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.nfc; import android.content.Context; import android.nfc.NfcAdapter; -import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.core.BasePreferenceController; @@ -26,15 +25,12 @@ import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; -import java.util.List; - public class AndroidBeamPreferenceController extends BasePreferenceController implements LifecycleObserver, OnResume, OnPause { public static final String KEY_ANDROID_BEAM_SETTINGS = "android_beam_settings"; private final NfcAdapter mNfcAdapter; private AndroidBeamEnabler mAndroidBeamEnabler; - private NfcAirplaneModeObserver mAirplaneModeObserver; public AndroidBeamPreferenceController(Context context, String key) { super(context, key); @@ -52,12 +48,6 @@ public class AndroidBeamPreferenceController extends BasePreferenceController final RestrictedPreference restrictedPreference = (RestrictedPreference) screen.findPreference(getPreferenceKey()); mAndroidBeamEnabler = new AndroidBeamEnabler(mContext, restrictedPreference); - - // Manually set dependencies for NFC when not toggleable. - if (!NfcPreferenceController.isToggleableInAirplaneMode(mContext)) { - mAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, mNfcAdapter, - (Preference) restrictedPreference); - } } @Override @@ -70,9 +60,6 @@ public class AndroidBeamPreferenceController extends BasePreferenceController @Override public void onResume() { - if (mAirplaneModeObserver != null) { - mAirplaneModeObserver.register(); - } if (mAndroidBeamEnabler != null) { mAndroidBeamEnabler.resume(); } @@ -80,9 +67,6 @@ public class AndroidBeamPreferenceController extends BasePreferenceController @Override public void onPause() { - if (mAirplaneModeObserver != null) { - mAirplaneModeObserver.unregister(); - } if (mAndroidBeamEnabler != null) { mAndroidBeamEnabler.pause(); } diff --git a/src/com/android/settings/nfc/BaseNfcEnabler.java b/src/com/android/settings/nfc/BaseNfcEnabler.java index 88bafb98fb9..64c6d15c7ad 100644 --- a/src/com/android/settings/nfc/BaseNfcEnabler.java +++ b/src/com/android/settings/nfc/BaseNfcEnabler.java @@ -28,7 +28,7 @@ import androidx.preference.Preference; * preference. It will receive intent and update state to ensure preference show correct state. */ public abstract class BaseNfcEnabler { - private final Context mContext; + protected final Context mContext; protected final NfcAdapter mNfcAdapter; private final IntentFilter mIntentFilter; diff --git a/src/com/android/settings/nfc/NfcAirplaneModeObserver.java b/src/com/android/settings/nfc/NfcAirplaneModeObserver.java index d0ce045296f..65ac655bd8e 100644 --- a/src/com/android/settings/nfc/NfcAirplaneModeObserver.java +++ b/src/com/android/settings/nfc/NfcAirplaneModeObserver.java @@ -70,12 +70,13 @@ public class NfcAirplaneModeObserver extends ContentObserver { } mAirplaneMode = airplaneMode; - boolean toggleable = mAirplaneMode != 1; - if (toggleable) { - mNfcAdapter.enable(); - } else { + if (mAirplaneMode == 1) { + // airplane mode is on, need to turn off NFC, and check if user can toggle it mNfcAdapter.disable(); + mPreference.setEnabled(NfcPreferenceController.isToggleableInAirplaneMode(mContext)); + } else { + // airplane mode is off, no restriction + mPreference.setEnabled(true); } - mPreference.setEnabled(toggleable); } } diff --git a/src/com/android/settings/nfc/NfcEnabler.java b/src/com/android/settings/nfc/NfcEnabler.java index 507a0532378..777e7d114bd 100644 --- a/src/com/android/settings/nfc/NfcEnabler.java +++ b/src/com/android/settings/nfc/NfcEnabler.java @@ -18,8 +18,10 @@ package com.android.settings.nfc; import android.content.Context; import android.nfc.NfcAdapter; -import androidx.preference.SwitchPreference; +import android.provider.Settings; +import androidx.annotation.VisibleForTesting; +import androidx.preference.SwitchPreference; /** * NfcEnabler is a helper to manage the Nfc on/off checkbox preference. It turns on/off Nfc @@ -38,7 +40,7 @@ public class NfcEnabler extends BaseNfcEnabler { switch (newState) { case NfcAdapter.STATE_OFF: mPreference.setChecked(false); - mPreference.setEnabled(true); + mPreference.setEnabled(isToggleable()); break; case NfcAdapter.STATE_ON: mPreference.setChecked(true); @@ -54,4 +56,15 @@ public class NfcEnabler extends BaseNfcEnabler { break; } } + + @VisibleForTesting + boolean isToggleable() { + if (NfcPreferenceController.isToggleableInAirplaneMode(mContext) + || !NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)) { + return true; + } + final int airplaneMode = Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); + return airplaneMode != 1; + } } diff --git a/src/com/android/settings/nfc/NfcPreferenceController.java b/src/com/android/settings/nfc/NfcPreferenceController.java index e1fa1b3e6ce..0f68a9ce47c 100644 --- a/src/com/android/settings/nfc/NfcPreferenceController.java +++ b/src/com/android/settings/nfc/NfcPreferenceController.java @@ -20,7 +20,7 @@ import android.content.IntentFilter; import android.nfc.NfcAdapter; import android.provider.Settings; -import androidx.preference.Preference; +import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; @@ -37,7 +37,8 @@ public class NfcPreferenceController extends TogglePreferenceController public static final String KEY_TOGGLE_NFC = "toggle_nfc"; private final NfcAdapter mNfcAdapter; private NfcEnabler mNfcEnabler; - private NfcAirplaneModeObserver mAirplaneModeObserver; + @VisibleForTesting + NfcAirplaneModeObserver mAirplaneModeObserver; public NfcPreferenceController(Context context, String key) { super(context, key); @@ -57,10 +58,10 @@ public class NfcPreferenceController extends TogglePreferenceController mNfcEnabler = new NfcEnabler(mContext, switchPreference); - // Manually set dependencies for NFC when not toggleable. - if (!isToggleableInAirplaneMode(mContext)) { - mAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, - mNfcAdapter, (Preference) switchPreference); + // Listen to airplane mode updates if NFC should be turned off when airplane mode is on + if (shouldTurnOffNFCInAirplaneMode(mContext) || isToggleableInAirplaneMode(mContext)) { + mAirplaneModeObserver = + new NfcAirplaneModeObserver(mContext, mNfcAdapter, switchPreference); } } @@ -125,6 +126,12 @@ public class NfcPreferenceController extends TogglePreferenceController } } + public static boolean shouldTurnOffNFCInAirplaneMode(Context context) { + final String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS); + return airplaneModeRadios != null && airplaneModeRadios.contains(Settings.Global.RADIO_NFC); + } + public static boolean isToggleableInAirplaneMode(Context context) { final String toggleable = Settings.Global.getString(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); diff --git a/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java b/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java index 5efa94aaa98..c5e38a6fe44 100644 --- a/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java +++ b/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java @@ -18,11 +18,12 @@ package com.android.settings.nfc; import static com.google.common.truth.Truth.assertThat; +import android.content.ContentResolver; import android.content.Context; import android.nfc.NfcAdapter; import android.provider.Settings; -import androidx.preference.Preference; +import android.provider.Settings.Global; import androidx.preference.SwitchPreference; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -52,8 +53,8 @@ public class NfcAirplaneModeObserverTest { mNfcPreference = new SwitchPreference(RuntimeEnvironment.application); - mNfcAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, mNfcAdapter, - (Preference) mNfcPreference); + mNfcAirplaneModeObserver = + new NfcAirplaneModeObserver(mContext, mNfcAdapter, mNfcPreference); } @Test @@ -67,20 +68,51 @@ public class NfcAirplaneModeObserverTest { NfcAirplaneModeObserver.AIRPLANE_MODE_URI); assertThat(mNfcAdapter.isEnabled()).isFalse(); + } + + @Test + public void NfcAirplaneModeObserver_airplaneModeOnNfcToggleable_shouldEnablePreference() { + ReflectionHelpers.setField(mNfcAirplaneModeObserver, "mAirplaneMode", 0); + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); + + mNfcAirplaneModeObserver.onChange(false, NfcAirplaneModeObserver.AIRPLANE_MODE_URI); + + assertThat(mNfcPreference.isEnabled()).isTrue(); + } + + @Test + public void NfcAirplaneModeObserver_airplaneModeOnNfcNotToggleable_shouldDisablePreference() { + ReflectionHelpers.setField(mNfcAirplaneModeObserver, "mAirplaneMode", 0); + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Global.RADIO_WIFI); + + mNfcAirplaneModeObserver.onChange(false, NfcAirplaneModeObserver.AIRPLANE_MODE_URI); + assertThat(mNfcPreference.isEnabled()).isFalse(); } @Test - public void NfcAirplaneModeObserver_airplaneOff_shouldEnableNfc() { - ReflectionHelpers.setField(mNfcAirplaneModeObserver, - "mAirplaneMode", 1); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0); + public void NfcAirplaneModeObserver_airplaneModeOff_shouldEnablePreference() { + ReflectionHelpers.setField(mNfcAirplaneModeObserver, "mAirplaneMode", 1); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); - mNfcAirplaneModeObserver.onChange(false, - NfcAirplaneModeObserver.AIRPLANE_MODE_URI); + mNfcAirplaneModeObserver.onChange(false, NfcAirplaneModeObserver.AIRPLANE_MODE_URI); - assertThat(mNfcAdapter.isEnabled()).isTrue(); assertThat(mNfcPreference.isEnabled()).isTrue(); } + + @Test + public void NfcAirplaneModeObserver_airplaneModeOff_shouldNotEnableNfcAutomatically() { + ReflectionHelpers.setField(mNfcAirplaneModeObserver, "mAirplaneMode", 1); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); + + mNfcAirplaneModeObserver.onChange(false, NfcAirplaneModeObserver.AIRPLANE_MODE_URI); + + assertThat(mNfcAdapter.isEnabled()).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java new file mode 100644 index 00000000000..a10c27a874b --- /dev/null +++ b/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java @@ -0,0 +1,106 @@ +/* + * 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.settings.nfc; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.ContentResolver; +import android.content.Context; +import android.nfc.NfcAdapter; +import android.provider.Settings; +import androidx.preference.SwitchPreference; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class NfcEnablerTest { + + @Mock + private SwitchPreference mNfcPreference; + + private Context mContext; + private NfcEnabler mNfcEnabler; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mNfcEnabler = spy(new NfcEnabler(mContext, mNfcPreference)); + } + + @Test + public void isToggleable_AirplaneModeOff_shouldReturnTrue() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); + + assertThat(mNfcEnabler.isToggleable()).isTrue(); + } + + @Test + public void isToggleable_AirplaneModeOnNfcNotInAirplaneModeRadio_shouldReturnTrue() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, Settings.Global.AIRPLANE_MODE_RADIOS, ""); + + assertThat(mNfcEnabler.isToggleable()).isTrue(); + } + + @Test + public void isToggleable_AirplaneModeOnNfcToggleable_shouldReturnTrue() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); + + assertThat(mNfcEnabler.isToggleable()).isTrue(); + } + + @Test + public void isToggleable_AirplaneModeOnNfcNotToggleable_shouldReturnFalse() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, ""); + + assertThat(mNfcEnabler.isToggleable()).isFalse(); + } + + @Test + public void handleNfcStateChanged_stateOff_shouldCheckIfPreferenceEnableState() { + mNfcEnabler.handleNfcStateChanged(NfcAdapter.STATE_OFF); + + verify(mNfcEnabler).isToggleable(); + } +} diff --git a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java index eaf6425f43f..758f72c0998 100644 --- a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java @@ -75,15 +75,6 @@ public class NfcPreferenceControllerTest { mNfcPreference = new SwitchPreference(RuntimeEnvironment.application); when(mScreen.findPreference(mNfcController.getPreferenceKey())).thenReturn(mNfcPreference); - - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, - Settings.Global.RADIO_NFC); - - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, - 0); - mNfcController.displayPreference(mScreen); } @Test @@ -102,6 +93,7 @@ public class NfcPreferenceControllerTest { @Test public void isNfcEnable_nfcStateNotTurning_shouldReturnTrue() { + mNfcController.displayPreference(mScreen); when(mNfcAdapter.getAdapterState()).thenReturn(NfcAdapter.STATE_ON); mNfcController.onResume(); assertThat(mNfcPreference.isEnabled()).isTrue(); @@ -113,6 +105,7 @@ public class NfcPreferenceControllerTest { @Test public void isNfcEnable_nfcStateTurning_shouldReturnFalse() { + mNfcController.displayPreference(mScreen); when(mNfcAdapter.getAdapterState()).thenReturn(NfcAdapter.STATE_TURNING_ON); mNfcController.onResume(); assertThat(mNfcPreference.isEnabled()).isFalse(); @@ -124,6 +117,7 @@ public class NfcPreferenceControllerTest { @Test public void isNfcChecked_nfcStateOn_shouldReturnTrue() { + mNfcController.displayPreference(mScreen); when(mNfcAdapter.getAdapterState()).thenReturn(NfcAdapter.STATE_ON); mNfcController.onResume(); assertThat(mNfcPreference.isChecked()).isTrue(); @@ -205,4 +199,52 @@ public class NfcPreferenceControllerTest { assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isFalse(); } + + @Test + public void shouldTurnOffNFCInAirplaneMode_airplaneModeRadiosContainsNfc_shouldReturnTrue() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + + assertThat(NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)).isTrue(); + } + + @Test + public void shouldTurnOffNFCInAirplaneMode_airplaneModeRadiosWithoutNfc_shouldReturnFalse() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS, ""); + + assertThat(NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)).isFalse(); + } + + @Test + public void displayPreference_airplaneModeRadiosContainsNfc_shouldCreateAirplaneModeObserver() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + + mNfcController.displayPreference(mScreen); + + assertThat(mNfcController.mAirplaneModeObserver).isNotNull(); + } + + @Test + public void displayPreference_nfcToggleableInAirplaneMode_shouldCreateAirplaneModeObserver() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); + + mNfcController.displayPreference(mScreen); + + assertThat(mNfcController.mAirplaneModeObserver).isNotNull(); + } + + @Test + public void displayPreference_nfcNotAffectByAirplaneMode_shouldNotCreateAirplaneModeObserver() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, ""); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS, ""); + + mNfcController.displayPreference(mScreen); + + assertThat(mNfcController.mAirplaneModeObserver).isNull(); + } }