我是靠谱客的博主 义气西装,最近开发中收集的这篇文章主要介绍android9.0紧急号码拨号流程紧急电话的流程针对紧急电话的几种特殊情况代码处理拨打紧急电话过程中Call的状态变化对比log分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

紧急电话的流程

针对紧急电话的几种特殊情况代码处理

拨打紧急电话过程中Call的状态变化

对比log分析


紧急电话的流程

针对紧急电话的几种特殊情况代码处理

ACTION_CALL和ACTION_CALL_EMERGENCY

在MO流程中call action有三种,在NewOutgoingCallIntentBroadcaster中对三种action进行了处理,我们先从源码的注释中先看一下他们分别用在什么时候

181
/**
182
* Processes the supplied intent and starts the outgoing call broadcast process relevant to the
183
* intent.
184
*
185
* This method will handle three kinds of actions:
186
*
187
* - CALL (intent launched by all third party dialers)
188
* - CALL_PRIVILEGED (intent launched by system apps e.g. system Dialer, voice Dialer)
189
* - CALL_EMERGENCY (intent launched by lock screen emergency dialer)
190
*
191
* @return {@link DisconnectCause#NOT_DISCONNECTED} if the call succeeded, and an appropriate
192
*
{@link DisconnectCause} if the call did not, describing why it failed.
193
*/

ACTION_CALL:所有第三方应用拨号发出的action

ACTION_CALL_PRIVILEGED:系统app发出的action

ACTION_CALL_EMERGENCY:在锁屏界面点击紧急拨号出现的拨号盘发出的action

 

实际上一般系统拨号盘发出的action是ACTION_CALL,锁屏界面紧急拨号的action是ACTION_CALL_EMERGENCY。我们看下代码是如何针对两者处理的:

/packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

