首页 > 代码库 > Android4.0(Phone)拨号启动过程分析(三)与Framework层通信

Android4.0(Phone)拨号启动过程分析(三)与Framework层通信

        由于Android几乎所有的代码都是公开的,如果要对Framework层分析就必需先拿到Framework层的代码,我在前面已经搭建好了ubuntu14.04的环境,下载好了Android4.0的源码,其中也包括了Framework层和Package的代码,导出到宿主机Windows XP中用Source Insight 3.5工具来查看源码,Package中的代码可以导入到Eclipse下查看,我是把frameworks\base整个目录都导入到Source Insight 3.5工程中,可以选择我们需要的目录导入,如frameworks\base\media\tests\contents\media_api目录下有很多音、视频文件,可以只导入frameworks\base\core、frameworks\base\telephony、frameworks\base\services、frameworks\base\include等目录。需要代码的可以到这下载:http://pan.baidu.com/s/1i3KczeX在PhoneApp初始化时,有以下代码

@Override
public void onCreate() {
	//...........
	if (phone == null) {
		// 初始化phone frameworks层
			PhoneFactory.makeDefaultPhones(this);

			// 获取默认的phone对象
			phone = PhoneFactory.getDefaultPhone();

			mCM = CallManager.getInstance();
			mCM.registerPhone(phone);
		//.............
	}
	//...............
}
在应用层的PhoneApp中调用PhoneFactory的静态方法makeDefaultPhones创建一个默认的Phone对象,而framework中采用的是代理模式和工厂模式实现,在makedefaultPhone中
 //***** Class Methods

    public static void makeDefaultPhones(Context context) {
        makeDefaultPhone(context);
    }

    /**
     * FIXME replace this with some other way of making these
     * instances
     */
    public static void makeDefaultPhone(Context context) {
        synchronized(Phone.class) {
            if (!sMadeDefaults) {
                sLooper = Looper.myLooper();
                sContext = context;

                if (sLooper == null) {
                    throw new RuntimeException(
                        "PhoneFactory.makeDefaultPhone must be called from Looper thread");
                }

                int retryCount = 0;
                for(;;) {
                    boolean hasException = false;
                    retryCount ++;

                    try {
                        // use UNIX domain socket to
                        // prevent subsequent initialization
                        new LocalServerSocket("com.android.internal.telephony");
                    } catch (java.io.IOException ex) {
                        hasException = true;
                    }

                    if ( !hasException ) {
                        break;
                    } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
                        throw new RuntimeException("PhoneFactory probably already running");
                    } else {
                        try {
                            Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
                        } catch (InterruptedException er) {
                        }
                    }
                }

                sPhoneNotifier = new DefaultPhoneNotifier();

                // Get preferred network mode
                int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
                if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
                    preferredNetworkMode = Phone.NT_MODE_GLOBAL;
                }
                int networkMode = Settings.Secure.getInt(context.getContentResolver(),
                        Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode);
                Log.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkMode));

                // Get cdmaSubscription
                // TODO: Change when the ril will provides a way to know at runtime
                //       the configuration, bug 4202572. And the ril issues the
                //       RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, bug 4295439.
                int cdmaSubscription;
                int lteOnCdma = BaseCommands.getLteOnCdmaModeStatic();
                switch (lteOnCdma) {
                    case Phone.LTE_ON_CDMA_FALSE:
                        cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_NV;
                        Log.i(LOG_TAG, "lteOnCdma is 0 use SUBSCRIPTION_FROM_NV");
                        break;
                    case Phone.LTE_ON_CDMA_TRUE:
                        cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_RUIM;
                        Log.i(LOG_TAG, "lteOnCdma is 1 use SUBSCRIPTION_FROM_RUIM");
                        break;
                    case Phone.LTE_ON_CDMA_UNKNOWN:
                    default:
                        //Get cdmaSubscription mode from Settings.System
                        cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(),
                                Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION,
                                preferredCdmaSubscription);
                        Log.i(LOG_TAG, "lteOnCdma not set, using PREFERRED_CDMA_SUBSCRIPTION");
                        break;
                }
                Log.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);

                //reads the system properties and makes commandsinterface
                sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);

                int phoneType = getPhoneType(networkMode);
                if (phoneType == Phone.PHONE_TYPE_GSM) {
                    Log.i(LOG_TAG, "Creating GSMPhone");
                    sProxyPhone = new PhoneProxy(new GSMPhone(context,
                            sCommandsInterface, sPhoneNotifier));
                } else if (phoneType == Phone.PHONE_TYPE_CDMA) {
                    switch (BaseCommands.getLteOnCdmaModeStatic()) {
                        case Phone.LTE_ON_CDMA_TRUE:
                            Log.i(LOG_TAG, "Creating CDMALTEPhone");
                            sProxyPhone = new PhoneProxy(new CDMALTEPhone(context,
                                sCommandsInterface, sPhoneNotifier));
                            break;
                        case Phone.LTE_ON_CDMA_FALSE:
                        default:
                            Log.i(LOG_TAG, "Creating CDMAPhone");
                            sProxyPhone = new PhoneProxy(new CDMAPhone(context,
                                    sCommandsInterface, sPhoneNotifier));
                            break;
                    }
                }

                sMadeDefaults = true;
            }
        }
    }
