Android4.0(Phone)拨号启动过程分析(一)
发布日期:2021-06-24 18:18:41 浏览次数:2 分类:技术文章

本文共 18984 字,大约阅读时间需要 63 分钟。

     因为工作的须要。须要改动原生的Phone程序,如今就好好看下来电与拨号是怎样处理的;无论是拨号还是来电,调用的都是Phone程序,因为非常多类都涉及到framework层,比較复杂;先从简单的拨号分析。在外部拨号是由Action:android.intent.action.CALL_PRIVILEGEDandroid.intent.action.CALL发起,这里仅仅分析android.intent.action.CALL的情况,程序文件夹结构:

能够在Phone程序的AndroidManifest.xml文件里找到

在收到Action:
android.intent.action.CALL后会启动Activity:
OutgoingCallBroadcaster。在启动Activity之前最先会调用:
PhoneApp,由于它继承了
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中已经完毕

转载地址:https://blog.csdn.net/weixin_34248258/article/details/86357804 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:儒释道的最高境界
下一篇:MVC ---- ckeditor 批量绑定 blur 事件

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年04月06日 11时46分24秒