在开机时,系统会启动PhoneApp类,那是因为在AndroidManifest.xml文件中配置了

<application        android: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"> @Override    public 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 void        run() {            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 time                    if (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 reached                            break;                        }                        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 close                clearRequestsList(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*/ void    internalNotifyRegistrant (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 Call        stopSignalInfoTone();        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 panel        if (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/目录下,在开机时才会启动,在程序启动后不会被系统回收,非常简单

<?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


更多相关文章

  1. 另辟思路解决 Android(安卓)4.0.4 不能监听Home键的问题
  2. android client随机验证码生成函数
  3. eventloop(ril.cpp)函数中定义的管道的作用(Android)
  4. android小游戏 飞机躲子弹
  5. Android软键盘监听KeyboardWatcher
  6. Android(安卓)Multimedia框架总结(二十七)MediaCodec回顾
  7. Android(安卓)Support Annotations 使用详解
  8. Android平台中Wifi的初始化
  9. Android线程封装基类Thread

随机推荐

  1. 移动开发者大会.html5。Android。ios。wp
  2. android中怎样声明操作通话记录的权利
  3. android edittext的属性
  4. 完美解决隐藏Listview和RecyclerView去掉
  5. 横竖屏布局小技巧
  6. android与j2me移植之clipRect
  7. Android中的5种数据存储方式
  8. Android练习小项目时踩到的坑
  9. Android之倍数提高工作效率的 Android(安
  10. AndroidMenifest.xml中android:sharedUse