在PhoneFactory.java类中定义了

static private CommandsInterface sCommandsInterface = null;
//reads the system properties and makes commandsinterface
sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
所以后面用到的CommandsInterface对象都是RIL类,由于RIL实现了CommandsInterface
public final class RIL extends BaseCommands implements CommandsInterface {}
对于什么是RIL,百度上的解释如下,后面有时间再去分析RIL类。
Android上的RIL
Android是目前最流行智能手机操作系统之一,Android的RIL位于应用程序框架与内核之间,分成了两个部分,一个部分是rild,它负责socket与应用程序框架进行通信。另外一个部分是Vendor RIL,这个部分负责向下是通过两种方式与radio进行通信,它们是直接与radio通信的AT指令通道和用于传输包数据的通道,数据通道用于手机的上网功能。对于RIL的java框架部分,也被分成了两个部分,一个是RIL模块,这个模块主要用于与下层的rild进行通信,另外一个是Phone模块,这个模块直接暴露电话功能接口给应用开发用户,供他们调用以进行电话功能的实现。

在以下代码中根据不同的类型创建Phone,如GSM(2G中国移动和联通)、CDMA(中国电信)等,采用了向上转型,向上转型是安全的。

int phoneType = getPhoneType(networkMode);
                if (phoneType == Phone.PHONE_TYPE_GSM) {
                    Log.i(LOG_TAG, "Creating GSMPhone");
                    sProxyPhone = new PhoneProxy(new GSMPhone(context,
                            sCommandsInterface, sPhoneNotifier));
                } else if (phoneType == Phone.PHONE_TYPE_CDMA) {
                    switch (BaseCommands.getLteOnCdmaModeStatic()) {
                        case Phone.LTE_ON_CDMA_TRUE:
                            Log.i(LOG_TAG, "Creating CDMALTEPhone");
                            sProxyPhone = new PhoneProxy(new CDMALTEPhone(context,
                                sCommandsInterface, sPhoneNotifier));
                            break;
                        case Phone.LTE_ON_CDMA_FALSE:
                        default:
                            Log.i(LOG_TAG, "Creating CDMAPhone");
                            sProxyPhone = new PhoneProxy(new CDMAPhone(context,
                                    sCommandsInterface, sPhoneNotifier));
                            break;
                    }
                }
GSMPhoneCDMAPhone都继承了PhoneBase,以下分析默认创建的是GSMPhone;在GSMPhone.java的构造函数中

// Constructors

    public
    GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier) {
        this(context,ci,notifier, false);
    }

    public
    GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {
        super(notifier, context, ci, unitTestMode);

        if (ci instanceof SimulatedRadioControl) {
            mSimulatedRadioControl = (SimulatedRadioControl) ci;
        }

        mCM.setPhoneType(Phone.PHONE_TYPE_GSM);
        mCT = new GsmCallTracker(this);   //通话管理
        mSST = new GsmServiceStateTracker (this);
        mSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor);
        mIccFileHandler = new SIMFileHandler(this);
        mIccRecords = new SIMRecords(this);
        mDataConnectionTracker = new GsmDataConnectionTracker (this);
        mIccCard = new SimCard(this);
        if (!unitTestMode) {
            mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
            mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS);
            mSubInfo = new PhoneSubInfo(this);
        }
        mStkService = CatService.getInstance(mCM, mIccRecords, mContext, mIccFileHandler, mIccCard);

        mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
        mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
        mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
        mCM.registerForOn(this, EVENT_RADIO_ON, null);
        mCM.setOnUSSD(this, EVENT_USSD, null);
        mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);
        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);

        if (false) {
            try {
                //debugSocket = new LocalServerSocket("com.android.internal.telephony.debug");
                debugSocket = new ServerSocket();
                debugSocket.setReuseAddress(true);
                debugSocket.bind (new InetSocketAddress("127.0.0.1", 6666));

                debugPortThread
                    = new Thread(
                        new Runnable() {
                            public void run() {
                                for(;;) {
                                    try {
                                        Socket sock;
                                        sock = debugSocket.accept();
                                        Log.i(LOG_TAG, "New connection; resetting radio");
                                        mCM.resetRadio(null);
                                        sock.close();
                                    } catch (IOException ex) {
                                        Log.w(LOG_TAG,
                                            "Exception accepting socket", ex);
                                    }
                                }
                            }
                        },
                        "GSMPhone debug");

                debugPortThread.start();

            } catch (IOException ex) {
                Log.w(LOG_TAG, "Failure to open com.android.internal.telephony.debug socket", ex);
            }
        }

        //Change the system property
        SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
                new Integer(Phone.PHONE_TYPE_GSM).toString());
    }
