Fix ServiceConnectionLeak in network fragment

Change-Id: I43efe9ae31fc2f58361abdb034b0205a3f3e2b71
Fix: 35957226
Test: make RunSettingsRoboTests
diff --git a/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java b/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
index 0a1a628..abe1427 100644
--- a/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
+++ b/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java
@@ -88,4 +88,11 @@
         super.onPause();
     }
 
+    @CallSuper
+    @Override
+    public void onDestroy() {
+        mLifecycle.onDestroy();
+        super.onDestroy();
+    }
+
 }
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index f52230b..6add786 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -95,7 +95,7 @@
         final List<PreferenceController> controllers = new ArrayList<>();
         controllers.add(airplaneModePreferenceController);
         controllers.add(mobileNetworkPreferenceController);
-        controllers.add(new TetherPreferenceController(context));
+        controllers.add(new TetherPreferenceController(context, lifecycle));
         controllers.add(vpnPreferenceController);
         controllers.add(new ProxyPreferenceController(context));
         controllers.add(mobilePlanPreferenceController);
diff --git a/src/com/android/settings/network/TetherPreferenceController.java b/src/com/android/settings/network/TetherPreferenceController.java
index 5b8d55e..f23118a 100644
--- a/src/com/android/settings/network/TetherPreferenceController.java
+++ b/src/com/android/settings/network/TetherPreferenceController.java
@@ -28,6 +28,9 @@
 import com.android.settings.R;
 import com.android.settings.TetherSettings;
 import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnDestroy;
 
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -35,7 +38,8 @@
 import static com.android.settingslib.RestrictedLockUtils.checkIfRestrictionEnforced;
 import static com.android.settingslib.RestrictedLockUtils.hasBaseUserRestriction;
 
-public class TetherPreferenceController extends PreferenceController {
+public class TetherPreferenceController extends PreferenceController
+        implements LifecycleObserver, OnDestroy {
 
     private static final String KEY_TETHER_SETTINGS = "tether_settings";
 
@@ -62,12 +66,12 @@
     TetherPreferenceController() {
         super(null);
         mAdminDisallowedTetherConfig = false;
-        mBluetoothPan = null;
+        mBluetoothPan = new AtomicReference<>();
         mConnectivityManager = null;
-        mBluetoothAdapter = null;
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
-    public TetherPreferenceController(Context context) {
+    public TetherPreferenceController(Context context, Lifecycle lifecycle) {
         super(context);
         mBluetoothPan = new AtomicReference<>();
         mAdminDisallowedTetherConfig = checkIfRestrictionEnforced(
@@ -75,6 +79,9 @@
         mConnectivityManager =
                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
         if (mBluetoothAdapter != null) {
             mBluetoothAdapter.getProfileProxy(context, mBtProfileServiceListener,
                     BluetoothProfile.PAN);
@@ -113,6 +120,14 @@
         return KEY_TETHER_SETTINGS;
     }
 
+    @Override
+    public void onDestroy() {
+        final BluetoothProfile profile = mBluetoothPan.getAndSet(null);
+        if (profile != null && mBluetoothAdapter != null) {
+            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile);
+        }
+    }
+
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     void updateSummary() {
         if (mPreference == null) {
diff --git a/tests/robotests/src/com/android/settings/network/TetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/TetherPreferenceControllerTest.java
index a856e8c..6f751eb 100644
--- a/tests/robotests/src/com/android/settings/network/TetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/TetherPreferenceControllerTest.java
@@ -18,9 +18,10 @@
 
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.os.UserManager;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
@@ -35,6 +36,9 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -65,6 +69,18 @@
     }
 
     @Test
+    public void goThroughLifecycle_shouldDestoryBluetoothProfile() {
+        final BluetoothPan pan = mock(BluetoothPan.class);
+        final AtomicReference<BluetoothPan> panRef =
+                ReflectionHelpers.getField(mController, "mBluetoothPan");
+        panRef.set(pan);
+
+        mController.onDestroy();
+
+        verify(mBluetoothAdapter).closeProfileProxy(BluetoothProfile.PAN, pan);
+    }
+
+    @Test
     public void updateSummary_noPreference_noInteractionWithConnectivityManager() {
         mController.updateSummary();
         verifyNoMoreInteractions(mConnectivityManager);