編輯:關於Android編程
設置項網絡按鈕類定義
網絡監聽二:statusbar frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
EthernetStateMachine.java -> 網絡狀態機,用於管理網絡狀態變化及動作邏輯
EthernetManager.java -> 網絡管理器,是app和EthernetService信息交互的橋梁
EthernetInfo.java -> 網絡狀態參數類,是Parcelable的一個實現
EthernetInfo.aidl -> aidl文件,Manager和service統一使用的數據結構
IEthernetManager.aidl -> aidl文件,用於Manager和service通信
private class InterfaceStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(EthernetManager.INTERFACE_STATE_CHANGED_ACTION)) {
...
Intent newIntent = new Intent(EthernetManager.NETWORK_STATE_CHANGED_ACTION);
newIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
newIntent.putExtra(EthernetManager.EXTRA_ETHERNET_INFO, ei);
在service中,可以看到發送NETWORK_STATE_CHANGED_ACTION的發送動作,而這個發送行為還不是底層上報的狀態直接啟動的,而是上面說的網絡狀態機,它發送的INTERFACE_STATE_CHANGED_ACTION廣播信息,怎麼源頭又跑上面去了?有些人可能並不理解為什麼在framework裡面要把一個簡單的事件廣播要這麼來回的發送,等明白了網絡狀態機的作用,就知道這些過程的邏輯性了。
addState(mRootState);
addState(mIdleState, mRootState);
//addState(mObtainingLinkState, mRootState);
addState(mObtainingIpState, mRootState);
addState(mIPConnectedState, mRootState);
addState(mDisconnectingState, mRootState);
private void sendInterfaceStateChangedBroadcast() {
if (DBG) Slog.d(TAG, Sending INTERFACE_STATE_CHANGED_ACTION for
+ mEthernetInfo.getName());
Intent intent = new Intent(EthernetManager.INTERFACE_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(EthernetManager.EXTRA_ETHERNET_INFO, new EthernetInfo(mEthernetInfo));
mContext.sendBroadcast(intent);
}
private void setNetworkDetailedState(DetailedState state) {
if (DBG) Slog.d(TAG, mEthernetInfo.getName() + setDetailed state, old =
+ mEthernetInfo.getDetailedState() + and new state= + state);
if (state != mEthernetInfo.getDetailedState()) {
mEthernetInfo.setDetailedState(state, null, null);
mEthernetInfo.setIsAvailable(true);
sendInterfaceStateChangedBroadcast();
}
}
void dhcpSuccess(DhcpResults dr) {
if (DBG) Slog.d(TAG, mEthernetInfo.getName() + DHCP successful);
LinkProperties lp = dr.linkProperties;
...
setNetworkDetailedState(DetailedState.CONNECTED);
}
public void updateInterface(EthernetInfo newInfo) {
if (newInfo == null) {
Slog.e(TAG, Null EthernetInfo);
return;
}
if (mAvailableInterface == null) {
Slog.e(TAG, Unable to find statemachine for interface + newInfo.getName());
return;
}
sendMessage(mAvailableInterface,
EthernetStateMachine.CMD_UPDATE_INTERFACE,
newInfo);
if(DBG) Slog.d(TAG, newInfo.getName() + updateInterface done);
}
看到了來來回回的廣播,至此算是結束了,這裡還要注意一點,廣播收發程序中,我們要注意一個序列化參數的傳遞,就是EthernetInfo對象,這個對象存儲著當前網絡狀態參數。
public EthernetService(Context context) {
mContext = context;
mNetd = INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)
);
try {
mNetd.registerObserver(new NetworkManagementEventObserver());
} catch (RemoteException e) {
Slog.e(TAG, Remote NetworkManagementService error: + e);
}
看下NetworkManagementEventObserver的實現:
private class NetworkManagementEventObserver extends INetworkManagementEventObserver.Stub {
public void interfaceAdded(String iface) {
if(DBG) Slog.d(TAG, interfaceAdded: + iface);
addInterface(iface);
}
public void interfaceRemoved(String iface) {
if(DBG) Slog.d(TAG, interfaceRemoved: + iface);
removeInterface(iface);
}
public void limitReached(String limitName, String iface) {}
public void interfaceClassDataActivityChanged(String label, boolean active) {}
public void interfaceLinkStateChanged(String iface, boolean up) {
if(DBG) Slog.d(TAG, interfaceLinkStateChanged for + iface + , up = + up);
if (mAvailableInterface != null && up) {
//sendMessage(mAvailableInterface,
//EthernetStateMachine.CMD_LINK_UP);
}
}
public void interfaceStatusChanged(String iface, boolean up) {
if(DBG) Slog.d(TAG, interfaceStatusChanged for + iface + , up = + up);
//addInterface(iface);
}
public void addressUpdated(String address, String iface, int flags, int scope) {}
public void addressRemoved(String address, String iface, int flags, int scope) {}
}
這裡我們看到seivice從NetworkManagementService進行函數回調。 NetworkManagementService也是注冊到系統中的服務項,顧名思義負責網絡管理服務,具體功能在本篇不做深入分析,其中之一通過socket和netd進行交互,這個在後面繼續跟蹤。
SystemServer.java try {
Slog.i(TAG, NetworkManagement Service);
networkManagement = NetworkManagementService.create(context);
ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
} catch (Throwable e) {
reportWtf(starting NetworkManagement Service, e);
}
在後續的分析中,我們開始了解到framework到native的交互,這裡我們先看一張網絡的圖示 

private static final String NETD_SOCKET_NAME = netd;
private NetworkManagementService(Context context, String socket) {
mContext = context;
if (simulator.equals(SystemProperties.get(ro.product.device))) {
return;
}
mConnector = new NativeDaemonConnector(
new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160);
mThread = new Thread(mConnector, NETD_TAG);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
}
這裡socket值就是netd字符串,service啟動了名為NativeDaemonConnector的Runnable線程,同時從構造函數中傳遞了NetdCallbackReceiver 對象,用於回調處理各種網絡事件。
private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
@Override
public void onDaemonConnected() {
@Override
public boolean onEvent(int code, String raw, String[] cooked) {
switch (code) {
case NetdResponseCode.InterfaceChange:
} else if (cooked[2].equals(linkstate) && cooked.length == 5) {
// 網絡狀態變化事件在這裡回調處理
notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals(up));
return true;
}
/**
* Notify our observers of an interface link state change
* (typically, an Ethernet cable has been plugged-in or unplugged).
*/
private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
final int length = mObservers.beginBroadcast();
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
} catch (RemoteException e) {
} catch (RuntimeException e) {
}
}
mObservers.finishBroadcast();
}
上面說了NativeDaemonConnector是一個Runnable的實現,那麼這個線程在後台做些什麼工作呢?在線程run函數中可以看到線程在while死循環中一直listenToSocket,可以猜想這裡是在監聽獲取native中網絡相關事件的地方了。
@Override
public void run() {
mCallbackHandler = new Handler(FgThread.get().getLooper(), this);
while (true) {
try {
listenToSocket();
} catch (Exception e) {
loge(Error in NativeDaemonConnector: + e);
SystemClock.sleep(5000);
}
}
}
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
// 創建一個socket
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
socket.connect(address);
// 從socket中獲取流數據並處理
InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}
...
mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
event.getCode(), event.getRawEvent()));
// 收到流數據時,直接發給主線程,通過NetdCallbackReceiver 對象進行回調處理
@Override
public boolean handleMessage(Message msg) {
String event = (String) msg.obj;
try {
if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
log(String.format(Unhandled event '%s', event));
}
} catch (Exception e) {
loge(Error handling ' + event + ': + e);
}
return true;
}
private LocalSocketAddress determineSocketAddress() {
// If we're testing, set up a socket in a namespace that's accessible to test code.
// In order to ensure that unprivileged apps aren't able to impersonate native daemons on
// production devices, even if said native daemons ill-advisedly pick a socket name that
// starts with __test__, only allow this on debug builds.
if (mSocket.startsWith(__test__) && Build.IS_DEBUGGABLE) {
return new LocalSocketAddress(mSocket);
} else {
return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED);
}
}
我們接下來看一下LocalSocket相關的類,了解一下這個socket是如何connect和get的。 LocalSocket*相關的類定義在 frameworks/base/core/java/android/net/ |->LocalSocket.java
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_net_LocalSocketImpl),
了解了庫加載的問題後,我們接著看LocalSocketImpl.java中使用的幾個重要的函數:
connect() getInputStream() getOutputStream()函數具體內容不具體貼出來,其中可以看到調用了本地方法如read_native(),writeba_native()等。我們就走到native的大門了。打開本地函數文件,看下native中本地函數列表。 frameworks/base/core/jni/android_net_LocalSocketImpl.cpp
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{getOption_native, (Ljava/io/FileDescriptor;I)I, (void*)socket_getOption},
{setOption_native, (Ljava/io/FileDescriptor;III)V, (void*)socket_setOption},
{connectLocal, (Ljava/io/FileDescriptor;Ljava/lang/String;I)V,
(void*)socket_connect_local},
{bindLocal, (Ljava/io/FileDescriptor;Ljava/lang/String;I)V, (void*)socket_bind_local},
{listen_native, (Ljava/io/FileDescriptor;I)V, (void*)socket_listen},
{accept, (Ljava/io/FileDescriptor;Landroid/net/LocalSocketImpl;)Ljava/io/FileDescriptor;, (void*)socket_accept},
{shutdown, (Ljava/io/FileDescriptor;Z)V, (void*)socket_shutdown},
{available_native, (Ljava/io/FileDescriptor;)I, (void*) socket_available},
{pending_native, (Ljava/io/FileDescriptor;)I, (void*) socket_pending},
{read_native, (Ljava/io/FileDescriptor;)I, (void*) socket_read},
{readba_native, ([BIILjava/io/FileDescriptor;)I, (void*) socket_readba},
{writeba_native, ([BIILjava/io/FileDescriptor;)V, (void*) socket_writeba},
{write_native, (ILjava/io/FileDescriptor;)V, (void*) socket_write},
{getPeerCredentials_native,
(Ljava/io/FileDescriptor;)Landroid/net/Credentials;,
(void*) socket_get_peer_credentials}
//,{getSockName_native, (Ljava/io/FileDescriptor;)Ljava/lang/String;,
// (void *) socket_getSockName}
};
int register_android_net_LocalSocketImpl(JNIEnv *env){}
Android 藍牙對戰五子棋項目實現(含人機對戰功能)
上周花了一周時間做的課程設計的項目,實現的功能如下:基本功能:(1) 該APP能夠通過藍牙自動搜索周圍其他使用了該APP的手機,用戶可選擇其中某一個APP發起對戰的要求,
Android沉浸式狀態欄實現
蘋果上的UI基本上都是這個效果,然而Android機上的頂部狀態欄總是和app的主題顏色不搭。還好如今的api19以上的版本,我們也能做出這樣的效果。第一步: // 需
Android網絡編程(十)Retrofit2後篇[請求參數]
前言在上一篇Android網絡編程(九)Retrofit2前篇[基本使用]中我們了解了Retrofit的最基本的GET方式訪問網絡的寫法以及請求參數的簡單介紹。這一篇我們
Android開發仿映客送禮物效果
這裡寫鏈接內容仿映客送小禮物的特效,順便復習一下屬性動畫,話不多說先看效果圖。需求分析可以看到整個動畫有幾部分組成,那我們就把每個部分拆分出來各個擊破。1.要顯示那些內容