Android L 5.0 上紧急电话EmergencyCall与普通电话在MO流程上的区别

转载请注明出处:http://blog.csdn.net/aaa111/article/details/47296721

拨打紧急号码的流程,以112为例。

参考了有SIM卡,无SIM卡,和有SIM卡但开启飞行模式的log,将分析结果简单整理如下。
首先紧急电话的拨打流程也是一个MO流程,至于相对于普通号码在流程细节上有几处区别,那么我们以普通MO流程为参考,着重讲一下不同之处。 下面是一个MO的流程图:

在MO流程中call action有三种,我们先看一下他们分别用在什么时候,在NewOutgoingCallIntentBroadcaster.java中processIntent()方法前的注释中可以看到:
    
    
/**
* Processes the supplied intent and starts the outgoing call broadcast process relevant to the
* intent.
*
* This method will handle three kinds of actions:
*
* - CALL (intent launched by all third party dialers)
* - CALL_PRIVILEGED (intent launched by system apps e.g. system Dialer, voice Dialer)
* - CALL_EMERGENCY (intent launched by lock screen emergency dialer)
*
* @return {@link CallActivity#OUTGOING_CALL_SUCCEEDED} if the call succeeded, and an
* appropriate {@link DisconnectCause} if the call did not, describing why it failed.
*/

CALL为第三方的拨号程序,CALL_PRIVILEGED是系统拨号程序,CALL_EMERGENCY是锁屏界面的emergency 拨号程序。

但是在系统拨号程序中拨入号码,系统是如何是辨认emergency number的呢?
往方法里面可以看到,代码中通过isPotentialEmergencyNumber()判断这个是不是Emergency Number (注:是否是Emergency Number 是跟SIM卡以及国家有关的,好像是在PhoneNumberUtil中) ,如果是的话就去重写Intent 的Action值,
android/packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java     processIntent()
     
     
final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);
 
rewriteCallIntentAction(intent, isPotentialEmergencyNumber);//紧急号码的话 重写action值为android.intent.action.CALL_EMERGENCY了

在isPotentialEmergencyNumber()方法前的注释中,我们可以看到, “in order to enforce the restriction  that only the CALL_PRIVILEGED and CALL_EMERGENCY intents are allowed to make emergency   calls.”
那我们是否得出结论第三方的拨号程序是不允许拨打紧急电话的?( five minutes later)试了某第三方拨号程序,拨打112的时候会跳转到系统dialer,不会直接拨出电话。
rewriteCallIntentAction()方法中  updating action from CALL_PRIVILEGED to android.intent.action.CALL_EMERGENCY”

11 android/packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
     
     
void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
int videoState) {
if (call == null) {
// don't do anything if the call no longer exists
Log.i(this, "Canceling unknown call.");
return;
}
 
final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
 
if (gatewayInfo == null) {
Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
} else {
Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
Log.pii(uriHandle), Log.pii(handle));
}
 
call.setHandle(uriHandle);
call.setGatewayInfo(gatewayInfo);
call.setStartWithSpeakerphoneOn(speakerphoneOn);
call.setVideoState(videoState);
 
boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
call.getHandle());
if (isEmergencyCall) {
// Emergency -- CreateConnectionProcessor will choose accounts automatically
call.setTargetPhoneAccount(null);
}
 
if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
if (!isEmergencyCall) {
updateLchStatus(call.getTargetPhoneAccount().getId());
}
// If the account has been set, proceed to place the outgoing call.
// Otherwise the connection will be initiated when the account is set by the user.
call.startCreateConnection(mPhoneAccountRegistrar);
}
}

13 android/packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java

     
     
