Android -- Airplane Mode对Bluetooth启动的影响
在Android/IOS系统的手机上,在某些场景下可能会用到Airplane模式;开启这个功能后,它会关闭Wifi/BT功能等无线功能。这里记录下在Android系统上,飞行模式是如何影响开关蓝牙的。
在Android手机等设备上,点击状态栏上的飞行模式按钮时,会作用到SystemUI中的AirplaneModeTitle中:
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTileImpl<BooleanState> {
private final Icon mIcon =
ResourceIcon.get(R.drawable.ic_signal_airplane);
private final GlobalSetting mSetting;
private boolean mListening;
public AirplaneModeTile(QSHost host) {
super(host);
mSetting = new GlobalSetting(mContext, mHandler, Global.AIRPLANE_MODE_ON) {
@Override
protected void handleValueChanged(int value) {
handleRefreshState(value);
}
};
}
......
@Override
public void handleClick() {
boolean airplaneModeEnabled = mState.value;
MetricsLogger.action(mContext, getMetricsCategory(), !airplaneModeEnabled);
if (!airplaneModeEnabled && Boolean.parseBoolean(
SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS), 0);
return;
}
setEnabled(!airplaneModeEnabled);
}
private void setEnabled(boolean enabled) {
final ConnectivityManager mgr =
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mgr.setAirplaneMode(enabled);
}
........
}
用户点击Airplane Mode按钮时,最终会call到setEnable()中,通过ConnectityManager::setAirplaneMode():
ConnectityManager.java
/**
* Set the value for enabling/disabling airplane mode
*
* @param enable whether to enable airplane mode or not
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
public void setAirplaneMode(boolean enable) {
try {
mService.setAirplaneMode(enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
ConnectivityService.java
@Override
public void setAirplaneMode(boolean enable) {
enforceConnectivityInternalPermission();
final long ident = Binder.clearCallingIdentity();
try {
final ContentResolver cr = mContext.getContentResolver();
Settings.Global.putInt(cr, Settings.Global.AIRPLANE_MODE_ON, encodeBool(enable));
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", enable);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
可以看到,设置Airplane Mode最终是改变Settings.Global.AIRPLANE_MODE_ON字段,并发送广播向外通知这次改变。在前面分析BT启动的文章中,可以看到BluetoothManagerService有通过注册ContentObserve监听这个字段的变化:
BluetoothManagerService::init()
String airplaneModeRadios =
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);//监听Airplane mode改变,相应改变BT状态;这在TV plateform上基本没用
}
......
private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
@Override
public void onChange(boolean unused) {
synchronized (this) {
if (isBluetoothPersistedStateOn()) {//判断当前BT是否enable
if (isAirplaneModeOn()) {//如果当前飞行模式已开启
persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);//设置BLUETOOTH_ON字段为BLUETOOTH_ON_AIRPLANE;
} else {
persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);设置BLUETOOTH_ON字段为BLUETOOTH_ON_BLUETOOTH;
}
}
int st = BluetoothAdapter.STATE_OFF;
try {
mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
st = mBluetooth.getState();
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to call getState", e);
return;
} finally {
mBluetoothLock.readLock().unlock();
}
Slog.d(TAG,
"Airplane Mode change - current state: " + BluetoothAdapter.nameForState(
st));
if (isAirplaneModeOn()) {//Enable飞行模式,需要关闭蓝牙
// Clear registered LE apps to force shut-off
clearBleApps();
// If state is BLE_ON make sure we trigger disableBLE
if (st == BluetoothAdapter.STATE_BLE_ON) {//如果之前Bt只是enable ble mode,单独处理
try {
mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
addActiveLog(
BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
mContext.getPackageName(), false);
mBluetooth.onBrEdrDown();
mEnable = false;
mEnableExternal = false;
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to call onBrEdrDown", e);
} finally {
mBluetoothLock.readLock().unlock();
}
} else if (st == BluetoothAdapter.STATE_ON) {//如果之前bt的enable mode是normal,普通关闭蓝牙
sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
mContext.getPackageName());
}
} else if (mEnableExternal) {//关闭飞行模式,如果之前BT是开的,这时要开启蓝牙,恢复之前状态;
sendEnableMsg(mQuietEnableExternal,
BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
mContext.getPackageName());
}
}
}
};
主要处理是:
- 开启分析模式时,BLUETOOTH_ON的字段设置为BLUETOOTH_ON_AIRPLANE;否则为BLUETOOTH_ON_BLUETOOTH;这两个值都表示之前蓝牙是enable的,但AIRPLANE会影响蓝牙启动,它拥有更高的优先级来影响enable进程
- 当前是开启飞行模式,如果此时蓝牙是开的,则需要关掉蓝牙。我们开启蓝牙时,会有两种模式:1)ble only模式,这时无法支持br/edr设备(如音箱),只能支持鼠标、键盘这在低功耗设备;2)普通模式,同时支持ble和br/edr设备
- 当前是关闭飞行模式,如果之前蓝牙是开的,则需要开启蓝牙
从上一篇分析开机自启蓝牙的流程看到,在handleOnBootPhase()中,只有当BLUETOOTH_ON的值为不为STATE_OFF、且为BLUETOOTH_ON_BLUETOOTH时,才去enable蓝牙;也就是如果这次用户设置了飞行模式、导致蓝牙关闭,下次开机时蓝牙也不会打开。