7

Android 当点击飞行模式都发生了什么?

 3 years ago
source link: http://wuxiaolong.me/2019/04/14/AirplaneMode/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

今天中午午休时,我把手机开飞行模式了,能看到 WiFi 、蜂窝数据和蓝牙都关闭了,心想,这时候还能收到短信吗?顺着好奇心,我们不妨来研究一下源码,看看点击飞行模式都发生了什么?

基于 Android 9.0 源码分析。

AirplaneModeTile#handleClick

飞行模式设置入口,下拉状态栏,点击飞行模式图标,我们就从这里看起,其他入口逻辑差不多。这个源码位于AOSP/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java直接看其点击事件。

public class AirplaneModeTile extends QSTileImpl<BooleanState> {
//省略其他代码
@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);
//省略其他代码

接下来调用 ConnectivityManager#setAirplaneMode 方法。

ConnectivityManager#setAirplaneMode

AOSP/frameworks/base/core/java/android/net/ConnectivityManager.java

@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_SETTINGS,
android.Manifest.permission.NETWORK_SETUP_WIZARD,
android.Manifest.permission.NETWORK_STACK})
@SystemApi
public void setAirplaneMode(boolean enable) {
mService.setAirplaneMode(enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();

mService 是 IConnectivityManager,是个接口,其实现看 ConnectivityService#setAirplaneMode。

ConnectivityService#setAirplaneMode

/AOSP/frameworks/base/services/core/java/com/android/server

@Override
public void setAirplaneMode(boolean enable) {
enforceNetworkStackSettingsOrSetup();
final long ident = Binder.clearCallingIdentity();
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);

这里设置了飞行模式状态的系统变量以及发送 ACTION_AIRPLANE_MODE_CHANGED 系统广播,接下来看看该广播的接受。经过查找,ACTION_AIRPLANE_MODE_CHANGED 广播接受有好几次,,而手机开启或关闭飞行模式时,主要是开启或关闭 Radio 无线通信,其处理逻辑在 PhoneGlobals 类中。另外 WiFi 、蜂窝数据和蓝牙处理都能相应追踪到。

补充:什么是 Radio?Radio 是无线通信模块的驱动程序,负责网络通信。

PhoneGlobals

AOSP/packages/services/Telephony/src/com/android/phone/PhoneGlobals.java

public class PhoneGlobals extends ContextWrapper {
//省略其他代码
private void handleAirplaneModeChange(Context context, int newMode) {
int cellState = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.CELL_ON, PhoneConstants.CELL_ON_FLAG);
boolean isAirplaneNewlyOn = (newMode == 1);
switch (cellState) {
case PhoneConstants.CELL_OFF_FLAG:
// Airplane mode does not affect the cell radio if user
// has turned it off.
break;
case PhoneConstants.CELL_ON_FLAG:
maybeTurnCellOff(context, isAirplaneNewlyOn);
break;
case PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG:
maybeTurnCellOn(context, isAirplaneNewlyOn);
break;
* Returns true if the radio must be turned off when entering airplane mode.
private boolean isCellOffInAirplaneMode(Context context) {
String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_RADIOS);
return airplaneModeRadios == null
|| airplaneModeRadios.contains(Settings.Global.RADIO_CELL);
private void setRadioPowerOff(Context context) {
Log.i(LOG_TAG, "Turning radio off - airplane");
Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
SystemProperties.set("persist.radio.airplane_mode_on", "1");
Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
PhoneUtils.setRadioPower(false);
private void setRadioPowerOn(Context context) {
Log.i(LOG_TAG, "Turning radio on - airplane");
Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
PhoneConstants.CELL_ON_FLAG);
Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT,
SystemProperties.set("persist.radio.airplane_mode_on", "0");
PhoneUtils.setRadioPower(true);
private void maybeTurnCellOff(Context context, boolean isAirplaneNewlyOn) {
if (isAirplaneNewlyOn) {
// If we are trying to turn off the radio, make sure there are no active
// emergency calls. If there are, switch airplane mode back to off.
TelecomManager tm = (TelecomManager) context.getSystemService(TELECOM_SERVICE);
if (tm != null && tm.isInEmergencyCall()) {
// Switch airplane mode back to off.
ConnectivityManager.from(this).setAirplaneMode(false);
Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
.show();
Log.i(LOG_TAG, "Ignoring airplane mode: emergency call. Turning airplane off");
} else if (isCellOffInAirplaneMode(context)) {
setRadioPowerOff(context);
} else {
Log.i(LOG_TAG, "Ignoring airplane mode: settings prevent cell radio power off");
private void maybeTurnCellOn(Context context, boolean isAirplaneNewlyOn) {
if (!isAirplaneNewlyOn) {
setRadioPowerOn(context);
* Receiver for misc intent broadcasts the Phone app cares about.
private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
int airplaneMode = Settings.Global.getInt(getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, AIRPLANE_OFF);
// Treat any non-OFF values as ON.
if (airplaneMode != AIRPLANE_OFF) {
airplaneMode = AIRPLANE_ON;
handleAirplaneModeChange(context, airplaneMode);
} //省略其他代码
//省略其他代码

PhoneUtils.setRadioPower 会继续调用 GsmCdmaPhone#setRadioPower,调用 mSST.setRadioPower,最终由 mSST 对象向 RIL 对象发起关闭或开启 Radio 无线通信模块的请求,这里就不细看了,有兴趣可以自己继续跟下去。到这里我们就对“Android 当点击飞行模式都发生了什么?”流程有了大致了解,就酱紫,Over。

 我的微信公众号:吴小龙同学,欢迎关注交流,公号回复关键字「1024」有惊喜哦。
code?username=MrWuXiaolong

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK