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

目录

紧急电话的流程

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

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

对比log分析


紧急电话的流程

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

ACTION_CALL和ACTION_CALL_EMERGENCY

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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方法

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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卡的情况拨打紧急电话

飞行模式下拨打紧急号码

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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拨打紧急打电话的差别

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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紧急号码拨号流程紧急电话内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部