在这里创建了通话管理类GsmCallTracker mCT = new GsmCallTracker(this); //通话管理
在构造时,监听EVENT_CALL_STATE_CHANGE标记

//***** Constructors

    GsmCallTracker (GSMPhone phone) {
        this.phone = phone;
        cm = phone.mCM;

        cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);

        cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
        cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
    }
由于GsmCallTracker的父类继承了Handler,有以下方法
//****** Overridden from Handler

    public void
    handleMessage (Message msg) {
        AsyncResult ar;

        switch (msg.what) {
            case EVENT_POLL_CALLS_RESULT:
                ar = (AsyncResult)msg.obj;

                if (msg == lastRelevantPoll) {
                    if (DBG_POLL) log(
                            "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
                    needsPoll = false;
                    lastRelevantPoll = null;
                    handlePollCalls((AsyncResult)msg.obj);
                }
            break;

            case EVENT_OPERATION_COMPLETE:
                ar = (AsyncResult)msg.obj;
                operationComplete();
            break;

            case EVENT_SWITCH_RESULT:
            case EVENT_CONFERENCE_RESULT:
            case EVENT_SEPARATE_RESULT:
            case EVENT_ECT_RESULT:
                ar = (AsyncResult)msg.obj;
                if (ar.exception != null) {
                    phone.notifySuppServiceFailed(getFailedService(msg.what));
                }
                operationComplete();
            break;

            case EVENT_GET_LAST_CALL_FAIL_CAUSE:
                int causeCode;
                ar = (AsyncResult)msg.obj;

                operationComplete();

                if (ar.exception != null) {
                    // An exception occurred...just treat the disconnect
                    // cause as "normal"
                    causeCode = CallFailCause.NORMAL_CLEARING;
                    Log.i(LOG_TAG,
                            "Exception during getLastCallFailCause, assuming normal disconnect");
                } else {
                    causeCode = ((int[])ar.result)[0];
                }
                // Log the causeCode if its not normal
                if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL ||
                    causeCode == CallFailCause.TEMPORARY_FAILURE ||
                    causeCode == CallFailCause.SWITCHING_CONGESTION ||
                    causeCode == CallFailCause.CHANNEL_NOT_AVAIL ||
                    causeCode == CallFailCause.QOS_NOT_AVAIL ||
                    causeCode == CallFailCause.BEARER_NOT_AVAIL ||
                    causeCode == CallFailCause.ERROR_UNSPECIFIED) {
                    GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
                    EventLog.writeEvent(EventLogTags.CALL_DROP,
                            causeCode, loc != null ? loc.getCid() : -1,
                            TelephonyManager.getDefault().getNetworkType());
                }

                for (int i = 0, s =  droppedDuringPoll.size()
                        ; i < s ; i++
                ) {
                    GsmConnection conn = droppedDuringPoll.get(i);

                    conn.onRemoteDisconnect(causeCode);
                }

                updatePhoneState();

                phone.notifyPreciseCallStateChanged();
                droppedDuringPoll.clear();
            break;

            case EVENT_REPOLL_AFTER_DELAY:
            case EVENT_CALL_STATE_CHANGE:
                pollCallsWhenSafe();
            break;

            case EVENT_RADIO_AVAILABLE:
                handleRadioAvailable();
            break;

            case EVENT_RADIO_NOT_AVAILABLE:
                handleRadioNotAvailable();
            break;
        }
    }  
在这个分支case EVENT_CALL_STATE_CHANGE: 去获取当前的状态
protected void pollCallsWhenSafe() {
        needsPoll = true;

        if (checkNoOperationsPending()) {
            lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
            cm.getCurrentCalls(lastRelevantPoll);
        }
    }
在RIL.java类中的实现

public void
    getCurrentCalls (Message result) {
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result);

        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

        send(rr);
    }
    
    private void
    send(RILRequest rr) {
        Message msg;

        if (mSocket == null) {
            rr.onError(RADIO_NOT_AVAILABLE, null);
            rr.release();
            return;
        }

        msg = mSender.obtainMessage(EVENT_SEND, rr);

        acquireWakeLock();

        msg.sendToTarget();
    }
