diff --git a/res/xml/tether_prefs.xml b/res/xml/tether_prefs.xml index 2a841d71608..70a57d7c98a 100644 --- a/res/xml/tether_prefs.xml +++ b/res/xml/tether_prefs.xml @@ -17,6 +17,7 @@ + android:summary="@string/usb_tethering_subtext" + settings:keywords="@string/keywords_hotspot_tethering" /> + android:summary="@string/bluetooth_tethering_subtext" + settings:keywords="@string/keywords_hotspot_tethering" /> @@ -43,4 +44,4 @@ android:dialogTitle="@string/wifi_hotspot_ap_band_title" android:negativeButtonText="@string/cancel" android:positiveButtonText="@string/apply"/> - \ No newline at end of file + diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index 8cb8efc27df..848e1765699 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -34,27 +34,41 @@ import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.UserManager; -import androidx.preference.SwitchPreference; +import android.provider.SearchIndexableResource; +import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; +import androidx.preference.SwitchPreference; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.datausage.DataSaverBackend; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; import com.android.settings.wifi.tether.WifiTetherPreferenceController; import com.android.settingslib.TetherUtil; +import com.android.settingslib.search.SearchIndexable; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; /* * Displays preferences for Tethering. */ +@SearchIndexable public class TetherSettings extends RestrictedSettingsFragment implements DataSaverBackend.Listener { - private static final String USB_TETHER_SETTINGS = "usb_tether_settings"; - private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering"; - private static final String DATA_SAVER_FOOTER = "disabled_on_data_saver"; + @VisibleForTesting + static final String KEY_TETHER_PREFS_SCREEN = "tether_prefs_screen"; + @VisibleForTesting + static final String KEY_WIFI_TETHER = "wifi_tether"; + @VisibleForTesting + static final String KEY_USB_TETHER_SETTINGS = "usb_tether_settings"; + @VisibleForTesting + static final String KEY_ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering"; + private static final String KEY_DATA_SAVER_FOOTER = "disabled_on_data_saver"; private static final String TAG = "TetheringSettings"; @@ -110,7 +124,7 @@ public class TetherSettings extends RestrictedSettingsFragment mDataSaverBackend = new DataSaverBackend(getContext()); mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled(); - mDataSaverFooter = findPreference(DATA_SAVER_FOOTER); + mDataSaverFooter = findPreference(KEY_DATA_SAVER_FOOTER); setIfOnlyAvailableForAdmins(true); if (isUiRestricted()) { @@ -126,8 +140,8 @@ public class TetherSettings extends RestrictedSettingsFragment BluetoothProfile.PAN); } - mUsbTether = (SwitchPreference) findPreference(USB_TETHER_SETTINGS); - mBluetoothTether = (SwitchPreference) findPreference(ENABLE_BLUETOOTH_TETHERING); + mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS); + mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING); mDataSaverBackend.addListener(this); @@ -433,6 +447,42 @@ public class TetherSettings extends RestrictedSettingsFragment } }; + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.tether_prefs; + return Arrays.asList(sir); + } + + @Override + public List getNonIndexableKeys(Context context) { + final List keys = super.getNonIndexableKeys(context); + final ConnectivityManager cm = + context.getSystemService(ConnectivityManager.class); + + if (!TetherUtil.isTetherAvailable(context)) { + keys.add(KEY_TETHER_PREFS_SCREEN); + keys.add(KEY_WIFI_TETHER); + } + + final boolean usbAvailable = + cm.getTetherableUsbRegexs().length != 0; + if (!usbAvailable || Utils.isMonkeyRunning()) { + keys.add(KEY_USB_TETHER_SETTINGS); + } + + final boolean bluetoothAvailable = + cm.getTetherableBluetoothRegexs().length != 0; + if (!bluetoothAvailable) { + keys.add(KEY_ENABLE_BLUETOOTH_TETHERING); + } + return keys; + } + }; + private static final class OnStartTetheringCallback extends ConnectivityManager.OnStartTetheringCallback { final WeakReference mTetherSettings; diff --git a/src/com/android/settings/network/TetherPreferenceController.java b/src/com/android/settings/network/TetherPreferenceController.java index 636fe0cee07..3ab83cda0b5 100644 --- a/src/com/android/settings/network/TetherPreferenceController.java +++ b/src/com/android/settings/network/TetherPreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.network; import static android.os.UserManager.DISALLOW_CONFIG_TETHERING; import static com.android.settingslib.RestrictedLockUtils.checkIfRestrictionEnforced; -import static com.android.settingslib.RestrictedLockUtils.hasBaseUserRestriction; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; @@ -40,6 +39,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.TetherSettings; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.TetherUtil; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -112,11 +112,7 @@ public class TetherPreferenceController extends AbstractPreferenceController imp @Override public boolean isAvailable() { - final boolean isBlocked = - (!mConnectivityManager.isTetheringSupported() && !mAdminDisallowedTetherConfig) - || hasBaseUserRestriction(mContext, DISALLOW_CONFIG_TETHERING, - UserHandle.myUserId()); - return !isBlocked; + return TetherUtil.isTetherAvailable(mContext); } @Override diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 7c50f0c2cdb..5e202d63471 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -27,28 +27,42 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.UserManager; -import androidx.annotation.VisibleForTesting; +import android.provider.SearchIndexableResource; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.dashboard.RestrictedDashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBarController; +import com.android.settingslib.TetherUtil; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.search.SearchIndexable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; +@SearchIndexable public class WifiTetherSettings extends RestrictedDashboardFragment implements WifiTetherBasePreferenceController.OnTetherConfigUpdateListener { private static final String TAG = "WifiTetherSettings"; private static final IntentFilter TETHER_STATE_CHANGE_FILTER; - private static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; + private static final String KEY_WIFI_TETHER_SCREEN = "wifi_tether_settings_screen"; + @VisibleForTesting + static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; + @VisibleForTesting + static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password"; + @VisibleForTesting + static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; + @VisibleForTesting + static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band"; private WifiTetherSwitchBarController mSwitchBarController; private WifiTetherSSIDPreferenceController mSSIDPreferenceController; @@ -182,6 +196,33 @@ public class WifiTetherSettings extends RestrictedDashboardFragment .updateDisplay(); } + public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.wifi_tether_settings; + return Arrays.asList(sir); + } + + @Override + public List getNonIndexableKeys(Context context) { + final List keys = super.getNonIndexableKeys(context); + + if (!TetherUtil.isTetherAvailable(context)) { + keys.add(KEY_WIFI_TETHER_NETWORK_NAME); + keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); + keys.add(KEY_WIFI_TETHER_AUTO_OFF); + keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND); + } + + // Remove duplicate + keys.add(KEY_WIFI_TETHER_SCREEN); + return keys; + } + }; + @VisibleForTesting class TetherChangeReceiver extends BroadcastReceiver { @Override diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider index 8bd4f823840..31e3a54ee26 100644 --- a/tests/robotests/assets/grandfather_not_implementing_index_provider +++ b/tests/robotests/assets/grandfather_not_implementing_index_provider @@ -21,7 +21,6 @@ com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMi com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment com.android.settings.inputmethod.KeyboardLayoutPickerFragment -com.android.settings.wifi.tether.WifiTetherSettings com.android.settings.wifi.SavedAccessPointsWifiSettings com.android.settings.notification.ZenModeEventRuleSettings com.android.settings.notification.ZenModeScheduleRuleSettings diff --git a/tests/robotests/src/com/android/settings/TetherSettingsTest.java b/tests/robotests/src/com/android/settings/TetherSettingsTest.java new file mode 100644 index 00000000000..1ccd958cfb3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/TetherSettingsTest.java @@ -0,0 +1,150 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; +import android.net.ConnectivityManager; + +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; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class TetherSettingsTest { + + private Context mContext; + + @Mock + private ConnectivityManager mConnectivityManager; + @Mock + private UserManager mUserManager; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + + MockitoAnnotations.initMocks(this); + doReturn(mConnectivityManager) + .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE); + doReturn(mUserManager) + .when(mContext).getSystemService(Context.USER_SERVICE); + + setupIsTetherAvailable(true); + + when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[]{}); + when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[]{}); + } + + @Test + public void testTetherNonIndexableKeys_tetherAvailable_keysNotReturned() { + // To let TetherUtil.isTetherAvailable return true, select one of the combinations + setupIsTetherAvailable(true); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(TetherSettings.KEY_TETHER_PREFS_SCREEN); + assertThat(niks).doesNotContain(TetherSettings.KEY_WIFI_TETHER); + } + + @Test + public void testTetherNonIndexableKeys_tetherNotAvailable_keysReturned() { + // To let TetherUtil.isTetherAvailable return false, select one of the combinations + setupIsTetherAvailable(false); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).contains(TetherSettings.KEY_TETHER_PREFS_SCREEN); + assertThat(niks).contains(TetherSettings.KEY_WIFI_TETHER); + } + + @Test + public void testTetherNonIndexableKeys_usbNotAvailable_usbKeyReturned() { + when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[]{}); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).contains(TetherSettings.KEY_USB_TETHER_SETTINGS); + } + + @Test + public void testTetherNonIndexableKeys_usbAvailable_usbKeyNotReturned() { + // We can ignore the condition of Utils.isMonkeyRunning() + // In normal case, monkey and robotest should not execute at the same time + when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[]{"dummyRegex"}); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(TetherSettings.KEY_USB_TETHER_SETTINGS); + } + + @Test + public void testTetherNonIndexableKeys_bluetoothNotAvailable_bluetoothKeyReturned() { + when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[]{}); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).contains(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING); + } + + @Test + public void testTetherNonIndexableKeys_bluetoothAvailable_bluetoothKeyNotReturned() { + when(mConnectivityManager.getTetherableBluetoothRegexs()) + .thenReturn(new String[]{"dummyRegex"}); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING); + } + + private void setupIsTetherAvailable(boolean returnValue) { + when(mConnectivityManager.isTetheringSupported()).thenReturn(true); + + // For RestrictedLockUtils.checkIfRestrictionEnforced + final int userId = UserHandle.myUserId(); + List enforcingUsers = new ArrayList<>(); + when(mUserManager.getUserRestrictionSources( + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId))) + .thenReturn(enforcingUsers); + + // For RestrictedLockUtils.hasBaseUserRestriction + when(mUserManager.hasBaseUserRestriction( + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId))) + .thenReturn(!returnValue); + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java new file mode 100644 index 00000000000..58bb1989d98 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.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.wifi.tether; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; +import android.net.ConnectivityManager; + +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; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class WifiTetherSettingsTest { + + private Context mContext; + + @Mock + private ConnectivityManager mConnectivityManager; + @Mock + private UserManager mUserManager; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + + MockitoAnnotations.initMocks(this); + doReturn(mConnectivityManager) + .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE); + doReturn(mUserManager) + .when(mContext).getSystemService(Context.USER_SERVICE); + } + + @Test + public void testWifiTetherNonIndexableKeys_tetherAvailable_keysNotReturned() { + // To let TetherUtil.isTetherAvailable return true, select one of the combinations + setupIsTetherAvailable(true); + + final List niks = + WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + } + + @Test + public void testWifiTetherNonIndexableKeys_tetherNotAvailable_keysReturned() { + // To let TetherUtil.isTetherAvailable return false, select one of the combinations + setupIsTetherAvailable(false); + + final List niks = + WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + } + + private void setupIsTetherAvailable(boolean returnValue) { + when(mConnectivityManager.isTetheringSupported()).thenReturn(true); + + // For RestrictedLockUtils.checkIfRestrictionEnforced + final int userId = UserHandle.myUserId(); + List enforcingUsers = new ArrayList<>(); + when(mUserManager.getUserRestrictionSources( + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId))) + .thenReturn(enforcingUsers); + + // For RestrictedLockUtils.hasBaseUserRestriction + when(mUserManager.hasBaseUserRestriction( + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId))) + .thenReturn(!returnValue); + } +}