Android4.0(Phone)来电过程分析

发布时间:2025-12-10 11:17:22 浏览次数:7

在开机时。系统会启动PhoneApp类,那是由于在AndroidManifest.xml文件里配置了

<applicationandroid:name="PhoneApp"android:icon="@drawable/ic_launcher_phone"android:label="@string/phoneAppLabel"android:persistent="true" ></application>因为配置了android:persistent="true"属性,而且Phone.apk是安装在/system/app/文件夹下的。所以在开机时会自己主动启动PhoneApp类。在PhoneApp初始化时会进入回调函数:onCreate() @Overridepublic void onCreate() {//.......if (phone == null) {//........// 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());//........}}对CallNotifier对象进行初始化,Callnotifier主要是对电话状态的监听

在CallNotifier的构造函数里

private CallNotifier(PhoneApp app, Phone phone, Ringer ringer,BluetoothHandsfree btMgr, CallLogAsync callLog) {//............//跟CallManager注冊通知。跟Framework通訊registerForNotifications();//...............}在CallNotifier.java中向CallManager类(Framework层)注冊监听消息 private void registerForNotifications() {mCM.registerForNewRingingConnection(this, PHONE_NEW_RINGING_CONNECTION, null);mCM.registerForPreciseCallStateChanged(this, PHONE_STATE_CHANGED, null);mCM.registerForDisconnect(this, PHONE_DISCONNECT, null);mCM.registerForUnknownConnection(this, PHONE_UNKNOWN_CONNECTION_APPEARED, null);mCM.registerForIncomingRing(this, PHONE_INCOMING_RING, null);mCM.registerForCdmaOtaStatusChange(this, EVENT_OTA_PROVISION_CHANGE, null);mCM.registerForCallWaiting(this, PHONE_CDMA_CALL_WAITING, null);mCM.registerForDisplayInfo(this, PHONE_STATE_DISPLAYINFO, null);mCM.registerForSignalInfo(this, PHONE_STATE_SIGNALINFO, null);mCM.registerForInCallVoicePrivacyOn(this, PHONE_ENHANCED_VP_ON, null);mCM.registerForInCallVoicePrivacyOff(this, PHONE_ENHANCED_VP_OFF, null);mCM.registerForRingbackTone(this, PHONE_RINGBACK_TONE, null);mCM.registerForResendIncallMute(this, PHONE_RESEND_MUTE, null);}通过mCM.registerForNewRingingConnection(this, PHONE_NEW_RINGING_CONNECTION, null);监听来电,在CallManager.java中
/*** Register for getting notifications for change in the Call State {@link Call.State}* This is called PreciseCallState because the call state is more precise than the* {@link Phone.State} which can be obtained using the {@link PhoneStateListener}** Resulting events will have an AsyncResult in <code>Message.obj</code>.* AsyncResult.userData will be set to the obj argument here.* The <em>h</em> parameter is held only by a weak reference.*/public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){mPreciseCallStateRegistrants.addUnique(h, what, obj);}

处理PHONE_NEW_RINGING_CONNECTION

<pre name="code" class="java"> @Overridepublic void handleMessage(Message msg) {switch (msg.what) {case PHONE_NEW_RINGING_CONNECTION:log("RINGING... (new)");onNewRingingConnection((AsyncResult) msg.obj);mSilentRingerRequested = false;break;//...........}}

什么时候会收到PHONE_NEW_RINGING_CONNECTION消息呢?当Modem(调制解调器)端收到来电信息时,会将相关来电信息通过AT指令发送给RILC。再通过RILC使用socket发送给RILJ,逐层向上传递,上传到Framework层,终于显示来电响铃界面。最后是通过下面广播上传到PhoneApp

在RIL中有一个内部类RILReceiver

class RILReceiver implements Runnable {byte[] buffer;RILReceiver() {buffer = new byte[RIL_MAX_COMMAND_BYTES];}public voidrun() {int retryCount = 0;try {for (;;) {LocalSocket s = null;LocalSocketAddress l;try {s = new LocalSocket();l = new LocalSocketAddress(SOCKET_NAME_RIL,LocalSocketAddress.Namespace.RESERVED);s.connect(l);} catch (IOException ex){try {if (s != null) {s.close();}} catch (IOException ex2) {//ignore failure to close after failure to connect}// don't print an error message after the the first time// or after the 8th timeif (retryCount == 8) {Log.e (LOG_TAG,"Couldn't find '" + SOCKET_NAME_RIL+ "' socket after " + retryCount+ " times, continuing to retry silently");} else if (retryCount > 0 && retryCount < 8) {Log.i (LOG_TAG,"Couldn't find '" + SOCKET_NAME_RIL+ "' socket; retrying after timeout");}try {Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);} catch (InterruptedException er) {}retryCount++;continue;}retryCount = 0;mSocket = s;Log.i(LOG_TAG, "Connected to '" + SOCKET_NAME_RIL + "' socket");int length = 0;try {InputStream is = mSocket.getInputStream();for (;;) {Parcel p;length = readRilMessage(is, buffer);if (length < 0) {// End-of-stream reachedbreak;}p = Parcel.obtain();p.unmarshall(buffer, 0, length);p.setDataPosition(0);//Log.v(LOG_TAG, "Read packet: " + length + " bytes");processResponse(p);p.recycle();}} catch (java.io.IOException ex) {Log.i(LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed",ex);} catch (Throwable tr) {Log.e(LOG_TAG, "Uncaught exception read length=" + length +"Exception:" + tr.toString());}Log.i(LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL+ "' socket");setRadioState (RadioState.RADIO_UNAVAILABLE);try {mSocket.close();} catch (IOException ex) {}mSocket = null;RILRequest.resetSerial();// Clear request list on closeclearRequestsList(RADIO_NOT_AVAILABLE, false);}} catch (Throwable tr) {Log.e(LOG_TAG,"Uncaught exception", tr);}/* We're disconnected so we don't know the ril version */notifyRegistrantsRilConnectionChanged(-1);}}在RIL的构造函数中创建RILReceiver对象 public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {//..................mReceiver = new RILReceiver();mReceiverThread = new Thread(mReceiver, "RILReceiver");mReceiverThread.start();//.................}}在前面的分析中知道RIL在PhoneApp中就进行初始化了,RILReceiver是一个线程使用Socket通信。在线程中调用processResponse(p)
private void processResponse (Parcel p) {int type;type = p.readInt();if (type == RESPONSE_UNSOLICITED) {//主动响应processUnsolicited (p);} else if (type == RESPONSE_SOLICITED) {//响应请求processSolicited (p);}releaseWakeLockIfDone();}来电调用的是下面函数 private void processUnsolicited (Parcel p) {//..............case RIL_UNSOL_CALL_RING: ret = responseCallRing(p); break;//..............case RIL_UNSOL_CALL_RING:if (RILJ_LOGD) unsljLogRet(response, ret);if (mRingRegistrant != null) {mRingRegistrant.notifyRegistrant(new AsyncResult (null, ret, null));}break;//..............}进入Registrant类中
public void notifyRegistrant(AsyncResult ar){internalNotifyRegistrant (ar.result, ar.exception);} /*package*/ voidinternalNotifyRegistrant (Object result, Throwable exception){Handler h = getHandler();if (h == null) {clear();} else {Message msg = Message.obtain();msg.what = what;msg.obj = new AsyncResult(userObj, result, exception);h.sendMessage(msg);}}当PhoneApp收到:PHONE_NEW_RINGING_CONNECTION后 /*** Handles a "new ringing connection" event from the telephony layer.*/private void onNewRingingConnection(AsyncResult r) {Connection c = (Connection) r.result;log("onNewRingingConnection(): state = " + mCM.getState() + ", conn = { " + c + " }");Call ringing = c.getCall();Phone phone = ringing.getPhone();// Check for a few cases where we totally ignore incoming calls.if (ignoreAllIncomingCalls(phone)) {// Immediately reject the call, without even indicating to the user// that an incoming call occurred. (This will generally send the// caller straight to voicemail, just as if we *had* shown the// incoming-call UI and the user had declined the call.)PhoneUtils.hangupRingingCall(ringing);return;}if (c == null) {Log.w(LOG_TAG, "CallNotifier.onNewRingingConnection(): null connection!");// Should never happen, but if it does just bail out and do nothing.return;}if (!c.isRinging()) {Log.i(LOG_TAG, "CallNotifier.onNewRingingConnection(): connection not ringing!");// This is a very strange case: an incoming call that stopped// ringing almost instantly after the onNewRingingConnection()// event. There's nothing we can do here, so just bail out// without doing anything. (But presumably we'll log it in// the call log when the disconnect event comes in...)return;}// Stop any signalInfo tone being played on receiving a CallstopSignalInfoTone();Call.State state = c.getState();// State will be either INCOMING or WAITING.if (VDBG) log("- connection is ringing! state = " + state);// if (DBG) PhoneUtils.dumpCallState(mPhone);// No need to do any service state checks here (like for// "emergency mode"), since in those states the SIM won't let// us get incoming connections in the first place.// TODO: Consider sending out a serialized broadcast Intent here// (maybe "ACTION_NEW_INCOMING_CALL"), *before* starting the// ringer and going to the in-call UI. The intent should contain// the caller-id info for the current connection, and say whether// it would be a "call waiting" call or a regular ringing call.// If anybody consumed the broadcast, we'd bail out without// ringing or bringing up the in-call UI.//// This would give 3rd party apps a chance to listen for (and// intercept) new ringing connections. An app could reject the// incoming call by consuming the broadcast and doing nothing, or// it could "pick up" the call (without any action by the user!)// via some future TelephonyManager API.//// See bug 1312336 for more details.// We'd need to protect this with a new "intercept incoming calls"// system permission.// Obtain a partial wake lock to make sure the CPU doesn't go to// sleep before we finish bringing up the InCallScreen.// (This will be upgraded soon to a full wake lock; see// showIncomingCall().)if (VDBG) log("Holding wake lock on new incoming connection.");mApplication.requestWakeState(PhoneApp.WakeState.PARTIAL);// - don't ring for call waiting connections// - do this before showing the incoming call panelif (PhoneUtils.isRealIncomingCall(state)) {startIncomingCallQuery(c);} else {if (VDBG) log("- starting call waiting tone...");if (mCallWaitingTonePlayer == null) {mCallWaitingTonePlayer = new InCallTonePlayer(InCallTonePlayer.TONE_CALL_WAITING);mCallWaitingTonePlayer.start();}// in this case, just fall through like before, and call// showIncomingCall().if (DBG) log("- showing incoming call (this is a WAITING call)...");showIncomingCall();}// Note we *don't* post a status bar notification here, since// we're not necessarily ready to actually show the incoming call// to the user. (For calls in the INCOMING state, at least, we// still need to run a caller-id query, and we may not even ring// at all if the "send directly to voicemail" flag is set.)//// Instead, we update the notification (and potentially launch the// InCallScreen) from the showIncomingCall() method, which runs// when the caller-id query completes or times out.if (VDBG) log("- onNewRingingConnection() done.");}调用showIncomingCall();函数显示来电界面 private void showIncomingCall() {log("showIncomingCall()... phone state = " + mCM.getState());// Before bringing up the "incoming call" UI, force any system// dialogs (like "recent tasks" or the power dialog) to close first.try {ActivityManagerNative.getDefault().closeSystemDialogs("call");} catch (RemoteException e) {}mApplication.preventScreenOn(true);mApplication.requestWakeState(PhoneApp.WakeState.FULL);// Post the "incoming call" notification *and* include the// fullScreenIntent that'll launch the incoming-call UI.// (This will usually take us straight to the incoming call// screen, but if an immersive activity is running it'll just// appear as a notification.)if (DBG) log("- updating notification from showIncomingCall()...");mApplication.notificationMgr.updateNotificationAndLaunchIncomingCallUi();}NotificationMgr.java public void updateNotificationAndLaunchIncomingCallUi() {// Set allowFullScreenIntent=true to indicate that we *should*// launch the incoming call UI if necessary.updateInCallNotification(true);}private void updateInCallNotification(boolean allowFullScreenIntent) {// incoming call is ringing:if (hasRingingCall) {if (DBG) log("- Using hi-pri notification for ringing call!");// This is a high-priority event that should be shown even if the// status bar is hidden or if an immersive activity is running.notification.flags |= Notification.FLAG_HIGH_PRIORITY;notification.tickerText = expandedViewLine2;if (allowFullScreenIntent) {if (DBG) log("- Setting fullScreenIntent: " + inCallPendingIntent);notification.fullScreenIntent = inCallPendingIntent;Call ringingCall = mCM.getFirstActiveRingingCall();if ((ringingCall.getState() == Call.State.WAITING) && !mApp.isShowingCallScreen()) {Log.i(LOG_TAG, "updateInCallNotification: call-waiting! force relaunch...");// Cancel the IN_CALL_NOTIFICATION immediately before// (re)posting it; this seems to force the// NotificationManager to launch the fullScreenIntent.mNotificationManager.cancel(IN_CALL_NOTIFICATION);}}}if (DBG) log("Notifying IN_CALL_NOTIFICATION: " + notification);mNotificationManager.notify(IN_CALL_NOTIFICATION,notification);// Finally, refresh the mute and speakerphone notifications (since// some phone state changes can indirectly affect the mute and/or// speaker state).updateSpeakerNotification();updateMuteNotification();}PendingIntent inCallPendingIntent =
PendingIntent.getActivity(mContext, 0,
PhoneApp.createInCallIntent(), 0);
notification.contentIntent = inCallPendingIntent;
/* 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;}static String getCallScreenClassName() {return InCallScreen.class.getName();}通过PendingIntent来启动InCallScreen来电界面,接听后就跟拨号界面一样了。

在測试android:persistent="true"时。编写了一个測试程序,一定要安装在system/app/文件夹下。在开机时才会启动。在程序启动后不会被系统回收,很easy

<?

xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.dzt.persistentdemo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <application android:name="PersionApp" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:persistent="true" android:theme="@style/AppTheme" > <activity android:name="com.dzt.persistentdemo.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

开机后会打印在程序中加入的消息

sh-4.2# logcat -v time | grep PersionApp01-02 00:18:46.090 I/PersionApp( 1033): [PersionApp]------------------->onCreate test01-02 00:18:46.090 I/PersionApp( 1033): [Persion]----------------------------->Persion01-02 00:18:46.090 I/PersionApp( 1033): [PersionApp]------------------->onCreate persion = null

演示样例代码:http://download.csdn.net/detail/deng0zhaotai/7714163

需要做网站?需要网络推广?欢迎咨询客户经理 13272073477