原创不得转载,你转载了请别去掉我的名字:W歹匕示申W
否则举报你思想强奸!
1:搜索菜单按钮:
---- search_shortcut_make_video_call Matches (155 in 155 files) ----
DialerPhoneNumberListAdapter.java (y:\linux\android\packages\apps\dialer\src\com\android\dialer\list): text = resources.getString(R.string.search_shortcut_make_video_call);
Strings.xml (y:\linux\android\packages\apps\dialer\res\values):
Make video call
查看菜单功能:
case SHORTCUT_MAKE_VIDEO_CALL:
text = resources.getString(R.string.search_shortcut_make_video_call);
drawableId = R.drawable.ic_videocam;
break;
2:搜索SHORTCUT_MAKE_VIDEO_CALL调用
---- SHORTCUT_MAKE_VIDEO_CALL Matches (5 in 4 files) ----
SearchFragment.java (y:\linux\android\packages\apps\dialer\src\com\android\dialer\list): case DialerPhoneNumberListAdapter.SHORTCUT_MAKE_VIDEO_CALL:
搜索号码获取:
case DialerPhoneNumberListAdapter.SHORTCUT_MAKE_VIDEO_CALL:
number = TextUtils.isEmpty(mAddToContactNumber) ?
adapter.getQueryString() : mAddToContactNumber;
listener = getOnPhoneNumberPickerListener();
if (listener != null && !checkForProhibitedPhoneNumber(number)) {
listener.onCallNumberDirectly(number, true /* isVideoCall */);
}
break;
2.1:查看mAddToContactNumber:
public void setAddToContactNumber(String addToContactNumber) {
mAddToContactNumber = addToContactNumber;
}
搜索setAddToContactNumber:
@Override
public void onDialpadQueryChanged(String query) {
if (mSmartDialSearchFragment != null) {
mSmartDialSearchFragment.setAddToContactNumber(query);
}
搜索onDialpadQueryChanged调用:
public interface OnDialpadQueryChangedListener {
void onDialpadQueryChanged(String query);
}
搜索接口:OnDialpadQueryChangedListener
private OnDialpadQueryChangedListener mDialpadQueryListener;
搜索mDialpadQueryListener
if (mDialpadQueryListener != null) {
mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());----拨号号码获取
}
本文件搜索mDigits
2.2 :查看getOnPhoneNumberPickerListener:
---- getOnPhoneNumberPickerListener Matches (3 in 2 files) ----
PhoneNumberPickerFragment.java (y:\linux\android\packages\apps\contactscommon\src\com\android\contacts\common\list): public OnPhoneNumberPickerActionListener getOnPhoneNumberPickerListener() {
public OnPhoneNumberPickerActionListener getOnPhoneNumberPickerListener() {
return mListener;
}
查看OnPhoneNumberPickerActionListener监听器
/**
* Calls the specified phone number audio call.
*/
void onCallNumberDirectly(String phoneNumber);
搜索onCallNumberDirectly
DialtactsActivity.java (y:\linux\android\packages\apps\dialer\src\com\android\dialer): public void onCallNumberDirectly(String phoneNumber) {
@Override
public void onCallNumberDirectly(String phoneNumber) {
onCallNumberDirectly(phoneNumber, false /* isVideoCall */);
}
本文搜索onCallNumberDirectly
@Override
public void onCallNumberDirectly(String phoneNumber, boolean isVideoCall) {----------拨打视频电话,仔细分析判断
查看:DialerUtils.startActivityWithErrorToast(this, intent);
public static void startActivityWithErrorToast(Context context, Intent intent) {
startActivityWithErrorToast(context, intent, R.string.activity_not_available);
}
查看:
startActivityWithErrorToast
这里补充小知识:context instanceof Activity:他是java里面的二元运算符,判断左边的对象是否是右边类的实例。假如是的话,返回true;假如不是的话,返回false。
public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
try {
if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
&& context instanceof Activity)) {
// All dialer-initiated calls should pass the touch point to the InCallUI
Point touchPoint = TouchPointManager.getInstance().getPoint();
if (touchPoint.x != 0 || touchPoint.y != 0) {
Bundle extras = new Bundle();
extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
}
final TelecomManager tm =
(TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
tm.placeCall(intent.getData(), intent.getExtras());---发生服务播出号码,服务启动跳转到正在拨号界面
} else {
context.startActivity(intent);-----返回的是拨号界面
}
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();----异常提示,异常类型msgId提示语,
}
}
2.3搜索服务:TelecomManager,
placeCall的函数
TelecomManager.java (y:\linux\android\frameworks\base\telecomm\java\android\telecom): public void placeCall(Uri address, Bundle extras) {
TelecomServiceImpl.java (y:\linux\android\packages\services\telecomm\src\com\android\server\telecom): public void placeCall(Uri handle, Bundle extras, String callingPackage) {
查看TelecomServiceImpl.java服务类功能placeCall的函数
@Override
public void placeCall(Uri handle, Bundle extras, String callingPackage) {
final Intent intent = new Intent(Intent.ACTION_CALL, handle);
intent.putExtras(extras);
new UserCallIntentProcessor(mContext, userHandle).processIntent(intent,
callingPackage, hasCallAppOp && hasCallPermission);
查看UserCallIntentProcessor类的processIntent方法
public void processIntent(Intent intent, String callingPackageName,
--》processOutgoingCallIntent--》sendBroadcastToReceiver--》
private boolean sendBroadcastToReceiver(Intent intent) {
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setClass(mContext, PrimaryCallReceiver.class);
Log.d(this, "Sending broadcast as user to CallReceiver");
mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
return true;
}
前面N个return,没有打出去电话的自己打印LOG看跑了哪。
查看sendBroadcastToReceiver(intent);服务的发送。。。。。。。。。
private boolean sendBroadcastToReceiver(Intent intent) {
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setClass(mContext, PrimaryCallReceiver.class);
Log.d(this, "Sending broadcast as user to CallReceiver");
mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
return true;
}继续查看PrimaryCallReceiver.class
public class PrimaryCallReceiver extends BroadcastReceiver implements TelecomSystem.Component {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (getTelecomSystem().getLock()) {
getTelecomSystem().getCallIntentProcessor().processIntent(intent);
}
}继续查看getCallIntentProcessor
public CallIntentProcessor getCallIntentProcessor() {
return mCallIntentProcessor;
}继续本文搜索mCallIntentProcessor
mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
继续查看CallIntentProcessor类中的processIntent
public void processIntent(Intent intent) {
final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);----------isUnknownCall打印LOG=false
Trace.beginSection("processNewCallCallIntent");
if (isUnknownCall) {
processUnknownCallIntent(mCallsManager, intent);
} else {
processOutgoingCallIntent(mContext, mCallsManager, intent);-------进入这里
}继续查看processOutgoingCallIntent方法
注意看LOG打印分析方法中跑的位置
LOG打印:
06-30 11:59:17.547 I/Telecom (13438): CallIntentProcessor: onReceive - isUnknownCall: false
06-30 11:59:17.548 D/Telecom (13438): Class: isSkipSchemaParsing = false
06-30 11:59:17.548 D/Telecom (13438): Class: isConferenceUri = false
06-30 11:59:17.548 D/Telecom (13438): Class: isAddparticipant = false
06-30 11:59:17.549 D/Telecom (13438): Class: callDomain = 0
06-30 11:59:17.549 D/Telecom (13438): Class: processOutgoingCallIntent callPull = false
06-30 11:59:17.550 I/Telecom (13438): Class: processOutgoingCallIntent handle = tel:%2B917021095908,scheme = tel, uriString = +917021095908, isSkipSchemaParsing = false, isAddParticipant = false, isCallPull = false
最终走入:
发送到呼叫管理器,以确保InCallUI广播返回之前被拉开序幕:
// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);
继续查看startOutgoingCall
Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
针对IMS的conf URI力tel架构/跳过模式调用来避免SIP帐号的选择:
// Force tel scheme for ims conf uri/skip schema calls to avoid selection of sip accounts
String scheme = (isSkipSchemaOrConfUri? PhoneAccount.SCHEME_TEL: handle.getScheme());
注意LOG打印的3个值:
06-30 11:59:42.140 D/Telecom (13438): CallsManager: startOutgoingCall :: isAddParticipant=false isSkipSchemaOrConfUri=false scheme=tel
再看下面代码:
List
accounts = null;
if (VideoProfile.isVideo(call.getVideoState())) {
accounts = mPhoneAccountRegistrar.getVideoCallCapablePhoneAccounts(scheme, false);
} else {
accounts = mPhoneAccountRegistrar.getCallCapablePhoneAccounts(scheme, false);
}
Log.v(this, "startOutgoingCall found accounts = " + accounts);
打印结果:
06-30 11:59:42.141 V/Telecom (13438): CallsManager: startOutgoingCall found accounts = [ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, [902ba3cda1883801594b6e1b452790cc53948fda], UserHandle{0}]
继续下看,很多拨出电话前的判断和处理:
boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
//不支持任何更多的实时呼叫。我们的选择是移动通话持有,断开
//一个电话,或者干脆取消这个召唤。
// Do not support any more live calls. Our options are to move a call to hold, disconnect
// a call, or cancel this call altogether.
if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, call.isEmergencyCall())) {
// just cancel at this point.
Log.i(this, "No remaining room for outgoing call: %s", call);
if (mCalls.contains(call)) {
// This call can already exist if it is a reused call,
// See {@link #getNewOutgoingCall}.
call.disconnect();------------------------条件符合就挂断电话,不让拨出去。
}
return null;
}
继续下看:
boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
!call.isEmergencyCall();
if (needsAccountSelection) {-------------需要帐户选择
// This is the state where the user is expected to select an account
call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
// Create our own instance to modify (since extras may be Bundle.EMPTY)
extras = new Bundle(extras);
extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
} else {
call.setState(
CallState.CONNECTING,
phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
}
继续下看,低电量处理:
Bundle callExtras = new Bundle();
收拾低电量信息它是提供给InCallUI作进一步处理:
//pack low battery information for it to be available to InCallUI for further processing
callExtras.putBoolean(QtiCallConstants.LOW_BATTERY_EXTRA_KEY,
TelephonyUtil.isLowBattery(mContext));
call.setExtras(callExtras);
跳转查看TelephonyUtil.isLowBattery:
static boolean isLowBattery(Context context) {
if(!isCarrierOneSupported()) {
return false;
}
Intent batteryStatus = context.registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
final int batteryLevel = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
//determine whether device is under low battery or not based on battery level
return (batteryLevel <= (context.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel)));--------低电量提示等级config_lowBatteryWarningLevel
}
返回上一步继续查看:
// Do not add the call if it is a potential MMI code.
如果它是一个潜在的MMI代码不要添加呼叫。
if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
call.addListener(this);
} else if (!mCalls.contains(call)) {
// We check if mCalls already contains the call because we could potentially be reusing
// a call which was previously added (See {@link #getNewOutgoingCall}).
//我们检查,如果mCalls已经包含了电话,因为我们可能被重用
//这是以前添加的调用(见{@link#getNewOutgoingCall})。
addCall(call);
}
return call;
查看addCall方法:
private void addCall(Call call) {
Trace.beginSection("addCall");-------------------跟踪打印LOG
Log.v(this, "addCall(%s)", call);----------------LOG打印
打印:
06-30 11:59:17.637 V/Telecom (13438): CallsManager: addCall([147747198, CONNECTING, null, tel:%2B917021095908, A, childs(0), has_parent(false), [[Capabilities:]], false, ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, [902ba3cda1883801594b6e1b452790cc53948fda], UserHandle{0}])
call.addListener(this);
mCalls.add(call);
updateCallsManagerState();
继续查看updateCallsManagerState方法
private void updateCallsManagerState() {
updateForegroundCall();
updateCanAddCall();
}继续查看updateForegroundCall
// TODO: Foreground-ness needs to be explicitly set. No call, regardless
// of its state will be foreground by default and instead the connection service should
// be notified when its calls enter and exit foreground state. Foreground will mean that
// the call should play audio and listen to microphone if it wants.
// TODO:前景岬需要明确设置。没有呼叫,无论
//其状态会被默认前景和替代的连接服务应
//当其呼叫进入和退出前台状态//通知。前景将意味着
//调用应该播放音频和收听麦克风如果它想。
if (TelephonyManager.getDefault().getMultiSimConfiguration()
== TelephonyManager.MultiSimVariants.DSDA)
//如果活动子没有任何来电,然后再考虑在所有潜艇电话,
//这曾经打电话激活集作为前景的呼叫。给予更多的优先
//超过当前呼叫振铃的LCH子电话。
// if active sub doesn't have any calls, then consider calls on all subs,
// which ever call is active set that as foreground call. give more priority
// to ringing call on LCH sub over active call.
if (newForegroundCall == null) {
newForegroundCall = getFirstCallWithState(CallState.RINGING);
if (newForegroundCall == null) {
newForegroundCall = getFirstCallWithState(CallState.ACTIVE);
}
}
for (Call call : mCalls) {
// Only top-level calls can be in foreground只有顶级的呼叫可以在前台
if (call.getParentCall() != null) {
continue;
}
// Active calls have priority.活动呼叫享有优先权。
if (call.isActive()) {
newForegroundCall = call;
break;
}
// If only call in call list is held call it's also a foreground call如果只在通话清单通话被保持调用它也是一个前台电话
if (getNumTopLevelCalls() == 1 && call.getState() == CallState.ON_HOLD) {
newForegroundCall = call;
}
if ((call.isAlive() && call.getState() != CallState.ON_HOLD)
|| call.getState() == CallState.RINGING) {
newForegroundCall = call;
// Don't break in case there's an active call that has priority.不要在案件不破有一个活跃的呼叫优先。
}
}
/ *在当按下主动保留按键,其中有2个呼叫的情况下,
*调用,作为一个中间状态,我们就必须在之前举行的状态两个呼叫
*后台调用移动到活动状态。在这样的一个中间阶段
*更新newForegroundCall为null出现问题的原因放弃音频
*焦点。跳过更新与空在这种情况下,前台打电话。 * /
/* In the case where there are 2 calls, when the Hold button is pressed for active
* call, as an intermediate state we would have both calls in held state before
* the background call is moved to active state. In such an intermediate stage
* updating the newForegroundCall to null causes issues with abandoning audio
* focus. Skip updating the foreground call with null in such cases. */
if (newForegroundCall == null && getFirstCallWithState(CallState.ON_HOLD) != null) {
Log.v(this, "Skip updating foreground call in intermediate stage");
newForegroundCall = mForegroundCall;
}
if (newForegroundCall != mForegroundCall) {
Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall);
打印:
06-30 11:59:52.705 V/Telecom (13438): CallsManager: Updating foreground call, [147747198, ON_HOLD, com.android.phone/com.android.services.telephony.TelephonyConnectionService, tel:%2B917021095908, A, childs(0), has_parent(false), [[Capabilities: CAPABILITY_HOLD CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_SUPPORTS_VT_REMOTE_RX CAPABILITY_SUPPORTS_VT_REMOTE_TX CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL CAPABILITY_SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE CAPABILITY_HIGH_DEF_AUDIO]], false, ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, [902ba3cda1883801594b6e1b452790cc53948fda], UserHandle{0}] -> [174614848, DIALING, com.android.phone/com.android.services.telephony.TelephonyConnectionService, tel:7021263897, A, childs(0), has_parent(false), [[Capabilities: CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE]], false, ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, [902ba3cda1883801594b6e1b452790cc53948fda], UserHandle{0}].
其中有前台电话和后台电话,mForegroundCall:7021095908被保持了ON_HOLD,newForegroundCall:7021263897被推到前台拨号DIALING
Call oldForegroundCall = mForegroundCall;
mForegroundCall = newForegroundCall;
for (CallsManagerListener listener : mListeners) {
if (Log.SYSTRACE_DEBUG) {
Trace.beginSection(listener.getClass().toString() + " updateForegroundCall");
}
listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
if (Log.SYSTRACE_DEBUG) {
Trace.endSection();
}
}
}
跳出这个函数返回上一部,继续查看下一步要走的函数:updateCanAddCall
private void updateCanAddCall() {
boolean newCanAddCall = canAddCall();
if (newCanAddCall != mCanAddCall) {
mCanAddCall = newCanAddCall;
for (CallsManagerListener listener : mListeners) {
if (Log.SYSTRACE_DEBUG) {
Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
}
listener.onCanAddCallChanged(mCanAddCall);
if (Log.SYSTRACE_DEBUG) {
Trace.endSection();
}
}
}
}继续查看CallsManagerListener的onCanAddCallChanged方法,算了,不看了,谁看自己看吧,累