diff --git a/res/xml/network_and_internet.xml b/res/xml/network_and_internet.xml
index 76537466e85..257363a16f4 100644
--- a/res/xml/network_and_internet.xml
+++ b/res/xml/network_and_internet.xml
@@ -79,6 +79,18 @@
settings:userRestriction="no_config_tethering"
settings:useAdminDisabledSummary="true" />
+
+
mBluetoothPan;
+ private final BluetoothAdapter mBluetoothAdapter;
+ @VisibleForTesting
+ final BluetoothProfile.ServiceListener mBtProfileServiceListener =
+ new BluetoothProfile.ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ mBluetoothPan.set((BluetoothPan) proxy);
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ mBluetoothPan.set(null);
+ }
+ };
+
+ private MasterSwitchPreference mPreference;
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ AllInOneTetherPreferenceController() {
+ super(null /*context*/, "test");
+ mAdminDisallowedTetherConfig = false;
+ mBluetoothPan = new AtomicReference<>();
+ mBluetoothAdapter = null;
+ }
+
+ public AllInOneTetherPreferenceController(Context context, String key) {
+ super(context, key);
+ mBluetoothPan = new AtomicReference<>();
+ mAdminDisallowedTetherConfig = checkIfRestrictionEnforced(
+ context, DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()) != null;
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(mPreferenceKey);
+ if (mPreference != null && !mAdminDisallowedTetherConfig) {
+ // Grey out if provisioning is not available.
+ mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!TetherUtil.isTetherAvailable(mContext)
+ || !FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)) {
+ return CONDITIONALLY_UNAVAILABLE;
+ } else {
+ return AVAILABLE;
+ }
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ if (mPreference != null && mPreference.isChecked()) {
+ // TODO(b/149256198) update summary accordingly.
+ return "Tethering";
+ }
+
+ return "Not sharing internet with other devices";
+ }
+
+ @OnLifecycleEvent(Event.ON_CREATE)
+ public void onCreate() {
+ if (mBluetoothAdapter != null
+ && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
+ mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener,
+ BluetoothProfile.PAN);
+ }
+ }
+
+ @OnLifecycleEvent(Event.ON_DESTROY)
+ public void onDestroy() {
+ final BluetoothProfile profile = mBluetoothPan.getAndSet(null);
+ if (profile != null && mBluetoothAdapter != null) {
+ mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile);
+ }
+ }
+
+ void init(Lifecycle lifecycle) {
+ lifecycle.addObserver(this);
+ }
+
+ void initEnabler(Lifecycle lifecycle) {
+ if (mPreference != null) {
+ TetherEnabler tetherEnabler = new TetherEnabler(
+ mContext, new MasterSwitchController(mPreference), mBluetoothPan);
+ if (lifecycle != null) {
+ lifecycle.addObserver(tetherEnabler);
+ }
+ } else {
+ Log.e(TAG, "TetherEnabler is not initialized");
+ }
+ }
+}
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index 71d70660ccf..b1685fd3fcb 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -20,6 +20,7 @@ import static com.android.settings.network.MobilePlanPreferenceController.MANAGE
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AlertDialog;
@@ -65,6 +66,13 @@ public class NetworkDashboardFragment extends DashboardFragment implements
use(MultiNetworkHeaderController.class).init(getSettingsLifecycle());
use(AirplaneModePreferenceController.class).setFragment(this);
+ use(AllInOneTetherPreferenceController.class).init(getSettingsLifecycle());
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ super.onCreatePreferences(savedInstanceState, rootKey);
+ use(AllInOneTetherPreferenceController.class).initEnabler(getSettingsLifecycle());
}
@Override
diff --git a/src/com/android/settings/network/TetherPreferenceController.java b/src/com/android/settings/network/TetherPreferenceController.java
index 8fc05aabb8e..d18d897393e 100644
--- a/src/com/android/settings/network/TetherPreferenceController.java
+++ b/src/com/android/settings/network/TetherPreferenceController.java
@@ -39,7 +39,6 @@ import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settings.AllInOneTetherSettings;
import com.android.settings.R;
import com.android.settings.TetherSettings;
import com.android.settings.core.FeatureFlags;
@@ -112,16 +111,13 @@ public class TetherPreferenceController extends AbstractPreferenceController imp
// Grey out if provisioning is not available.
mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
-
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)) {
- mPreference.setFragment(AllInOneTetherSettings.class.getName());
- }
}
}
@Override
public boolean isAvailable() {
- return TetherUtil.isTetherAvailable(mContext);
+ return TetherUtil.isTetherAvailable(mContext)
+ && !FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE);
}
@Override
diff --git a/tests/robotests/src/com/android/settings/network/AllInOneTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/AllInOneTetherPreferenceControllerTest.java
new file mode 100644
index 00000000000..4fbc4736adc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/AllInOneTetherPreferenceControllerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settings.widget.MasterSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(RobolectricTestRunner.class)
+public class AllInOneTetherPreferenceControllerTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private BluetoothAdapter mBluetoothAdapter;
+ @Mock
+ private MasterSwitchPreference mPreference;
+
+ private AllInOneTetherPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = spy(AllInOneTetherPreferenceController.class);
+ ReflectionHelpers.setField(mController, "mContext", mContext);
+ ReflectionHelpers.setField(mController, "mBluetoothAdapter", mBluetoothAdapter);
+ ReflectionHelpers.setField(mController, "mPreference", mPreference);
+ }
+
+ @Test
+ public void lifeCycle_onCreate_shouldInitBluetoothPan() {
+ when(mBluetoothAdapter.getState()).thenReturn(BluetoothAdapter.STATE_ON);
+ mController.onCreate();
+
+ verify(mBluetoothAdapter).getState();
+ verify(mBluetoothAdapter).getProfileProxy(mContext, mController.mBtProfileServiceListener,
+ BluetoothProfile.PAN);
+ }
+
+ @Test
+ public void lifeCycle_onCreate_shouldNotInitBluetoothPanWhenBluetoothOff() {
+ when(mBluetoothAdapter.getState()).thenReturn(BluetoothAdapter.STATE_OFF);
+ mController.onCreate();
+
+ verify(mBluetoothAdapter).getState();
+ verifyNoMoreInteractions(mBluetoothAdapter);
+ }
+
+ @Test
+ public void goThroughLifecycle_shouldDestroyBluetoothProfile() {
+ final BluetoothPan pan = mock(BluetoothPan.class);
+ final AtomicReference panRef =
+ ReflectionHelpers.getField(mController, "mBluetoothPan");
+ panRef.set(pan);
+
+ mController.onDestroy();
+
+ verify(mBluetoothAdapter).closeProfileProxy(BluetoothProfile.PAN, pan);
+ }
+}