195
public int processIntent() {
......
240
boolean callImmediately = false;
......
261
final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
....
267
if (Intent.ACTION_CALL.equals(action)) {
268
if (isPotentialEmergencyNumber) {
//如果是第三方应用播出的紧急号码,就会跳转到系统拨号盘拨号
269
if (!mIsDefaultOrSystemPhoneApp) {
270
Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
271
+ "unless caller is system or default dialer.", number, intent);
272
launchSystemDialer(intent.getData());
273
return DisconnectCause.OUTGOING_CANCELED;
274
} else {
275
callImmediately = true;
276
}
277
}
278
} else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
279
if (!isPotentialEmergencyNumber) {
280
Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL "
281
+ "Intent %s.", number, intent);
282
return DisconnectCause.OUTGOING_CANCELED;
283
}
284
callImmediately = true;
285
} else {
286
Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
287
return DisconnectCause.INVALID_NUMBER;
288
}
......
//可以看到,系统应用的拨号更快,直接调用placeOutgoingCallImmediately,而普通拨号还需要发送广播
300
if (callImmediately) {
301
boolean speakerphoneOn = mIntent.getBooleanExtra(
302
TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
303
int videoState = mIntent.getIntExtra(
304
TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
305
VideoProfile.STATE_AUDIO_ONLY);
306
placeOutgoingCallImmediately(mCall, callingAddress, null,
307
speakerphoneOn, videoState);
308
309
// Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
310
// so that third parties can still inspect (but not intercept) the outgoing call. When
311
// the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to
312
// initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
313
}
314
315
if (sendNewOutgoingCallBroadcast) {
316
UserHandle targetUser = mCall.getInitiatingUser();
317
Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
318
broadcastIntent(intent, number, !callImmediately, targetUser);
319
}
......

这段代码主要有两点:

  1. 第三方应用拨打紧急号码,会跳转到拨号盘
  2. 紧急电话的拨号流程更简单,会省略很多步骤

判断号码是不是紧急号码isPotentialEmergencyNumber

紧急通话流程走到类NewOutgoingCallIntentBroadcaster的processIntent方法中,主要是通过isPotentialEmergencyNumber判断了号码是不是紧急号码。我们跟踪这个方法到PhoneNumberUtils类的isEmergencyNumberInternal方法

1971
private static boolean isEmergencyNumberInternal(int subId, String number,
1972
String defaultCountryIso,
1973
boolean useExactMatch) {
1974
// If the number passed in is null, just return false:
1975
if (number == null) return false;
1976
1977
// If the number passed in is a SIP address, return false, since the
1978
// concept of "emergency numbers" is only meaningful for calls placed
1979
// over the cell network.
1980
// (Be sure to do this check *before* calling extractNetworkPortionAlt(),
1981
// since the whole point of extractNetworkPortionAlt() is to filter out
1982
// any non-dialable characters (which would turn 'abc911def@example.com'
1983
// into '911', for example.))
1984
if (isUriNumber(number)) {
1985
return false;
1986
}
1987
1988
// Strip the separators from the number before comparing it
1989
// to the list.
1990
number = extractNetworkPortionAlt(number);
1991
1992
String emergencyNumbers = "";
1993
int slotId = SubscriptionManager.getSlotIndex(subId);
1994
1995
// retrieve the list of emergency numbers
1996
// check read-write ecclist property first
1997
String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
1998
1999
emergencyNumbers = SystemProperties.get(ecclist, "");
2000
2001
Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
2002
+ defaultCountryIso + " emergencyNumbers: " +
emergencyNumbers);
2003
2004
if (TextUtils.isEmpty(emergencyNumbers)) {
2005
// then read-only ecclist property since old RIL only uses this
2006
emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
2007
}
2008
2009
if (!TextUtils.isEmpty(emergencyNumbers)) {
2010
// searches through the comma-separated list for a match,
2011
// return true if one is found.
2012
for (String emergencyNum : emergencyNumbers.split(",")) {
2013
// It is not possible to append additional digits to an emergency number to dial
2014
// the number in Brazil - it won't connect.
2015
if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
2016
if (number.equals(emergencyNum)) {
2017
return true;
2018
}
2019
} else {
2020
if (number.startsWith(emergencyNum)) {
2021
return true;
2022
}
2023
}
2024
}
2025
// no matches found against the list!
2026
return false;
2027
}
2028
2029
Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
2030
+ " Use embedded logic for determining ones.");
2031
2032
// If slot id is invalid, means that there is no sim card.
2033
// According spec 3GPP TS22.101, the following numbers should be
2034
// ECC numbers when SIM/USIM is not present.
2035
emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
2036
2037
for (String emergencyNum : emergencyNumbers.split(",")) {
2038
if (useExactMatch) {
2039
if (number.equals(emergencyNum)) {
2040
return true;
2041
}
2042
} else {
2043
if (number.startsWith(emergencyNum)) {
2044
return true;
2045
}
2046
}
2047
}
2048
2049
// No ecclist system property, so use our own list.
2050
if (defaultCountryIso != null) {
2051
ShortNumberInfo info = ShortNumberInfo.getInstance();
2052
if (useExactMatch) {
2053
return info.isEmergencyNumber(number, defaultCountryIso);
2054
} else {
2055
return info.connectsToEmergencyNumber(number, defaultCountryIso);
2056
}
2057
}
2058
2059
return false;
2060
}

 

在此函数中会从4个来源读取紧急号码: 

1)SIM卡(ril.ecclist, ril.ecclist2) 。

读取的是存储在SIM卡中的紧急号码,仅当插了SIM卡的情况下才可能读出数据。无法向此property中写入数据,因为每次开机都会重新改写此property。 

TODO//Log中打印的号码为

2) RILD(ro.ril.ecclist)。

这是一个只读的property,里面写入的紧急号码为112和911,此property内容无法更改。 

TODO//Log中打印的号码为

3)数组(emergencyNumList)