在这里通知应用层改变状态

事件通知流程





为了加深理解,我也自己写了一个例子,在GsmCallTracker开一个线程去随机模拟电话状态的改变,程序相当简单

程序目录结构



 在PhoneApp中做一些全局的初始化工作

package com.dzt.phonemsg;

import android.app.Application;
import android.util.Log;

import com.dzt.phonemsg.framework.CallManager;
import com.dzt.phonemsg.framework.Phone;

/**
 * 演示Phone程序中的事件传递,由于Phone应用程序在代码跟踪时不是很方便,
 * 并且Phone的消息通讯也比较复杂,就自己把部分代码拿出来模拟Handler的消息传递,用到了觀察者模式
 * 
 * @author Administrator
 * @date 2014.08.01
 */
public class PhoneApp extends Application {

	private static final String TAG = "PhoneApp_dzt";
	private static final boolean mIsShowLog = true;
	static PhoneApp instance = null;
	Phone phone = new Phone();
	CallManager mCM;

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		instance = this;
		mCM = CallManager.getInstance();
		mCM.registerPhone(phone);
		print_i("PhoneApp", "onCreate");
	}

	/**
	 * Returns the singleton instance of the PhoneApp.
	 */
	static PhoneApp getInstance() {
		return instance;
	}

	static boolean isRunning() {
		if (instance != null)
			return true;
		return false;
	}

	/**
	 * 打印消息
	 * 
	 * @param pkg
	 * @param msg
	 */
	public static void print_i(String pkg, String msg) {
		if (mIsShowLog)
			Log.i(TAG, "[" + pkg + "]-------------------->" + msg);
	}
}
在InCallScreen类中注册需要处理的消息,并根据不同的状态使用一个TextView来更新

package com.dzt.phonemsg;

import com.dzt.phonemsg.framework.CallManager;
import com.dzt.phonemsg.framework.Phone;
import com.dzt.phonemsg.os.AsyncResult;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

public class InCallScreen extends Activity {

	private static final int PHONE_STATE_CHANGED = 101;

	private static final int REQUEST_UPDATE_SCREEN = 122;
	private TextView mText = null;
	private Phone.State mState = Phone.State.IDLE;
	private CallManager mCM;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mText = (TextView) findViewById(R.id.tv_text);
		mCM = PhoneApp.getInstance().mCM;
		registerForPhoneStates();
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		if (PhoneApp.isRunning()) {
			PhoneApp.getInstance().phone.exitPhone();
		}
		super.onDestroy();
	}

	/**
	 * Something has changed in the phone's state. Update the UI.
	 */
	private void onPhoneStateChanged(AsyncResult r) {
		mState = mCM.getState();
		requestUpdateScreen();
	}

	/**
	 * 注册PHONE_STATE_CHANGED标记,跟CallManager通讯
	 */
	private void registerForPhoneStates() {
		mCM.registerForPreciseCallStateChanged(mHandler, PHONE_STATE_CHANGED,
				null);
	}

	/* package */void requestUpdateScreen() {
		PhoneApp.print_i("MainActivity", "requestUpdateScreen()...");
		mHandler.removeMessages(REQUEST_UPDATE_SCREEN);
		mHandler.sendEmptyMessage(REQUEST_UPDATE_SCREEN);
	}

	/**
	 * 处理UI的操作,如更新通话状态和时间
	 */
	private void updateScreen() {
		mText.setText(mState.toString());
	}

	private Handler mHandler = new Handler() {

		public void handleMessage(Message msg) {
			switch (msg.what) {
			case PHONE_STATE_CHANGED:
				onPhoneStateChanged((AsyncResult) msg.obj);
				break;

			case REQUEST_UPDATE_SCREEN:
				updateScreen();
				break;
			default:
				break;
			}
		};
	};
}
GsmCallTracker这个类去改变不同的状态,用一个线程来获取随机来改变

package com.dzt.phonemsg.framework;

import java.util.Random;

/**
 * 在这里随机改变状态,模拟通话管理
 * 
 * @author Administrator
 * 
 */
public class GsmCallTracker {

	Phone.State state = Phone.State.IDLE;
	boolean mIsRunning = false;
	Phone mPhone = null;

	GsmCallTracker(Phone phone) {
		// TODO Auto-generated constructor stub
		mIsRunning = true;
		mPhone = phone;
		new CallTrackerThread().start();
	}

	public void exitGsmCallTracker() {
		mIsRunning = false;
	}

	class CallTrackerThread extends Thread {
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while (mIsRunning) {
				Random random = new Random();
				int data = http://www.mamicode.com/random.nextInt(3);>

Demo下载:http://download.csdn.net/detail/deng0zhaotai/7706739