Loading service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java 0 → 100644 +258 −0 Original line number Diff line number Diff line /* * Copyright 2019 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.server; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.provider.Settings; import android.util.Log; import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; /** * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks * whether we need to inform BluetoothManagerService on this change. * * The information of airplane mode turns on would not be passed to the BluetoothManagerService * when Bluetooth is on and Bluetooth is in one of the following situations: * 1. Bluetooth A2DP is connected. * 2. Bluetooth Hearing Aid profile is connected. */ class BluetoothAirplaneModeListener { private static final String TAG = "BluetoothAirplaneModeListener"; @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count"; private static final int MSG_AIRPLANE_MODE_CHANGED = 0; @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times private final BluetoothManagerService mBluetoothManager; private final BluetoothAirplaneModeHandler mHandler; private AirplaneModeHelper mAirplaneHelper; @VisibleForTesting int mToastCount = 0; BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) { mBluetoothManager = service; mHandler = new BluetoothAirplaneModeHandler(looper); context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, mAirplaneModeObserver); } private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { @Override public void onChange(boolean unused) { // Post from system main thread to android_io thread. Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED); mHandler.sendMessage(msg); } }; private class BluetoothAirplaneModeHandler extends Handler { BluetoothAirplaneModeHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_AIRPLANE_MODE_CHANGED: handleAirplaneModeChange(); break; default: Log.e(TAG, "Invalid message: " + msg.what); break; } } } /** * Call after boot complete */ @VisibleForTesting void start(AirplaneModeHelper helper) { Log.i(TAG, "start"); mAirplaneHelper = helper; mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT); } @VisibleForTesting boolean shouldPopToast() { if (mToastCount >= MAX_TOAST_COUNT) { return false; } mToastCount++; mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount); return true; } @VisibleForTesting void handleAirplaneModeChange() { if (shouldSkipAirplaneModeChange()) { Log.i(TAG, "Ignore airplane mode change"); // We have to store Bluetooth state here, so if user turns off Bluetooth // after airplane mode is turned on, we don't forget to turn on Bluetooth // when airplane mode turns off. mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON, BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); if (shouldPopToast()) { mAirplaneHelper.showToastMessage(); } return; } mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); } @VisibleForTesting boolean shouldSkipAirplaneModeChange() { if (mAirplaneHelper == null) { return false; } if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn() || !mAirplaneHelper.isA2dpOrHearingAidConnected()) { return false; } return true; } /** * Helper class that handles callout and callback methods without * complex logic. */ @VisibleForTesting public static class AirplaneModeHelper { private volatile BluetoothA2dp mA2dp; private volatile BluetoothHearingAid mHearingAid; private final BluetoothAdapter mAdapter; private final Context mContext; AirplaneModeHelper(Context context) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mContext = context; mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.HEARING_AID); } private final ServiceListener mProfileServiceListener = new ServiceListener() { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { // Setup Bluetooth profile proxies switch (profile) { case BluetoothProfile.A2DP: mA2dp = (BluetoothA2dp) proxy; break; case BluetoothProfile.HEARING_AID: mHearingAid = (BluetoothHearingAid) proxy; break; default: break; } } @Override public void onServiceDisconnected(int profile) { // Clear Bluetooth profile proxies switch (profile) { case BluetoothProfile.A2DP: mA2dp = null; break; case BluetoothProfile.HEARING_AID: mHearingAid = null; break; default: break; } } }; @VisibleForTesting public boolean isA2dpOrHearingAidConnected() { return isA2dpConnected() || isHearingAidConnected(); } @VisibleForTesting public boolean isBluetoothOn() { final BluetoothAdapter adapter = mAdapter; if (adapter == null) { return false; } return adapter.getLeState() == BluetoothAdapter.STATE_ON; } @VisibleForTesting public boolean isAirplaneModeOn() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 1; } @VisibleForTesting public void onAirplaneModeChanged(BluetoothManagerService managerService) { managerService.onAirplaneModeChanged(); } @VisibleForTesting public int getSettingsInt(String name) { return Settings.Global.getInt(mContext.getContentResolver(), name, 0); } @VisibleForTesting public void setSettingsInt(String name, int value) { Settings.Global.putInt(mContext.getContentResolver(), name, value); } @VisibleForTesting public void showToastMessage() { Resources r = mContext.getResources(); final CharSequence text = r.getString( R.string.bluetooth_airplane_mode_toast, 0); Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); } private boolean isA2dpConnected() { final BluetoothA2dp a2dp = mA2dp; if (a2dp == null) { return false; } return a2dp.getConnectedDevices().size() > 0; } private boolean isHearingAidConnected() { final BluetoothHearingAid hearingAid = mHearingAid; if (hearingAid == null) { return false; } return hearingAid.getConnectedDevices().size() > 0; } }; } service/java/com/android/server/bluetooth/BluetoothManagerService.java +74 −57 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import android.util.Slog; import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.pm.UserRestrictionsUtils; Loading Loading @@ -138,7 +139,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Bluetooth persisted setting is on // but Airplane mode will affect Bluetooth state at start up // and Airplane mode will have higher priority. private static final int BLUETOOTH_ON_AIRPLANE = 2; @VisibleForTesting static final int BLUETOOTH_ON_AIRPLANE = 2; private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; Loading @@ -159,6 +161,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mBinding; private boolean mUnbinding; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; Loading Loading @@ -257,9 +261,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { @Override public void onChange(boolean unused) { public void onAirplaneModeChanged() { synchronized (this) { if (isBluetoothPersistedStateOn()) { if (isAirplaneModeOn()) { Loading Loading @@ -318,7 +320,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } }; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -430,9 +431,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS); if (airplaneModeRadios == null || airplaneModeRadios.contains( Settings.Global.RADIO_BLUETOOTH)) { mContentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, mAirplaneModeObserver); mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( this, IoThread.get().getLooper(), context); } int systemUiUid = -1; Loading Loading @@ -478,6 +478,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return state != BLUETOOTH_OFF; } private boolean isBluetoothPersistedStateOnAirplane() { if (!supportBluetoothPersistedState()) { return false; } int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); if (DBG) { Slog.d(TAG, "Bluetooth persisted state: " + state); } return state == BLUETOOTH_ON_AIRPLANE; } /** * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH */ Loading Loading @@ -954,10 +965,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } synchronized (mReceiver) { if (!isBluetoothPersistedStateOnAirplane()) { if (persist) { persistBluetoothSetting(BLUETOOTH_OFF); } mEnableExternal = false; } sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); } Loading Loading @@ -1185,6 +1198,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } if (mBluetoothAirplaneModeListener != null) { mBluetoothAirplaneModeListener.start( new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext)); } } /** Loading service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright 2019 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.server; import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.os.Looper; import android.provider.Settings; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @MediumTest @RunWith(AndroidJUnit4.class) public class BluetoothAirplaneModeListenerTest { private Context mContext; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; private BluetoothAdapter mBluetoothAdapter; private AirplaneModeHelper mHelper; @Mock BluetoothManagerService mBluetoothManagerService; @Before public void setUp() throws Exception { mContext = InstrumentationRegistry.getTargetContext(); mHelper = mock(AirplaneModeHelper.class); when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT)) .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT); doNothing().when(mHelper).setSettingsInt(anyString(), anyInt()); doNothing().when(mHelper).showToastMessage(); doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class)); mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( mBluetoothManagerService, Looper.getMainLooper(), mContext); mBluetoothAirplaneModeListener.start(mHelper); } @Test public void testIgnoreOnAirplanModeChange() { Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isBluetoothOn()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isAirplaneModeOn()).thenReturn(true); Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); } @Test public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() { mBluetoothAirplaneModeListener.handleAirplaneModeChange(); verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService); } @Test public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() { mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; when(mHelper.isBluetoothOn()).thenReturn(true); when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); verify(mHelper, times(0)).showToastMessage(); verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); } @Test public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() { mBluetoothAirplaneModeListener.mToastCount = 0; when(mHelper.isBluetoothOn()).thenReturn(true); when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); verify(mHelper).showToastMessage(); verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); } @Test public void testIsPopToast_PopToast() { mBluetoothAirplaneModeListener.mToastCount = 0; Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast()); verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1); } @Test public void testIsPopToast_NotPopToast() { mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast()); verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt()); } } Loading
service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java 0 → 100644 +258 −0 Original line number Diff line number Diff line /* * Copyright 2019 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.server; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.provider.Settings; import android.util.Log; import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; /** * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks * whether we need to inform BluetoothManagerService on this change. * * The information of airplane mode turns on would not be passed to the BluetoothManagerService * when Bluetooth is on and Bluetooth is in one of the following situations: * 1. Bluetooth A2DP is connected. * 2. Bluetooth Hearing Aid profile is connected. */ class BluetoothAirplaneModeListener { private static final String TAG = "BluetoothAirplaneModeListener"; @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count"; private static final int MSG_AIRPLANE_MODE_CHANGED = 0; @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times private final BluetoothManagerService mBluetoothManager; private final BluetoothAirplaneModeHandler mHandler; private AirplaneModeHelper mAirplaneHelper; @VisibleForTesting int mToastCount = 0; BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) { mBluetoothManager = service; mHandler = new BluetoothAirplaneModeHandler(looper); context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, mAirplaneModeObserver); } private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { @Override public void onChange(boolean unused) { // Post from system main thread to android_io thread. Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED); mHandler.sendMessage(msg); } }; private class BluetoothAirplaneModeHandler extends Handler { BluetoothAirplaneModeHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_AIRPLANE_MODE_CHANGED: handleAirplaneModeChange(); break; default: Log.e(TAG, "Invalid message: " + msg.what); break; } } } /** * Call after boot complete */ @VisibleForTesting void start(AirplaneModeHelper helper) { Log.i(TAG, "start"); mAirplaneHelper = helper; mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT); } @VisibleForTesting boolean shouldPopToast() { if (mToastCount >= MAX_TOAST_COUNT) { return false; } mToastCount++; mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount); return true; } @VisibleForTesting void handleAirplaneModeChange() { if (shouldSkipAirplaneModeChange()) { Log.i(TAG, "Ignore airplane mode change"); // We have to store Bluetooth state here, so if user turns off Bluetooth // after airplane mode is turned on, we don't forget to turn on Bluetooth // when airplane mode turns off. mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON, BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); if (shouldPopToast()) { mAirplaneHelper.showToastMessage(); } return; } mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); } @VisibleForTesting boolean shouldSkipAirplaneModeChange() { if (mAirplaneHelper == null) { return false; } if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn() || !mAirplaneHelper.isA2dpOrHearingAidConnected()) { return false; } return true; } /** * Helper class that handles callout and callback methods without * complex logic. */ @VisibleForTesting public static class AirplaneModeHelper { private volatile BluetoothA2dp mA2dp; private volatile BluetoothHearingAid mHearingAid; private final BluetoothAdapter mAdapter; private final Context mContext; AirplaneModeHelper(Context context) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mContext = context; mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.HEARING_AID); } private final ServiceListener mProfileServiceListener = new ServiceListener() { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { // Setup Bluetooth profile proxies switch (profile) { case BluetoothProfile.A2DP: mA2dp = (BluetoothA2dp) proxy; break; case BluetoothProfile.HEARING_AID: mHearingAid = (BluetoothHearingAid) proxy; break; default: break; } } @Override public void onServiceDisconnected(int profile) { // Clear Bluetooth profile proxies switch (profile) { case BluetoothProfile.A2DP: mA2dp = null; break; case BluetoothProfile.HEARING_AID: mHearingAid = null; break; default: break; } } }; @VisibleForTesting public boolean isA2dpOrHearingAidConnected() { return isA2dpConnected() || isHearingAidConnected(); } @VisibleForTesting public boolean isBluetoothOn() { final BluetoothAdapter adapter = mAdapter; if (adapter == null) { return false; } return adapter.getLeState() == BluetoothAdapter.STATE_ON; } @VisibleForTesting public boolean isAirplaneModeOn() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 1; } @VisibleForTesting public void onAirplaneModeChanged(BluetoothManagerService managerService) { managerService.onAirplaneModeChanged(); } @VisibleForTesting public int getSettingsInt(String name) { return Settings.Global.getInt(mContext.getContentResolver(), name, 0); } @VisibleForTesting public void setSettingsInt(String name, int value) { Settings.Global.putInt(mContext.getContentResolver(), name, value); } @VisibleForTesting public void showToastMessage() { Resources r = mContext.getResources(); final CharSequence text = r.getString( R.string.bluetooth_airplane_mode_toast, 0); Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); } private boolean isA2dpConnected() { final BluetoothA2dp a2dp = mA2dp; if (a2dp == null) { return false; } return a2dp.getConnectedDevices().size() > 0; } private boolean isHearingAidConnected() { final BluetoothHearingAid hearingAid = mHearingAid; if (hearingAid == null) { return false; } return hearingAid.getConnectedDevices().size() > 0; } }; }
service/java/com/android/server/bluetooth/BluetoothManagerService.java +74 −57 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import android.util.Slog; import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.pm.UserRestrictionsUtils; Loading Loading @@ -138,7 +139,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Bluetooth persisted setting is on // but Airplane mode will affect Bluetooth state at start up // and Airplane mode will have higher priority. private static final int BLUETOOTH_ON_AIRPLANE = 2; @VisibleForTesting static final int BLUETOOTH_ON_AIRPLANE = 2; private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; Loading @@ -159,6 +161,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mBinding; private boolean mUnbinding; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; Loading Loading @@ -257,9 +261,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { @Override public void onChange(boolean unused) { public void onAirplaneModeChanged() { synchronized (this) { if (isBluetoothPersistedStateOn()) { if (isAirplaneModeOn()) { Loading Loading @@ -318,7 +320,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } }; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -430,9 +431,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS); if (airplaneModeRadios == null || airplaneModeRadios.contains( Settings.Global.RADIO_BLUETOOTH)) { mContentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, mAirplaneModeObserver); mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( this, IoThread.get().getLooper(), context); } int systemUiUid = -1; Loading Loading @@ -478,6 +478,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return state != BLUETOOTH_OFF; } private boolean isBluetoothPersistedStateOnAirplane() { if (!supportBluetoothPersistedState()) { return false; } int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); if (DBG) { Slog.d(TAG, "Bluetooth persisted state: " + state); } return state == BLUETOOTH_ON_AIRPLANE; } /** * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH */ Loading Loading @@ -954,10 +965,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } synchronized (mReceiver) { if (!isBluetoothPersistedStateOnAirplane()) { if (persist) { persistBluetoothSetting(BLUETOOTH_OFF); } mEnableExternal = false; } sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); } Loading Loading @@ -1185,6 +1198,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } if (mBluetoothAirplaneModeListener != null) { mBluetoothAirplaneModeListener.start( new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext)); } } /** Loading
service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright 2019 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.server; import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.os.Looper; import android.provider.Settings; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @MediumTest @RunWith(AndroidJUnit4.class) public class BluetoothAirplaneModeListenerTest { private Context mContext; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; private BluetoothAdapter mBluetoothAdapter; private AirplaneModeHelper mHelper; @Mock BluetoothManagerService mBluetoothManagerService; @Before public void setUp() throws Exception { mContext = InstrumentationRegistry.getTargetContext(); mHelper = mock(AirplaneModeHelper.class); when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT)) .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT); doNothing().when(mHelper).setSettingsInt(anyString(), anyInt()); doNothing().when(mHelper).showToastMessage(); doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class)); mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( mBluetoothManagerService, Looper.getMainLooper(), mContext); mBluetoothAirplaneModeListener.start(mHelper); } @Test public void testIgnoreOnAirplanModeChange() { Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isBluetoothOn()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isAirplaneModeOn()).thenReturn(true); Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); } @Test public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() { mBluetoothAirplaneModeListener.handleAirplaneModeChange(); verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService); } @Test public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() { mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; when(mHelper.isBluetoothOn()).thenReturn(true); when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); verify(mHelper, times(0)).showToastMessage(); verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); } @Test public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() { mBluetoothAirplaneModeListener.mToastCount = 0; when(mHelper.isBluetoothOn()).thenReturn(true); when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); verify(mHelper).showToastMessage(); verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); } @Test public void testIsPopToast_PopToast() { mBluetoothAirplaneModeListener.mToastCount = 0; Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast()); verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1); } @Test public void testIsPopToast_NotPopToast() { mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast()); verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt()); } }