From 2bb35ff3fcd0325e81474b59d4dfc86c9c36b36c Mon Sep 17 00:00:00 2001 From: Jason Huang Date: Mon, 27 Jun 2022 20:57:22 +0800 Subject: [PATCH] network: fix binder object leakage in settings Summary: When BT ON and enter into NetworkDashboardFragment, and turning BT OFF and exiting the fragment, the `BluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN)` will not be called. This causes binder leakage on next time the NetworkDashboardFragment is entered, until killing Settings process. Reproduce Steps: 1. Turn BT ON 2. Open Settings process 3. Enter "Network & internet" (NetworkDashboardFragment) 4. Turn BT OFF 5. Back to previous page (do not kill Settings process) * Repeat Step 3-5 Solution: Do not set value of `mBluetoothPan` to null when `onServiceDisconnected` raised, to ensure the binder object (profile proxy) be closed in lifecycle `onDestroy()`. Bug: 243128377 Test: enter "Network & internet" page, turn off BT and leave the page Change-Id: Ieca3e5401c23d1b0ffece1bbb0db96988044262d --- src/com/android/settings/TetherSettings.java | 53 ++++++++++++++++--- .../AllInOneTetherPreferenceController.java | 46 ++++++++++++++-- .../network/TetherPreferenceController.java | 42 ++++++++++++++- 3 files changed, 128 insertions(+), 13 deletions(-) diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index 012996b878d..f4c9eb257a6 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -43,6 +43,7 @@ import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.provider.SearchIndexableResource; +import android.text.TextUtils; import android.util.FeatureFlagUtils; import android.util.Log; @@ -97,6 +98,7 @@ public class TetherSettings extends RestrictedSettingsFragment private SwitchPreference mEthernetTether; private BroadcastReceiver mTetherChangeReceiver; + private BroadcastReceiver mBluetoothStateReceiver; private String[] mBluetoothRegexs; private AtomicReference mBluetoothPan = new AtomicReference<>(); @@ -167,6 +169,12 @@ public class TetherSettings extends RestrictedSettingsFragment adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener, BluetoothProfile.PAN); } + if (mBluetoothStateReceiver == null) { + mBluetoothStateReceiver = new BluetoothStateReceiver(); + mContext.registerReceiver( + mBluetoothStateReceiver, + new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)); + } setupTetherPreference(); setTopIntroPreferenceTitle(); @@ -216,6 +224,10 @@ public class TetherSettings extends RestrictedSettingsFragment if (profile != null && adapter != null) { adapter.closeProfileProxy(BluetoothProfile.PAN, profile); } + if (mBluetoothStateReceiver != null) { + mContext.unregisterReceiver(mBluetoothStateReceiver); + mBluetoothStateReceiver = null; + } super.onDestroy(); } @@ -255,6 +267,30 @@ public class TetherSettings extends RestrictedSettingsFragment } } + private class BluetoothStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + Log.i(TAG, "onReceive: action: " + action); + + if (TextUtils.equals(action, BluetoothAdapter.ACTION_STATE_CHANGED)) { + final int state = + intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); + Log.i(TAG, "onReceive: state: " + BluetoothAdapter.nameForState(state)); + final BluetoothProfile profile = mBluetoothPan.get(); + switch(state) { + case BluetoothAdapter.STATE_ON: + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (profile == null && adapter != null) { + adapter.getProfileProxy(mContext, mProfileServiceListener, + BluetoothProfile.PAN); + } + break; + } + } + } + } + private class TetherChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context content, Intent intent) { @@ -558,13 +594,16 @@ public class TetherSettings extends RestrictedSettingsFragment private BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() { - public void onServiceConnected(int profile, BluetoothProfile proxy) { - mBluetoothPan.set((BluetoothPan) proxy); - } - public void onServiceDisconnected(int profile) { - mBluetoothPan.set(null); - } - }; + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (mBluetoothPan.get() == null) { + mBluetoothPan.set((BluetoothPan) proxy); + } + } + + @Override + public void onServiceDisconnected(int profile) { /* Do nothing */ } + }; public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider() { diff --git a/src/com/android/settings/network/AllInOneTetherPreferenceController.java b/src/com/android/settings/network/AllInOneTetherPreferenceController.java index 02dc440d4bf..7baa917448e 100644 --- a/src/com/android/settings/network/AllInOneTetherPreferenceController.java +++ b/src/com/android/settings/network/AllInOneTetherPreferenceController.java @@ -27,8 +27,12 @@ import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfRestric import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.UserHandle; +import android.text.TextUtils; import android.util.FeatureFlagUtils; import android.util.Log; @@ -66,17 +70,18 @@ public class AllInOneTetherPreferenceController extends BasePreferenceController new BluetoothProfile.ServiceListener() { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { - mBluetoothPan.set((BluetoothPan) proxy); + if (mBluetoothPan.get() == null) { + mBluetoothPan.set((BluetoothPan) proxy); + } } @Override - public void onServiceDisconnected(int profile) { - mBluetoothPan.set(null); - } + public void onServiceDisconnected(int profile) { /* Do nothing */ } }; private PrimarySwitchPreference mPreference; private TetherEnabler mTetherEnabler; + private BroadcastReceiver mBluetoothStateReceiver; @VisibleForTesting(otherwise = VisibleForTesting.NONE) AllInOneTetherPreferenceController() { @@ -164,6 +169,12 @@ public class AllInOneTetherPreferenceController extends BasePreferenceController mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener, BluetoothProfile.PAN); } + if (mBluetoothStateReceiver == null) { + mBluetoothStateReceiver = new BluetoothStateReceiver(); + mContext.registerReceiver( + mBluetoothStateReceiver, + new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)); + } } @OnLifecycleEvent(Event.ON_RESUME) @@ -186,6 +197,10 @@ public class AllInOneTetherPreferenceController extends BasePreferenceController if (profile != null && mBluetoothAdapter != null) { mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile); } + if (mBluetoothStateReceiver != null) { + mContext.unregisterReceiver(mBluetoothStateReceiver); + mBluetoothStateReceiver = null; + } } void initEnabler(Lifecycle lifecycle) { @@ -205,4 +220,27 @@ public class AllInOneTetherPreferenceController extends BasePreferenceController mTetheringState = state; updateState(mPreference); } + + private class BluetoothStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + Log.i(TAG, "onReceive: action: " + action); + + if (TextUtils.equals(action, BluetoothAdapter.ACTION_STATE_CHANGED)) { + final int state = + intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); + Log.i(TAG, "onReceive: state: " + BluetoothAdapter.nameForState(state)); + final BluetoothProfile profile = mBluetoothPan.get(); + switch(state) { + case BluetoothAdapter.STATE_ON: + if (profile == null && mBluetoothAdapter != null) { + mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener, + BluetoothProfile.PAN); + } + break; + } + } + } + } } diff --git a/src/com/android/settings/network/TetherPreferenceController.java b/src/com/android/settings/network/TetherPreferenceController.java index 803418190ff..3a1c4f04e6b 100644 --- a/src/com/android/settings/network/TetherPreferenceController.java +++ b/src/com/android/settings/network/TetherPreferenceController.java @@ -33,7 +33,9 @@ import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; import android.util.FeatureFlagUtils; +import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -56,6 +58,7 @@ import java.util.concurrent.atomic.AtomicReference; public class TetherPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnResume, OnPause, OnDestroy { + private static final String TAG = "TetherPreferenceController"; private static final String KEY_TETHER_SETTINGS = "tether_settings"; private final boolean mAdminDisallowedTetherConfig; @@ -66,12 +69,13 @@ public class TetherPreferenceController extends AbstractPreferenceController imp final BluetoothProfile.ServiceListener mBtProfileServiceListener = new android.bluetooth.BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { - mBluetoothPan.set((BluetoothPan) proxy); + if (mBluetoothPan.get() == null) { + mBluetoothPan.set((BluetoothPan) proxy); + } updateSummary(); } public void onServiceDisconnected(int profile) { - mBluetoothPan.set(null); updateSummary(); } }; @@ -79,6 +83,7 @@ public class TetherPreferenceController extends AbstractPreferenceController imp private SettingObserver mAirplaneModeObserver; private Preference mPreference; private TetherBroadcastReceiver mTetherReceiver; + private BroadcastReceiver mBluetoothStateReceiver; @VisibleForTesting(otherwise = VisibleForTesting.NONE) TetherPreferenceController() { @@ -133,6 +138,12 @@ public class TetherPreferenceController extends AbstractPreferenceController imp mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener, BluetoothProfile.PAN); } + if (mBluetoothStateReceiver == null) { + mBluetoothStateReceiver = new BluetoothStateReceiver(); + mContext.registerReceiver( + mBluetoothStateReceiver, + new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)); + } } @Override @@ -165,6 +176,10 @@ public class TetherPreferenceController extends AbstractPreferenceController imp if (profile != null && mBluetoothAdapter != null) { mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile); } + if (mBluetoothStateReceiver != null) { + mContext.unregisterReceiver(mBluetoothStateReceiver); + mBluetoothStateReceiver = null; + } } public static boolean isTetherConfigDisallowed(Context context) { @@ -270,4 +285,27 @@ public class TetherPreferenceController extends AbstractPreferenceController imp } } + + private class BluetoothStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + Log.i(TAG, "onReceive: action: " + action); + + if (TextUtils.equals(action, BluetoothAdapter.ACTION_STATE_CHANGED)) { + final int state = + intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); + Log.i(TAG, "onReceive: state: " + BluetoothAdapter.nameForState(state)); + final BluetoothProfile profile = mBluetoothPan.get(); + switch(state) { + case BluetoothAdapter.STATE_ON: + if (profile == null && mBluetoothAdapter != null) { + mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener, + BluetoothProfile.PAN); + } + break; + } + } + } + } }