編輯:關於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
在以下代碼中根據不同的類型創建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); //通話管理
//***** 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();
}
}
}
}
}

Android教你如何一步步打造通用適配器
前言在Android開發中ListView是最為常用的控件之一,基本每個應用都會涉及到它,要使用ListView列表展示,就不可避免地涉及到另外一個東西—&m
Android 繼承DialogFragment彈出dialog對話框二
之前寫過一篇關於Android 繼承DialogFragment彈出dialog對話框一,這次是在上次的基礎上修改了一些東西,就是怎樣在DialogFragment中獲取
實現折疊式Toolbar:CollapsingToolbarLayout 使用完全解析
簡介在各種不同的應用中,大家可能會經常見到這樣一個效果:Toolbar是透明的,有著一個背景圖片以及大標題,隨著頁面向上滑動,其標題逐漸縮放到Toolbar上,而背景圖片
Android JNI配置及入門
JNI是一種可以在Java中調用C/C++代碼的技術,也就是說可以在Android上使用C/C++來開發。但是並不能用純C/C+&