void process() {
Log.v(this, "process");
mAttemptRecords = new ArrayList<>();
if (mCall.getTargetPhoneAccount() != null) {
mAttemptRecords.add(new CallAttemptRecord(
mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
}
adjustAttemptsForConnectionManager();
adjustAttemptsForEmergency();
mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();
}
14  如果是一般的号码那么执行adjustAttemptsForEmergency()并不会对流程有什么影响, 如果是紧急号码的话那么我们进来看看它做了什么。
     
     
// If we are possibly attempting to call a local emergency number, ensure that the
// plain PSTN connection services are listed, and nothing else.
private void adjustAttemptsForEmergency() {
if (TelephonyUtil.shouldProcessAsEmergency(mContext, mCall.getHandle())) {
Log.i(this, "Emergency number detected");
mAttemptRecords.clear();
List<PhoneAccount> allAccounts = mPhoneAccountRegistrar.getAllPhoneAccounts();//获得所有账户(SIM/SIP/IMS等)
 
if (allAccounts.isEmpty()) {//如果是空的话
// If the list of phone accounts is empty at this point, it means Telephony hasn't
// registered any phone accounts yet. Add a fallback emergency phone account so
// that emergency calls can still go through. We create a new ArrayLists here just
// in case the implementation of PhoneAccountRegistrar ever returns an unmodifiable
// list.
allAccounts = new ArrayList<PhoneAccount>();
allAccounts.add(TelephonyUtil.getDefaultEmergencyPhoneAccount()); //如果是空的话,那么我么赋予一个默认的紧急拨号账户
}
 
 
// First, add SIM phone accounts which can place emergency calls.
for (PhoneAccount phoneAccount : allAccounts) {
if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) &&
phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
Log.i(this, "Will try PSTN account %s for emergency",     //PSTN?
phoneAccount.getAccountHandle());
mAttemptRecords.add(
new CallAttemptRecord(
phoneAccount.getAccountHandle(),
phoneAccount.getAccountHandle()));
}
}
 
// Next, add the connection manager account as a backup if it can place emergency calls.
PhoneAccountHandle callManagerHandle = mPhoneAccountRegistrar.getSimCallManager();
if (callManagerHandle != null) {
PhoneAccount callManager = mPhoneAccountRegistrar
.getPhoneAccount(callManagerHandle);
if (callManager.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManagerHandle,
mPhoneAccountRegistrar.
getDefaultOutgoingPhoneAccount(mCall.getHandle().getScheme())
);
 
if (!mAttemptRecords.contains(callAttemptRecord)) {
Log.i(this, "Will try Connection Manager account %s for emergency",
callManager);
mAttemptRecords.add(callAttemptRecord);
}
}
}
}
}
allAccounts . add ( TelephonyUtil . getDefaultEmergencyPhoneAccount ());如果phone Account是空的(没有SIM/SIP等),那么则返回一个专用的EmergencyPhoneAccount
     
     
/**
* @return fallback {@link PhoneAccount} to be used by Telecom for emergency calls in the
* rare case that Telephony has not registered any phone accounts yet. Details about this
* account are not expected to be displayed in the UI, so the description, etc are not
* populated.
*/
static PhoneAccount getDefaultEmergencyPhoneAccount() {
return PhoneAccount.builder(DEFAULT_EMERGENCY_PHONE_ACCOUNT_HANDLE, "E")
.setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
PhoneAccount.CAPABILITY_CALL_PROVIDER |
PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS).build();
}
这个CAPABILITY_SIM_SUBSCRIPTION比较奇特啊, a built-in PSTN SIM  subscription,一个内置的SIM账户?
    
    
/**
* Flag indicating that this {@code PhoneAccount} represents a built-in PSTN SIM
* subscription.
* <p>
* Only the Android framework can register a {@code PhoneAccount} having this capability.
* <p>
* See {@link #getCapabilities}
*/
public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;

下面是几种情况下拨打112 ,phoneAccount.getAccountHandle());打印出来的结果多是不同的,具体后面的 5 E 3 2 代表的什么意思我也不知道。了解的大神,求解释!求补充!
      
      
C:\Users\admin\Desktop\MO_Log\112 with simcard.txt (3 hits)
07-23 13:04:52.020 4146-4146/com.android.server.telecom I/Telecom﹕ Call: setTargetPhoneAccount
ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, 5
C:\Users\admin\Desktop\MO_Log\112 with no sim card.txt (4 hits)
07-23 13:06:06.409 4146-4146/com.android.server.telecom I/Telecom﹕ Call: setTargetPhoneAccount
ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, E (这个稍后会变成-1)
C:\Users\admin\Desktop\MO_Log\112 airplane mode.txt (3 hits)
07-23 13:08:57.877 4146-4146/com.android.server.telecom I/Telecom﹕ Call: setTargetPhoneAccount
ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, 3
C:\Users\admin\Desktop\MO_Log\MO.txt (1 hit)
04-04 14:25:02.677: I/Telecom(4147): Call: setTargetPhoneAccount
ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, 2

15 之后继续  attemptNextPhoneAccount