有卡情况下数组为"112,911",无卡"112,911,000,08,110,118,119,999"。如果需要添加紧急号码,可以写入此数组。 

4)根据国家码读取文件

这个目录下不同的国家码与文件相对应,从文件中读取紧急拨号的号码

/external/libphonenumber/libphonenumber/src/com/google/i18n/phonenumbers/data/

例如中国

//TODO 看log 拨打120试试

有sim卡和无sim卡的情况拨打紧急电话

飞行模式下拨打紧急号码

如果打开了飞行模式拨打紧急电话,会关闭再去拨打


269
public Connection onCreateOutgoingConnection(
270
PhoneAccountHandle connectionManagerPhoneAccount,
271
final ConnectionRequest request) {
272
Log.i(this, "onCreateOutgoingConnection, request: " + request);
.........
367
368
final boolean isEmergencyNumber =
369
PhoneNumberUtils.isLocalEmergencyNumber(this, numberToDial);
370
371
372
final boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
373
Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
374
//如果飞行模式打开,这个值为true
375
boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
376
|| isRadioPowerDownOnBluetooth();
377
378
if (needToTurnOnRadio) {
379
final Uri resultHandle = handle;
380
// By default, Connection based on the default Phone, since we need to return to Telecom
381
// now.
382
final int originalPhoneType = PhoneFactory.getDefaultPhone().getPhoneType();
383
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
384
isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone());
385
if (mRadioOnHelper == null) {
386
mRadioOnHelper = new RadioOnHelper(this);
387
}
//尝试去关闭飞行模式
388
mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
389
@Override
//关闭成功后回调,继续拨打紧急电话
390
public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
391
handleOnComplete(isRadioReady, isEmergencyNumber, resultConnection, request,
392
numberToDial, resultHandle, originalPhoneType);
393
}
394
395
@Override
396
public boolean isOkToCall(Phone phone, int serviceState) {
397
if (isEmergencyNumber) {
398
// We currently only look to make sure that the radio is on before dialing.
399
// We should be able to make emergency calls at any time after the radio has
400
// been powered on and isn't in the UNAVAILABLE state, even if it is
401
// reporting the OUT_OF_SERVICE state.
402
return (phone.getState() == PhoneConstants.State.OFFHOOK)
403
|| phone.getServiceStateTracker().isRadioOn();
404
} else {
405
// It is not an emergency number, so wait until we are in service and ready
406
// to make calls. This can happen when we power down the radio on bluetooth
407
// to save power on watches.
408
return (phone.getState() == PhoneConstants.State.OFFHOOK)
409
|| serviceState == ServiceState.STATE_IN_SERVICE;
410
}
411
}
412
});
413
// Return the still unconnected GsmConnection and wait for the Radios to boot before
414
// connecting it to the underlying Phone.
415
return resultConnection;
416
} else {
417
if (!canAddCall() && !isEmergencyNumber) {
418
Log.d(this, "onCreateOutgoingConnection, cannot add call .");
419
return Connection.createFailedConnection(
420
new DisconnectCause(DisconnectCause.ERROR,
421
getApplicationContext().getText(
422
R.string.incall_error_cannot_add_call),
423
getApplicationContext().getText(
424
R.string.incall_error_cannot_add_call),
425
"Add call restricted due to ongoing video call"));
426
}
427
428
// Get the right phone object from the account data passed in.
429
final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
430
Connection resultConnection = getTelephonyConnection(request, numberToDial,
431
isEmergencyNumber, handle, phone);
432
// If there was a failure, the resulting connection will not be a TelephonyConnection,
433
// so don't place the call!
434
if (resultConnection instanceof TelephonyConnection) {
435
if (request.getExtras() != null && request.getExtras().getBoolean(
436
TelecomManager.EXTRA_USE_ASSISTED_DIALING, false)) {
437
((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);
438
}
439
placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
440
}
441
return resultConnection;
442
}
443
}

