中午午休时,我把手机开飞行模式了,能看到 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})@SystemApipublic void setAirplaneMode(boolean enable) { try { mService.setAirplaneMode(enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }}
mService 是 IConnectivityManager,是个接口,其实现看 ConnectivityService#setAirplaneMode。
ConnectivityService#setAirplaneMode
/AOSP/frameworks/base/services/core/java/com/android/server
@Overridepublic void setAirplaneMode(boolean enable) { enforceNetworkStackSettingsOrSetup(); 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); }}
这里设置了飞行模式状态的系统变量以及发送 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.conta×××(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, 1); 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 当点击飞行模式都发生了什么?”流程有了大致了解
品略图书馆 http://www.pinlue.com/
转载于:https://blog.51cto.com/14325182/2408985