如果已经开启飞行模式会关闭飞行模式先 log:
    
    
770: 07-23 13:08:58.351 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: EmergencyCallHelper constructor.
770: 07-23 13:08:58.351 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: EmergencyCallHelper constructor.
772: 07-23 13:08:58.352 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: startTurnOnRadioSequence
788: 07-23 13:08:58.358 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: startSequenceInternal()
790: 07-23 13:08:58.358 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: cleanup()
794: 07-23 13:08:58.358 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: powerOnRadio().
798: 07-23 13:08:58.359 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: ==> Turning off airplane mode.
1720: 07-23 13:08:58.787 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: onServiceStateChanged(), new state = 1 1 home null null null Unknown Unknown CSS not supported -1 -1 RoamInd=-1 DefRoamInd=-1 EmergOnly=false.
1722: 07-23 13:08:58.787 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: onServiceStateChanged: not ready to call yet, keep waiting.
1976: 07-23 13:08:59.019 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: onServiceStateChanged(), new state = 0 1 home CHN-UNICOM UNICOM 46001 GSM Unknown CSS supported -1 -1 RoamInd=-1 DefRoamInd=-1 EmergOnly=false.
1978: 07-23 13:08:59.020 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: onServiceStateChanged: ok to call!
2160: 07-23 13:08:59.092 4171-4171/com.android.phone D/Telephony EmergencyCallHelper: cleanup()
具体流程不详述,打开飞行模式的入口代码如下,并且打开radio之后,同样执行了placeOutgoingConnection方法。
25 android/packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java
    
    
@Override
public Connection onCreateOutgoingConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
final ConnectionRequest request) {
...
boolean useEmergencyCallHelper = false;
if (isEmergencyNumber) {
mRequest = request;
if (state == ServiceState.STATE_POWER_OFF) {//如果radio没有开
useEmergencyCallHelper = true;
}
}
...
if (useEmergencyCallHelper) {
if (mEmergencyCallHelper == null) {
mEmergencyCallHelper = new EmergencyCallHelper(this);
}
mEmergencyCallHelper.startTurnOnRadioSequence(phone,//打开radio,进而如果是开启了飞行模式的话则关闭飞行模式
new EmergencyCallHelper.Callback() {
@Override
public void onComplete(boolean isRadioReady) {
if (connection.getState() == Connection.STATE_DISCONNECTED) {
// If the connection has already been disconnected, do nothing.
} else if (isRadioReady) {
connection.setInitialized();
placeOutgoingConnection(connection,//打开radio后
PhoneFactory.getPhone(getPhoneIdForECall()), request);
} else {
Log.d(this, "onCreateOutgoingConnection, failed to turn on radio");
connection.setDisconnected(
DisconnectCauseUtil.toTelecomDisconnectCause(
android.telephony.DisconnectCause.POWER_OFF,
"Failed to turn on radio."));
connection.destroy();
}
}
});
}else {
placeOutgoingConnection(connection, phone, request);//如果radio本来就可用的话
}
}
而紧急号码和非紧急号码之间的区别,在 placeOutgoingConnection()中的第二个参数,Phone
(飞行模式的时候会打印两次?TelephonyConnectionService: Voice phoneId in service = 0 preferred phoneId =0)
26  android/packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java
    
    
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, ConnectionRequest request) {
String number = connection.getAddress().getSchemeSpecificPart();
 
PhoneAccountHandle pHandle = TelecomAccountRegistry.makePstnPhoneAccountHandle(phone);
// For ECall handling on MSIM, till the request reaches here(i.e PhoneApp)
// we dont know on which phone account ECall can be placed, once after deciding
// the phone account for ECall we should inform Telecomm so that
// the proper sub information will be displayed on InCallUI.
if (!Objects.equals(pHandle, request.getAccountHandle())) {
Log.i(this, "setPhoneAccountHandle, account = " + pHandle);
connection.setPhoneAccountHandle(pHandle);
}
Bundle bundle = request.getExtras();
boolean isAddParticipant = (bundle != null) && bundle
.getBoolean(TelephonyProperties.ADD_PARTICIPANT_KEY, false);
Log.d(this, "placeOutgoingConnection isAddParticipant = " + isAddParticipant);
 
com.android.internal.telephony.Connection originalConnection;
try {
if (isAddParticipant) {
phone.addParticipant(number);
return;
} else {
originalConnection = phone.dial(number, request.getVideoState(), bundle);     // 拨号
}
} catch (CallStateException e) {
Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
android.telephony.DisconnectCause.OUTGOING_FAILURE,
e.getMessage()));
return;
}
 
if (originalConnection == null) {
int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
// On GSM phones, null connection means that we dialed an MMI code
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
Log.d(this, "dialed MMI code");
telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
final Intent intent = new Intent(this, MMIDialogActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
Log.d(this, "placeOutgoingConnection, phone.dial returned null");
connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
telephonyDisconnectCause, "Connection is null"));
} else {
connection.setOriginalConnection(originalConnection);
}
}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值