这边就是对飞行模式下拨打紧急电话进行处理,尝试关闭飞行模式再拨打电话

Gsm和Cdma拨打紧急打电话的差别

382
private Connection dial(String dialString, int clirMode) throws CallStateException {
383
// note that this triggers call state changed notif
384
clearDisconnected();
385
386
if (!canDial()) {
387
throw new CallStateException("cannot dial in current state");
388
}
389
390
TelephonyManager tm =
391
(TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
392
String origNumber = dialString;
393
String operatorIsoContry = tm.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
394
String simIsoContry = tm.getSimCountryIsoForPhone(mPhone.getPhoneId());
395
boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry)
396
&& !TextUtils.isEmpty(simIsoContry)
397
&& !simIsoContry.equals(operatorIsoContry);
398
if (internationalRoaming) {
399
if ("us".equals(simIsoContry)) {
400
internationalRoaming = internationalRoaming && !"vi".equals(operatorIsoContry);
401
} else if ("vi".equals(simIsoContry)) {
402
internationalRoaming = internationalRoaming && !"us".equals(operatorIsoContry);
403
}
404
}
405
if (internationalRoaming) {
406
dialString = convertNumberIfNecessary(mPhone, dialString);
407
}
408
409
boolean isPhoneInEcmMode = mPhone.isInEcm();
//Cdma和Gsm验证紧急电话方式不一样
410
boolean isEmergencyCall =
411
PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
412
413
// Cancel Ecm timer if a second emergency call is originating in Ecm mode
//在Ecm模式(锁屏界面拨打紧急电话)下拨打紧急电话,不显示通话时间栏
414
if (isPhoneInEcmMode && isEmergencyCall) {
415
handleEcmTimer(GsmCdmaPhone.CANCEL_ECM_TIMER);
416
}
417
418
// The new call must be assigned to the foreground call.
419
// That call must be idle, so place anything that's
420
// there on hold
421
if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) {
422
return dialThreeWay(dialString);
423
}
424
425
mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
426
this, mForegroundCall, isEmergencyCall);
427
mHangupPendingMO = false;
428
429
if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
430
|| mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) {
431
// Phone number is invalid
432
mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
433
434
// handlePollCalls() will notice this call not present
435
// and will mark it as dropped.
436
pollCallsWhenSafe();
437
} else {
438
// Always unmute when initiating a new call
439
setMute(false);
440
441
// Check data call
//Cdma拨打紧急电话会关闭数据连接
442
disableDataCallInEmergencyCall(dialString);
443
444
// In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
//Cdma只能在紧急模式下拨打紧急号码
445
if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
446
mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
447
} else {
448
mPhone.exitEmergencyCallbackMode();
449
mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
450
mPendingCallClirMode=clirMode;
451
mPendingCallInEcm=true;
452
}
453
}
454
455
if (mNumberConverted) {
456
mPendingMO.setConverted(origNumber);
457
mNumberConverted = false;
458
}
459
460
updatePhoneState();
461
mPhone.notifyPreciseCallStateChanged();
462
463
return mPendingMO;
464
}

总结一下:cdma拨打紧急电话有以下几点

Cdma和Gsm验证紧急电话方式不一样

//TODO,等找到CDMA手机再去验证一下

Cdma只能在在Ecm模式(锁屏界面拨打紧急电话)下拨打紧急号码

cdma拨打紧急电话,不显示通话时间栏

Cdma拨打紧急电话会关闭数据连接

拨打紧急电话过程中Call的状态变化

对比log分析

 

 

最后

以上就是义气西装为你收集整理的android9.0紧急号码拨号流程紧急电话的流程针对紧急电话的几种特殊情况代码处理拨打紧急电话过程中Call的状态变化对比log分析的全部内容,希望文章能够帮你解决android9.0紧急号码拨号流程紧急电话的流程针对紧急电话的几种特殊情况代码处理拨打紧急电话过程中Call的状态变化对比log分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部