Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android4.0(Phone)撥號啟動過程分析(三)與Framework層通信

Android4.0(Phone)撥號啟動過程分析(三)與Framework層通信

編輯:關於Android編程

由於Android幾乎所有的代碼都是公開的,如果要對Framework層分析就必需先拿到Framework層的代碼,我在前面已經搭建好了ubuntu14.04的環境,下載好了Android4.0的源碼,其中也包括了Framework層和Package的代碼,導出到宿主機Windows XP中用Source Insight 3.5工具來查看源碼,Package中的代碼可以導入到Eclipse下查看,我是把frameworksase整個目錄都導入到Source Insight 3.5工程中,可以選擇我們需要的目錄導入,如frameworksasemedia estscontentsmedia_api目錄下有很多音、視頻文件,可以只導入frameworksasecore、frameworksase elephony、frameworksaseservices、frameworksaseinclude等目錄。需要代碼的可以到這下載: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;
                    }
                }
GSMPhone和CDMAPhone都繼承了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 = random.nextInt(3);

				switch (data) {
				case 0:
					state = Phone.State.IDLE;
					break;
				case 1:
					state = Phone.State.RINGING;
					break;
				case 2:
					state = Phone.State.OFFHOOK;
					break;
				default:
					state = Phone.State.IDLE;
					break;
				}
				mPhone.notifyPreciseCallStateChanged();
				// PhoneApp.print_i(GsmCallTracker,
				// CallTrackerThread data = 
				// + data + ---- + state.toString());
				try {
					sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}
\

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved