android.intent.action.CALL_PRIVILEGED或android.intent.action.CALL发起,这里只分析android.intent.action.CALL的情况,程序目录结构:
可以在Phone程序的AndroidManifest.xml文件中找到
<activity
android:name="OutgoingCallBroadcaster"
android:configChanges="orientation|screenSize|keyboardHidden"
android:permission="android.permission.CALL_PHONE"
android:theme="@android:style/Theme.NoDisplay" >
<!--
CALL action intent filters, for the various ways
of initiating an outgoing call.
-->
<intent-filter>
<action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
<intent-filter android:icon="@drawable/ic_launcher_sip_call" >
<action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sip" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="voicemail" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/phone" />
<data android:mimeType="vnd.android.cursor.item/phone_v2" />
<data android:mimeType="vnd.android.cursor.item/person" />
</intent-filter>
</activity>
在收到Action:
android.intent.action.CALL后会启动Activity:
OutgoingCallBroadcaster,在启动Activity之前最先会调用:
PhoneApp,因为它继承了
Application就是程序的入口
<application
android:name="PhoneApp"
android:icon="@drawable/ic_launcher_phone"
android:label="@string/phoneAppLabel"
android:persistent="true" >
</application>
关于Application类的作用主要是一些全局的初始化工作,静态对象给其它类使用;在onCreate()函数里会创建Phone phone对象,这是framework层的一个类com.android.internal.telephony.Phone,所以导入Eclipse后会报很多错误,我是在Eclipse修改后在ubuntu14.04下进行编译生成apk的。在onCreate()下有这样一段代码进行初始化
if (phone == null) {
// 初始化phone frameworks层
PhoneFactory.makeDefaultPhones(this);
// 获取默认的phone对象
phone = PhoneFactory.getDefaultPhone();
mCM = CallManager.getInstance();
mCM.registerPhone(phone);
// 创建一个的单例的 NotificationMgr对象,用来显示状态栏图标和控制其他状态栏
notificationMgr = NotificationMgr.init(this);
//是一个phone的应用层服务,ITelephony.Stub的实现
phoneMgr = PhoneInterfaceManager.init(this, phone);
// 开启Sip卡的服务
mHandler.sendEmptyMessage(EVENT_START_SIP_SERVICE);
// 获取电话的类型PHONE_TYPE_CDMA、PHONE_TYPE_GSM、PHONE_TYPE_SIP
int phoneType = phone.getPhoneType();
if (phoneType == Phone.PHONE_TYPE_CDMA) {
// Create an instance of CdmaPhoneCallState and initialize it to
// IDLE
cdmaPhoneCallState = new CdmaPhoneCallState();
cdmaPhoneCallState.CdmaPhoneCallStateInit();
}
if (BluetoothAdapter.getDefaultAdapter() != null) {
// Start BluetoothHandsree even if device is not voice capable.
// The device can still support VOIP.
// 初始化蓝牙免提对象
mBtHandsfree = BluetoothHandsfree.init(this, mCM);
// 开启一个蓝牙耳机服务
startService(new Intent(this, BluetoothHeadsetService.class));
} else {
// Device is not bluetooth capable
mBtHandsfree = null;
}
// 获取铃声对象
ringer = Ringer.init(this);
// before registering for phone state changes
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
| PowerManager.ACQUIRE_CAUSES_WAKEUP, LOG_TAG);
// lock used to keep the processor awake, when we don't care for the
// display.
mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE, LOG_TAG);
// Wake lock used to control proximity sensor behavior.
if ((pm.getSupportedWakeLockFlags() & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) != 0x0) {
mProximityWakeLock = pm.newWakeLock(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, LOG_TAG);
}
if (DBG)
Log.d(LOG_TAG, "onCreate: mProximityWakeLock: "
+ mProximityWakeLock);
// create mAccelerometerListener only if we are using the proximity
// sensor
if (proximitySensorModeEnabled()) {
mAccelerometerListener = new AccelerometerListener(this, this);
}
mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
// get a handle to the service so that we can use it later when we
// want to set the poke lock.
mPowerManagerService = IPowerManager.Stub
.asInterface(ServiceManager.getService("power"));
// Create the CallController singleton, which is the interface
// to the telephony layer for user-initiated telephony functionality
// (like making outgoing calls.)
callController = CallController.init(this);
// ...and also the InCallUiState instance, used by the
// CallController to
// keep track of some "persistent state" of the in-call UI.
inCallUiState = InCallUiState.init(this);
// Create the CallNotifer singleton, which handles
// asynchronous events from the telephony layer (like
// launching the incoming-call UI when an incoming call comes
// in.)
notifier = CallNotifier.init(this, phone, ringer, mBtHandsfree,
new CallLogAsync());
// 注册ICC的状态
IccCard sim = phone.getIccCard();
if (sim != null) {
if (VDBG)
Log.v(LOG_TAG, "register for ICC status");
sim.registerForNetworkLocked(mHandler,
EVENT_SIM_NETWORK_LOCKED, null);
}
// register for MMI/USSD
mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);
// 通过PhoneUtils跟踪CallManager
PhoneUtils.initializeConnectionHandler(mCM);
// Read platform settings for TTY feature
mTtyEnabled = getResources().getBoolean(R.bool.tty_enabled);
// 注册广播的Action
IntentFilter intentFilter = new IntentFilter(
Intent.ACTION_AIRPLANE_MODE_CHANGED);
intentFilter
.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
intentFilter
.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
intentFilter.addAction(Intent.ACTION_BATTERY_LOW);
intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intentFilter
.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
intentFilter
.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
intentFilter
.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
if (mTtyEnabled) {
intentFilter
.addAction(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION);
}
intentFilter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
registerReceiver(mReceiver, intentFilter);
// Use a separate receiver for ACTION_MEDIA_BUTTON broadcasts,
// since we need to manually adjust its priority (to make sure
// we get these intents *before* the media player.)
IntentFilter mediaButtonIntentFilter = new IntentFilter(
Intent.ACTION_MEDIA_BUTTON);
//
// Make sure we're higher priority than the media player's
// MediaButtonIntentReceiver (which currently has the default
// priority of zero; see apps/Music/AndroidManifest.xml.)
mediaButtonIntentFilter.setPriority(1);
//
registerReceiver(mMediaButtonReceiver, mediaButtonIntentFilter);
// set the default values for the preferences in the phone.
PreferenceManager.setDefaultValues(this, R.xml.network_setting,
false);
PreferenceManager.setDefaultValues(this,
R.xml.call_feature_setting, false);
// Make sure the audio mode (along with some
// audio-mode-related state of our own) is initialized
// correctly, given the current state of the phone.
PhoneUtils.setAudioMode(mCM);
}
在这个过程中获取了phone、CallController、InCallUiState、CallNotifier、NotificationMgr、Ringer、BluetoothHandsfree、PhoneInterfaceManager、CallManager等对象和动态注册广播消息。
接下来是启动Activity:OutgoingCallBroadcaster根据生命周期最先会执行onCreate函数,获取一个Intent:Intent intent = getIntent();得到以下信息Action和拨出号码:
String action = intent.getAction();
String number = PhoneNumberUtils.getNumberFromIntent(intent, this);
并判断该号码是不是紧急号码,如果是设置-->callNow = true;启动InCallScreen-->mApp.displayCallScreen();不管callNow是true或false都会发送以下广播:
sendOrderedBroadcast(broadcastIntent, PERMISSION, new OutgoingCallReceiver(),
null, // scheduler
Activity.RESULT_OK, // initialCode
number, // initialData: initial value for the result data
null); // initialExtras
进入一个内部类:
OutgoingCallReceiver处理完后-->finish()
public void onReceive(Context context, Intent intent) {
doReceive(context, intent);
finish();
}
在广播里判断是否已经启动InCallScreen-->alreadyCalled = intent.getBooleanExtra(OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false);如果alreadyCalled为false就做一些初始化工作,设置Intent为ACTION_CALL,并带上号码和uri。启动InCallScreen-->startSipCallOptionHandler(context, intent, uri, number);
private void startSipCallOptionHandler(Context context, Intent intent,
Uri uri, String number) {
if (VDBG) {
Log.i(TAG, "startSipCallOptionHandler...");
Log.i(TAG, "- intent: " + intent);
Log.i(TAG, "- uri: " + uri);
Log.i(TAG, "- number: " + number);
}
// Create a copy of the original CALL intent that started the whole
// outgoing-call sequence. This intent will ultimately be passed to
// CallController.placeCall() after the SipCallOptionHandler step.
Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
newIntent.putExtra(EXTRA_ACTUAL_NUMBER_TO_DIAL, number);
PhoneUtils.checkAndCopyPhoneProviderExtras(intent, newIntent);
// Finally, launch the SipCallOptionHandler, with the copy of the
// original CALL intent stashed away in the EXTRA_NEW_CALL_INTENT
// extra.
Intent selectPhoneIntent = new Intent(ACTION_SIP_SELECT_PHONE, uri);
selectPhoneIntent.setClass(context, SipCallOptionHandler.class);
selectPhoneIntent.putExtra(EXTRA_NEW_CALL_INTENT, newIntent);
selectPhoneIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (DBG) Log.v(TAG, "startSipCallOptionHandler(): " +
"calling startActivity: " + selectPhoneIntent);
context.startActivity(selectPhoneIntent);
// ...and see SipCallOptionHandler.onCreate() for the next step of the sequence.
}
启动了SipCallOptionHandler类在
onCreate()的最后会调用-->setResultAndFinish();
private void setResultAndFinish() {
runOnUiThread(new Runnable() {
public void run() {
if (mOutgoingSipProfile != null) {
if (!isNetworkConnected()) {
showDialog(DIALOG_NO_INTERNET_ERROR);
return;
}
if (DBG) Log.v(TAG, "primary SIP URI is " +
mOutgoingSipProfile.getUriString());
createSipPhoneIfNeeded(mOutgoingSipProfile);
mIntent.putExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI,
mOutgoingSipProfile.getUriString());
if (mMakePrimary) {
mSipSharedPreferences.setPrimaryAccount(
mOutgoingSipProfile.getUriString());
}
}
if (mUseSipPhone && mOutgoingSipProfile == null) {
showDialog(DIALOG_START_SIP_SETTINGS);
return;
} else {
// Woo hoo -- it's finally OK to initiate the outgoing call!
PhoneApp.getInstance().callController.placeCall(mIntent);
}
finish();
}
});
}
正常情况会跑到-->PhoneApp.getInstance().callController.placeCall(mIntent);之后Activity:
SipCallOptionHandler会finish; 在CallController.java类中在
placeCall这个函数有一段注释说明调用流程
/**
* Initiate an outgoing call.
*
* Here's the most typical outgoing call sequence:
*
* (1) OutgoingCallBroadcaster receives a CALL intent and sends the
* NEW_OUTGOING_CALL broadcast
*
* (2) The broadcast finally reaches OutgoingCallReceiver, which stashes
* away a copy of the original CALL intent and launches
* SipCallOptionHandler
*
* (3) SipCallOptionHandler decides whether this is a PSTN or SIP call (and
* in some cases brings up a dialog to let the user choose), and
* ultimately calls CallController.placeCall() (from the
* setResultAndFinish() method) with the stashed-away intent from step
* (2) as the "intent" parameter.
*
* (4) Here in CallController.placeCall() we read the phone number or SIP
* address out of the intent and actually initate the call, and
* simultaneously launch the InCallScreen to display the in-call UI.
*
* (5) We handle various errors by directing the InCallScreen to
* display error messages or dialogs (via the InCallUiState
* "pending call status code" flag), and in some cases we also
* sometimes continue working in the background to resolve the
* problem (like in the case of an emergency call while in
* airplane mode). Any time that some onscreen indication to the
* user needs to change, we update the "status dialog" info in
* the inCallUiState and (re)launch the InCallScreen to make sure
* it's visible.
*/
public void placeCall(Intent intent) {
log("placeCall()... intent = " + intent);
if (VDBG) log(" extras = " + intent.getExtras());
final InCallUiState inCallUiState = mApp.inCallUiState;
// TODO: Do we need to hold a wake lock while this method runs?
// Or did we already acquire one somewhere earlier
// in this sequence (like when we first received the CALL intent?)
if (intent == null) {
Log.wtf(TAG, "placeCall: called with null intent");
throw new IllegalArgumentException("placeCall: called with null intent");
}
String action = intent.getAction();
Uri uri = intent.getData();
if (uri == null) {
Log.wtf(TAG, "placeCall: intent had no data");
throw new IllegalArgumentException("placeCall: intent had no data");
}
String scheme = uri.getScheme();
String number = PhoneNumberUtils.getNumberFromIntent(intent, mApp);
if (VDBG) {
log("- action: " + action);
log("- uri: " + uri);
log("- scheme: " + scheme);
log("- number: " + number);
}
// This method should only be used with the various flavors of CALL
// intents. (It doesn't make sense for any other action to trigger an
// outgoing call!)
if (!(Intent.ACTION_CALL.equals(action)
|| Intent.ACTION_CALL_EMERGENCY.equals(action)
|| Intent.ACTION_CALL_PRIVILEGED.equals(action))) {
Log.wtf(TAG, "placeCall: unexpected intent action " + action);
throw new IllegalArgumentException("Unexpected action: " + action);
}
// Check to see if this is an OTASP call (the "activation" call
// used to provision CDMA devices), and if so, do some
// OTASP-specific setup.
Phone phone = mApp.mCM.getDefaultPhone();
if (TelephonyCapabilities.supportsOtasp(phone)) {
checkForOtaspCall(intent);
}
// Clear out the "restore mute state" flag since we're
// initiating a brand-new call.
//
// (This call to setRestoreMuteOnInCallResume(false) informs the
// phone app that we're dealing with a new connection
// (i.e. placing an outgoing call, and NOT handling an aborted
// "Add Call" request), so we should let the mute state be handled
// by the PhoneUtils phone state change handler.)
mApp.setRestoreMuteOnInCallResume(false);
// If a provider is used, extract the info to build the
// overlay and route the call. The overlay will be
// displayed when the InCallScreen becomes visible.
if (PhoneUtils.hasPhoneProviderExtras(intent)) {
inCallUiState.setProviderOverlayInfo(intent);
} else {
inCallUiState.clearProviderOverlayInfo();
}
CallStatusCode status = placeCallInternal(intent);
if (status == CallStatusCode.SUCCESS) {
if (DBG) log("==> placeCall(): success from placeCallInternal(): " + status);
// There's no "error condition" that needs to be displayed to
// the user, so clear out the InCallUiState's "pending call
// status code".
inCallUiState.clearPendingCallStatusCode();
// Notify the phone app that a call is beginning so it can
// enable the proximity sensor
mApp.setBeginningCall(true);
} else {
log("==> placeCall(): failure code from placeCallInternal(): " + status);
// Handle the various error conditions that can occur when
// initiating an outgoing call, typically by directing the
// InCallScreen to display a diagnostic message (via the
// "pending call status code" flag.)
handleOutgoingCallError(status);
}
// Finally, regardless of whether we successfully initiated the
// outgoing call or not, force the InCallScreen to come to the
// foreground.
//
// (For successful calls the the user will just see the normal
// in-call UI. Or if there was an error, the InCallScreen will
// notice the InCallUiState pending call status code flag and display an
// error indication instead.)
// TODO: double-check the behavior of mApp.displayCallScreen()
// if the InCallScreen is already visible:
// - make sure it forces the UI to refresh
// - make sure it does NOT launch a new InCallScreen on top
// of the current one (i.e. the Back button should not take
// you back to the previous InCallScreen)
// - it's probably OK to go thru a fresh pause/resume sequence
// though (since that should be fast now)
// - if necessary, though, maybe PhoneApp.displayCallScreen()
// could notice that the InCallScreen is already in the foreground,
// and if so simply call updateInCallScreen() instead.
mApp.displayCallScreen();
}
最后启动InCallScreen-->startActivity(createInCallIntent());
/**
* Starts the InCallScreen Activity.
*/
/* package */void displayCallScreen() {
if (VDBG)
Log.d(LOG_TAG, "displayCallScreen()...");
// On non-voice-capable devices we shouldn't ever be trying to
// bring up the InCallScreen in the first place.
if (!sVoiceCapable) {
Log.w(LOG_TAG,
"displayCallScreen() not allowed: non-voice-capable device",
new Throwable("stack dump")); // Include a stack trace since
// this warning
// indicates a bug in our
// caller
return;
}
try {
startActivity(createInCallIntent());
} catch (ActivityNotFoundException e) {
// It's possible that the in-call UI might not exist (like on
// non-voice-capable devices), so don't crash if someone
// accidentally tries to bring it up...
Log.w(LOG_TAG,
"displayCallScreen: transition to InCallScreen failed: "
+ e);
}
Profiler.callScreenRequested();
}
/* package */static Intent createInCallIntent() {
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_NO_USER_ACTION);
intent.setClassName("com.android.phone", getCallScreenClassName());
return intent;
}
//获取InCallScreen的包名
static String getCallScreenClassName() {
return InCallScreen.class.getName();
}
到这里一次普通的拨号界面启动流程就完成了。有很多的全局的初始化工作在PhoneApp.java中已经完成