相關Service的啓動
圖中主要有如下幾個模塊,DisplayManagerService、MediaRouterService、WifiDisplayAdapter和WifiDisplayController。其中:html
DisplayManagerService用於管理系統顯示設備的生命週期,包含物理屏幕、虛擬屏幕、wifi display等,它用一組DiaplayAdapter來管理這些顯示設備。java
MediaRouterService用於管理各個應用程序的多媒體播放的行爲。android
MediaRouter用於和MediaRouterService交互一塊兒管理多媒體的播放行爲,並維護當前已經配對上的remote display設備,包括Wifi diplay、藍牙A2DP設備、chromecast設備。chrome
WifiDisplayAdapter是用於DisplayManagerService管理Wifi display顯示的adapter。數據庫
WifiDisplayController用於控制掃描wifi display設備、鏈接、斷開等操做。數組
先來順着上面的架構圖看各個Service的啓動。首先來看DisplayManagerService,在SystemServer中先建立一個DisplayManagerService對象,而後調用systemReady方法:session
- public DisplayManagerService(Context context, Handler mainHandler) {
- mContext = context;
- mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
- mHandler = new DisplayManagerHandler(mainHandler.getLooper());
- mUiHandler = UiThread.getHandler();
- mDisplayAdapterListener = new DisplayAdapterListener();
- mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
- mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
- }
- public void systemReady(boolean safeMode, boolean onlyCore) {
- synchronized (mSyncRoot) {
- mSafeMode = safeMode;
- mOnlyCore = onlyCore;
- }
- mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
- }
在DisplayManagerService的構造函數中,首先獲取SYSTEM_HEADLESS屬性,用於代表系統是否支持headless模式,默認爲0。而後建立一個DisplayManagerHandler用於處理DisplayManagerService中的消息,mSigleDisplayDemoMode用於開發模式中。而後給本身發送MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER,咱們到DisplayManagerHandler看如何處理這個消息:
- private final class DisplayManagerHandler extends Handler {
- public DisplayManagerHandler(Looper looper) {
- super(looper, null, true /*async*/);
- }
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:
- registerDefaultDisplayAdapter();
- break;
- case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:
- registerAdditionalDisplayAdapters();
- break;
處理MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER消息就是調用registerDefaultDisplayAdapter來註冊一個默認的DiaplayAdapter,DisplayManagerService維護一組DiaplayAdapter,用於管理這些顯示設備。默認的DiaplayAdapter就是系統的物理屏幕,經過Surface flinger來控制輸出。
- private void registerDefaultDisplayAdapter() {
- // Register default display adapter.
- synchronized (mSyncRoot) {
- if (mHeadless) {
- registerDisplayAdapterLocked(new HeadlessDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
- } else {
- registerDisplayAdapterLocked(new LocalDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
- }
- }
- }
- private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
- mDisplayAdapters.add(adapter);
- adapter.registerLocked();
- }
管理surface finger的知識就不講解了。接着來看systemReady函數中會發送MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS,這裏就會調用registerAdditionalDisplayAdapters來註冊其它的顯示設備:
- private void registerAdditionalDisplayAdapters() {
- synchronized (mSyncRoot) {
- if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
- registerOverlayDisplayAdapterLocked();
- registerWifiDisplayAdapterLocked();
- registerVirtualDisplayAdapterLocked();
- }
- }
- }
這裏主要註冊三種DisplayAdapter,一種是OverlayDiaplayAdapter用於開發模式用;一種是WifiDisplayAdapter用於wifi display,也是咱們接下來要講的;還有一種是虛擬顯示。接下來只看registerWifiDisplayAdapterLocked:
- private void registerWifiDisplayAdapterLocked() {
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableWifiDisplay)
- || SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
- mWifiDisplayAdapter = new WifiDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayAdapterListener,
- mPersistentDataStore);
- registerDisplayAdapterLocked(mWifiDisplayAdapter);
- }
- }
這裏會建立WifiDisplayAdapter對象,咱們到它的構造函數中去分析,並調用registerDisplayAdapterLocked添加到mDisplayAdapter中,這裏會回調WifiDisplayAdapter的registerLocked方法:
- public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener,
- PersistentDataStore persistentDataStore) {
- super(syncRoot, context, handler, listener, TAG);
- mHandler = new WifiDisplayHandler(handler.getLooper());
- mPersistentDataStore = persistentDataStore;
- mSupportsProtectedBuffers = context.getResources().getBoolean(
- com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
- mNotificationManager = (NotificationManager)context.getSystemService(
- Context.NOTIFICATION_SERVICE);
- }
- public void registerLocked() {
- super.registerLocked();
- updateRememberedDisplaysLocked();
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- mDisplayController = new WifiDisplayController(
- getContext(), getHandler(), mWifiDisplayListener);
- getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
- new IntentFilter(ACTION_DISCONNECT), null, mHandler);
- }
- });
- }
PersistentDateStore用於持久性存儲連過的wifi display設備,用於在WifiDisplaySettings中顯示前面已經鏈接過的設備列表。SupportsProtectedBuffer與gralloc顯示相關。在registerLocked經過updateRememberedDisplaysLocked去加載/data/system/display-manager-state.xml中保存過的列表,並記錄在mRememberedDisplays中。接着實例化一個WifiDisplayController對象,同時註冊對ACTION_DISCONNECT的receiver。接着到WifiDisplayController去分析,注意WifiDisplayController最後一個參數用於回調通知WifiDisplayAdapter相關狀態的改變,好比wifi display打開/關閉、wifi display鏈接/斷開等。
- public WifiDisplayController(Context context, Handler handler, Listener listener) {
- mContext = context;
- mHandler = handler;
- mListener = listener;
- mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);
- mWifiP2pChannel = mWifiP2pManager.initialize(context, handler.getLooper(), null);
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
- intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
- intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
- intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
- context.registerReceiver(mWifiP2pReceiver, intentFilter, null, mHandler);
- ContentObserver settingsObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- updateSettings();
- }
- };
- final ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.WIFI_DISPLAY_ON), false, settingsObserver);
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, settingsObserver);
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.WIFI_DISPLAY_WPS_CONFIG), false, settingsObserver);
- updateSettings();
- }
這裏主要註冊WifiP2pReceiver用於接收處理WIFI_P2P_STATE_CHANGED_ACTION、WIFI_P2P_PEERS_CHANGED_ACTION、WIFI_P2P_CONNECTION_CHANGED_ACTION、WIFI_P2P_THIS_DEVICE_CHANGED_ACTION消息,而後註冊ContentObserver來監控Settings.Global這個數據庫裏面的WIFI_DISPLAY_ON、WIFI_DISPLAY_CERTIFICATION_ON和WIFI_DISPLAY_WPS_CONFIG,這裏比較重要,咱們後面會看到在WifiDisplaySettings裏面enable wifi display的時候,就會走到這個地方來。接着調用updateSettings來處理默認是否打開Wifi display,這裏默認是關閉的,咱們後面再來分析這一塊。架構
接着來看MediaRouterService和MediaRouter,MediaRouter經過AIDL調用MediaRouterService的實現來完成一些工做。在SystemServer啓動MediaRouterService的時候,主要建立一個MediaRouterService,而後調用它的systemRunning方法,代碼以下:app
- public MediaRouterService(Context context) {
- mContext = context;
- Watchdog.getInstance().addMonitor(this);
- }
- public void systemRunning() {
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) {
- switchUser();
- }
- }
- }, filter);
- switchUser();
- }
上面的方法比較簡單,主要就是接收ACTION_USER_SWITCHED,這是關於多用戶切換的操做。MediaRouterService的工做比較少,主要都是MediaRouter經過AIDL調用完成,接下來去看MediaRouter的部分,在Android官方文檔中有說明MediaRouter的調用方法:
A MediaRouter is retrieved through Context.getSystemService()
of a Context.MEDIA_ROUTER_SERVICE
. 這樣系統是實例化一個MediaRouter對象並返回,下面來看它的構造函數:
less
- public MediaRouter(Context context) {
- synchronized (Static.class) {
- if (sStatic == null) {
- final Context appContext = context.getApplicationContext();
- sStatic = new Static(appContext);
- sStatic.startMonitoringRoutes(appContext);
- }
- }
- }
- Static(Context appContext) {
- mAppContext = appContext;
- mResources = Resources.getSystem();
- mHandler = new Handler(appContext.getMainLooper());
- IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
- mAudioService = IAudioService.Stub.asInterface(b);
- mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
- mMediaRouterService = IMediaRouterService.Stub.asInterface(
- ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
- mSystemCategory = new RouteCategory(
- com.android.internal.R.string.default_audio_route_category_name,
- ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
- mSystemCategory.mIsSystem = true;
- mCanConfigureWifiDisplays = appContext.checkPermission(
- Manifest.permission.CONFIGURE_WIFI_DISPLAY,
- Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED;
- }
MediaRouter中主要經過Static對象來實現其大多數的方法,Static就是一個單例模式,先看Static的構造函數,也能夠經過上面的圖看到,MediaRouter包含DisplayManager對象和MediaRouterService的BpBinder引用,MediaRouter還持有AudioService的BpBind,用於控制audio數據的輸出設備,例如能夠用於藍牙A2DP中使用。接着看Static的startMonitoringRoutes方法:
- void startMonitoringRoutes(Context appContext) {
- mDefaultAudioVideo = new RouteInfo(mSystemCategory);
- mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
- mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
- mDefaultAudioVideo.updatePresentationDisplay();
- addRouteStatic(mDefaultAudioVideo);
- // This will select the active wifi display route if there is one.
- updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
- appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(),
- new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
- appContext.registerReceiver(new VolumeChangeReceiver(),
- new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
- mDisplayService.registerDisplayListener(this, mHandler);
- // Bind to the media router service.
- rebindAsUser(UserHandle.myUserId());
- // Select the default route if the above didn't sync us up
- // appropriately with relevant system state.
- if (mSelectedRoute == null) {
- selectDefaultRouteStatic();
- }
- }
首先註冊系統中默認的AudioVideo輸出設備,若是有處於活動狀態的wifi display鏈接,就記錄下當前處於活動鏈接的設備,默認爲空。上面會註冊兩個broadcastReceiver,一個用於接收ACTION_WIFI_DISPLAY_STATUS_CHANGED,另外一個接收VOLUME_CHANGED_ACTION,咱們主要看前面接收ACTION_WIFI_DISPLAY_STATUS_CHANGED的receiver,以下:
- static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
- updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
- DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
- }
- }
mFeatureState | 代表如今wifi display是關閉仍是打開狀態 |
mScanState | 表現如今wifi display是否在scanning狀態 |
mActiveDisplayState | 代表如今wifi display是在鏈接仍是無鏈接狀態 |
mActiveDisplay | 處於正在鏈接或者鏈接中的WifiDisplay對象 |
mDisplays | 掃描到的WifiDisplay對象數組 |
mSessionInfo | 用於過Miracast認證時用 |
而後向DisplayManager註冊一個回調函數,當有顯示設備增長、刪除或者改變的時候,就會有相應的回調函數來通知Static對象。接着綁定MediaRouterService:
- void rebindAsUser(int userId) {
- if (mCurrentUserId != userId || userId < 0 || mClient == null) {
- mCurrentUserId = userId;
- try {
- Client client = new Client();
- mMediaRouterService.registerClientAsUser(client,
- mAppContext.getPackageName(), userId);
- mClient = client;
- } catch (RemoteException ex) {
- Log.e(TAG, "Unable to register media router client.", ex);
- }
- publishClientDiscoveryRequest();
- publishClientSelectedRoute(false);
- updateClientState();
- }
- }
Enable WifiDisplay
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- final Context context = getActivity();
- mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
- mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
- mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);
- mWifiP2pChannel = mWifiP2pManager.initialize(context, Looper.getMainLooper(), null);
- addPreferencesFromResource(R.xml.wifi_display_settings);
- setHasOptionsMenu(true);
- }
- public void onStart() {
- super.onStart();
- mStarted = true;
- final Context context = getActivity();
- IntentFilter filter = new IntentFilter();
- filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
- context.registerReceiver(mReceiver, filter);
- getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.WIFI_DISPLAY_ON), false, mSettingsObserver);
- getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, mSettingsObserver);
- getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.WIFI_DISPLAY_WPS_CONFIG), false, mSettingsObserver);
- mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback,
- MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
- update(CHANGE_ALL);
- }
首先註冊對ACTION_WIFI_DISPLAY_STATUS_CHANGED的receiver,這個broadcast會在WifiDisplayAdapter裏面當wifi display的狀態發送改變時發送,包括掃描到新的設備、開始鏈接、鏈接成功、斷開等消息都會被這個receiver接收到,後面咱們會來分析這個receiver幹了什麼,而後在onStart中想MediaRouter對象註冊一個callback函數,用於獲取系統中remote display的相關回調信息。而後相似WifiDisplayController同樣,註冊一些對數據庫改變的ContentObserver。接着來看MediaRouter.addCallback的實現:
- public void addCallback(int types, Callback cb, int flags) {
- CallbackInfo info;
- int index = findCallbackInfo(cb);
- if (index >= 0) {
- info = sStatic.mCallbacks.get(index);
- info.type |= types;
- info.flags |= flags;
- } else {
- info = new CallbackInfo(cb, types, flags, this);
- sStatic.mCallbacks.add(info);
- }
- sStatic.updateDiscoveryRequest();
- }
Static的mCallbacks是一個CopyOnWriteArrayList數組,記錄全部註冊到MediaRouter中的回調函數。若是已經向MediaRouter註冊過這個callback,則更新相關的type和flag;若是沒有註冊,則新建一個CallbackInfo對象並添加到mCallbacks數組中。而後調用Static的updateDiscoveryRequest去更新是否須要發送Discovery request請求:
- void updateDiscoveryRequest() {
- final int count = mCallbacks.size();
- for (int i = 0; i < count; i++) {
- CallbackInfo cbi = mCallbacks.get(i);
- if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
- | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) {
- // Discovery explicitly requested.
- routeTypes |= cbi.type;
- } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) {
- // Discovery only passively requested.
- passiveRouteTypes |= cbi.type;
- } else {
- // Legacy case since applications don't specify the discovery flag.
- // Unfortunately we just have to assume they always need discovery
- // whenever they have a callback registered.
- routeTypes |= cbi.type;
- }
- if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
- activeScan = true;
- if ((cbi.type & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
- activeScanWifiDisplay = true;
- }
- }
- }
- if (routeTypes != 0 || activeScan) {
- // If someone else requests discovery then enable the passive listeners.
- // This is used by the MediaRouteButton and MediaRouteActionProvider since
- // they don't receive lifecycle callbacks from the Activity.
- routeTypes |= passiveRouteTypes;
- }
- // Update wifi display scanning.
- // TODO: All of this should be managed by the media router service.
- if (mCanConfigureWifiDisplays) {
- if (mSelectedRoute != null
- && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {
- // Don't scan while already connected to a remote display since
- // it may interfere with the ongoing transmission.
- activeScanWifiDisplay = false;
- }
- if (activeScanWifiDisplay) {
- if (!mActivelyScanningWifiDisplays) {
- mActivelyScanningWifiDisplays = true;
- mDisplayService.startWifiDisplayScan();
- }
- } else {
- if (mActivelyScanningWifiDisplays) {
- mActivelyScanningWifiDisplays = false;
- mDisplayService.stopWifiDisplayScan();
- }
- }
- }
- }
這個函數體比較長,主要經過註冊的一系列的callback類型來決定是否要進行wifiDisplay scan的動做,根據在WifiDisplaySettings裏面註冊callback的方法: mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback,
MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN),上面函數中的activeScanWifiDisplay會爲true,接着會調用DisplayManagerService中的startWifiDisplayScan,以下圖。
- private void updateScanState() {
- if (mScanRequested && mWfdEnabled && mDesiredDevice == null) {
- if (!mDiscoverPeersInProgress) {
- Slog.i(TAG, "Starting Wifi display scan.");
- mDiscoverPeersInProgress = true;
- handleScanStarted();
- tryDiscoverPeers();
- }
- } else {
- if (mDiscoverPeersInProgress) {
- // Cancel automatic retry right away.
- mHandler.removeCallbacks(mDiscoverPeers);
- if (mDesiredDevice == null || mDesiredDevice == mConnectedDevice) {
- Slog.i(TAG, "Stopping Wifi display scan.");
- mDiscoverPeersInProgress = false;
- stopPeerDiscovery();
- handleScanFinished();
- }
- }
- }
- }
當初次進入到WifiDisplaySettings中,並無去optionMenu中enable wifi display時,上面code中的mWfdEnabled爲false,因此會跳出前面的if語句;後面的else語句中mDiscoverPeersInProgress也爲false,由於這個變量只有在scan時纔會被置爲true。
接着來分析當用戶點擊了optionMenu中enable wifi display後的流程,先看WifiDisplaySettings的代碼:
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_ID_ENABLE_WIFI_DISPLAY:
- mWifiDisplayOnSetting = !item.isChecked();
- item.setChecked(mWifiDisplayOnSetting);
- Settings.Global.putInt(getContentResolver(),
- Settings.Global.WIFI_DISPLAY_ON, mWifiDisplayOnSetting ? 1 : 0);
這裏首先改變OptionMenu的狀態,並置mWifiDisplayOnSetting爲上次MenuItem相反的狀態,而後改變Settings.Global數據庫中WIFI_DISPLAY_ON的指爲1。前面咱們介紹過,在WifiDisplaySettings和WifiDisplayController都有註冊ContentObserver來監控這個值的變化。其中WifiDisplaySettings在監控到這個值的變化後,主要是調用MediaRouter和DisplayManager的方法去獲取系統中已經掃描到的remote display設備,並更新到listview列表上,顯然這時候尚未開始scan,因此listview列表爲空。接着看WifiDisplayController處理ContentOberver的代碼:
- private void updateSettings() {
- final ContentResolver resolver = mContext.getContentResolver();
- mWifiDisplayOnSetting = Settings.Global.getInt(resolver,
- Settings.Global.WIFI_DISPLAY_ON, 0) != 0;
- mWifiDisplayCertMode = Settings.Global.getInt(resolver,
- Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0;
- mWifiDisplayWpsConfig = WpsInfo.INVALID;
- if (mWifiDisplayCertMode) {
- mWifiDisplayWpsConfig = Settings.Global.getInt(resolver,
- Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID);
- }
- updateWfdEnableState();
- }
這裏主要置mWifiDisplayOnSetting爲true,而後就調用updateWfdEnableState去更新wfd的狀態:
- private void updateWfdEnableState() {
- if (mWifiDisplayOnSetting && mWifiP2pEnabled) {
- // WFD should be enabled.
- if (!mWfdEnabled && !mWfdEnabling) {
- mWfdEnabling = true;
- WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
- wfdInfo.setWfdEnabled(true);
- wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE);
- wfdInfo.setSessionAvailable(true);
- wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);
- wfdInfo.setMaxThroughput(MAX_THROUGHPUT);
- mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener() {
- @Override
- public void onSuccess() {
- if (DEBUG) {
- Slog.d(TAG, "Successfully set WFD info.");
- }
- if (mWfdEnabling) {
- mWfdEnabling = false;
- mWfdEnabled = true;
- reportFeatureState();
- updateScanState();
- }
- }
- @Override
- public void onFailure(int reason) {
- if (DEBUG) {
- Slog.d(TAG, "Failed to set WFD info with reason " + reason + ".");
- }
- mWfdEnabling = false;
- }
- });
- }
首先調用WifiP2pMananger的setWFDInfo把與wifi display相關的信息設置到wpa_supplicant,這些信息包括enable狀態、device type(指爲source仍是sink)、session available(當前能否鏈接)、control port(用於rtsp鏈接)、maxThroughput(吞吐量),這些信息最終會隨着P2P的IE信息在掃描階段被對方知道。接着會調用reportFeatureState來通知WifiDisplayAdapter相應狀態的變化,這裏咱們先看一下下面的流程圖來了解一下WifiDisplaySettings、MediaRouter、DisplayMananger、WifiDisplayAdapter、WifiDisplayController是如何相互通知信息的,這其中有簡單的callback,也有發送/接收broadcast,以下圖:
- private void reportFeatureState() {
- final int featureState = computeFeatureState();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onFeatureStateChanged(featureState);
- }
- });
- }
- private int computeFeatureState() {
- if (!mWifiP2pEnabled) {
- return WifiDisplayStatus.FEATURE_STATE_DISABLED;
- }
- return mWifiDisplayOnSetting ? WifiDisplayStatus.FEATURE_STATE_ON :
- WifiDisplayStatus.FEATURE_STATE_OFF;
- }
- public void onFeatureStateChanged(int featureState) {
- synchronized (getSyncRoot()) {
- if (mFeatureState != featureState) {
- mFeatureState = featureState;
- scheduleStatusChangedBroadcastLocked();
- }
- }
- }
- private void scheduleStatusChangedBroadcastLocked() {
- mCurrentStatus = null;
- if (!mPendingStatusChangeBroadcast) {
- mPendingStatusChangeBroadcast = true;
- mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);
- }
- }
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SEND_STATUS_CHANGE_BROADCAST:
- handleSendStatusChangeBroadcast();
- break;
- case MSG_UPDATE_NOTIFICATION:
- handleUpdateNotification();
- break;
- }
- ate void handleSendStatusChangeBroadcast() {
- final Intent intent;
- synchronized (getSyncRoot()) {
- if (!mPendingStatusChangeBroadcast) {
- return;
- }
- mPendingStatusChangeBroadcast = false;
- intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
- getWifiDisplayStatusLocked());
- }
- // Send protected broadcast about wifi display status to registered receivers.
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
上面的代碼都比較簡單,在getWifiDisplayStatusLocked中會根據WifiDisplayAdapter中的變量mFeatureState、mScanState、mActiveDisplayState、mActiveDisplay、mDisplays、mSessionInfo去構造一個WifiDisplayStatus對象,在前面咱們介紹過這幾個變量的含義了,固然這幾個變量會從WifiDisplayListener的各個callback分別去改變本身的值。接着咱們到MediaRouter中去看如何處理這個broadcastReceiver,前面咱們已經講過了,WifiDisplayStatusChangedReceiver會接收這個broadcast,而後調用updateWifiDisplayStatus來更新狀態,咱們稍後來看這部分的實現。回到WifiDisplayController的updateWfdEnableState方法中,接着會調用updateScanState方法開始掃描WifiDisplay設備:
- private void updateScanState() {
- if (mScanRequested && mWfdEnabled && mDesiredDevice == null) {
- if (!mDiscoverPeersInProgress) {
- Slog.i(TAG, "Starting Wifi display scan.");
- mDiscoverPeersInProgress = true;
- handleScanStarted();
- tryDiscoverPeers();
- }
- }
handleScanStarted用於通知WifiDisplayAdapter掃描開始了,固然WifiDisplayAdapter也會發broadcast給MediaRouter。接着會調用tryDiscoverPeers:
- private void tryDiscoverPeers() {
- mWifiP2pManager.discoverPeers(mWifiP2pChannel, new ActionListener() {
- @Override
- public void onSuccess() {
- if (DEBUG) {
- Slog.d(TAG, "Discover peers succeeded. Requesting peers now.");
- }
- if (mDiscoverPeersInProgress) {
- requestPeers();
- }
- }
- mHandler.postDelayed(mDiscoverPeers, DISCOVER_PEERS_INTERVAL_MILLIS);
- }
這裏調用WifiP2pManager的discoverPeers去掃描全部的p2p設備,比較重要是後面有發一個delay message,表示每間隔10秒就去發一下P2P_FIND。固然下了P2P_FIND命令後,並不能立刻獲取到對方設備,但由於咱們前面有講過在/data/system/display-manager-state.xml有保存過前面鏈接過的設備列表,因此這裏會立刻調用requestPeers去獲取設備列表。固然在WifiDisplayController也會註冊對WIFI_P2P_PEERS_CHANGED_ACTION的receiver,最終仍是會調用reqeustPeers去獲取全部掃描到的設備列表,下面來看這個函數的實現:
- private void requestPeers() {
- mWifiP2pManager.requestPeers(mWifiP2pChannel, new PeerListListener() {
- @Override
- public void onPeersAvailable(WifiP2pDeviceList peers) {
- if (DEBUG) {
- Slog.d(TAG, "Received list of peers.");
- }
- mAvailableWifiDisplayPeers.clear();
- for (WifiP2pDevice device : peers.getDeviceList()) {
- if (DEBUG) {
- Slog.d(TAG, " " + describeWifiP2pDevice(device));
- }
- if (isWifiDisplay(device)) {
- mAvailableWifiDisplayPeers.add(device);
- }
- }
- if (mDiscoverPeersInProgress) {
- handleScanResults();
- }
- }
- });
- }
首先從掃描的設備列表中過濾掉不能作wifi display的設備,主要從三個方面過濾,一是純粹的P2P設備,不會待用WfdInfo;第二是帶有WfdInfo,可是暫時沒有被enable;三是隻能是PrimarySinkDevice,看起來Android還不支持SecondSink。並將過濾掉剩下的設備加入到mAvailableWifiDisplayPeers列表中,接着調用handleScanResults來組裝WifiDisplay列表數組並notify給WifiDisplayAdapter:
- private void handleScanResults() {
- final int count = mAvailableWifiDisplayPeers.size();
- final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);
- for (int i = 0; i < count; i++) {
- WifiP2pDevice device = mAvailableWifiDisplayPeers.get(i);
- displays[i] = createWifiDisplay(device);
- updateDesiredDevice(device);
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onScanResults(displays);
- }
- });
- }
這裏首先根據mAvailableWifiDisplayPeers的數目建立一個WifiDisplay數組,而後一個個構造WifiDisplay對象,WifiDiplay對象包含如下幾個變量:
mDeviceAddress | 設備的Mac地址 |
mDeviceName | 設備的名字 |
mDeviceAlias | 設備的別名,通常爲NULL |
mIsAvailable | 是否可用狀態 |
mCanConnect | WfdInfo中的SessionAvailable是否爲1 |
mIsRemembered | 是否被記錄的 |
- public void onScanResults(WifiDisplay[] availableDisplays) {
- synchronized (getSyncRoot()) {
- availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
- availableDisplays);
- boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);
- // Check whether any of the available displays changed canConnect status.
- for (int i = 0; !changed && i<availableDisplays.length; i++) {
- changed = availableDisplays[i].canConnect()
- != mAvailableDisplays[i].canConnect();
- }
- if (changed) {
- mAvailableDisplays = availableDisplays;
- fixRememberedDisplayNamesFromAvailableDisplaysLocked();
- updateDisplaysLocked();
- scheduleStatusChangedBroadcastLocked();
- }
- }
- }
這裏首先調用PersistentDateStore的applyWifiDisplayAliases方法去判斷掃描到的設備中有沒有之前鏈接過並記錄下來的wifi display設備,比較方法是比較二者的MAC地址,若是在PersistentDateStore中找到,再比較二者的別名(Alias),若是不相同則更新results列表,細節的代碼能夠看applyWifiDisplayAlias中的實現。
- public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {
- WifiDisplay[] results = displays;
- if (results != null) {
- int count = displays.length;
- for (int i = 0; i < count; i++) {
- WifiDisplay result = applyWifiDisplayAlias(displays[i]);
- if (result != displays[i]) {
- if (results == displays) {
- results = new WifiDisplay[count];
- System.arraycopy(displays, 0, results, 0, count);
- }
- results[i] = result;
- }
- }
- }
- return results;
- }
回到上面的onScanResults中,接着判斷剛掃描到的設備列表(availableDisplays)和以前存儲的設備列表(mAvailableDisplays)之間有沒有變化,能夠數組內容以及是否可連兩個方面檢查。若是有變化,則把剛掃描到的設備列表(availableDisplays)賦值給存儲的設備列表(mAvailableDisplays)。接下來調用fixRememberedDisplayNamesFromAvailableDisplaysLocked來更新PersistentDateStore中存儲的已經鏈接過的wifi display設備,更新的條件是設備的MAC地址同樣,但設備的DeviceName和DeviceAlias有變化,這是就要更新到PersistentDateStore中,代碼以下:
- private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() {
- boolean changed = false;
- for (int i = 0; i < mRememberedDisplays.length; i++) {
- WifiDisplay rememberedDisplay = mRememberedDisplays[i];
- WifiDisplay availableDisplay = findAvailableDisplayLocked(
- rememberedDisplay.getDeviceAddress());
- if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) {
- mRememberedDisplays[i] = availableDisplay;
- changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay);
- }
- }
- if (changed) {
- mPersistentDataStore.saveIfNeeded();
- }
- }
- private void updateDisplaysLocked() {
- List<WifiDisplay> displays = new ArrayList<WifiDisplay>(
- mAvailableDisplays.length + mRememberedDisplays.length);
- boolean[] remembered = new boolean[mAvailableDisplays.length];
- for (WifiDisplay d : mRememberedDisplays) {
- boolean available = false;
- for (int i = 0; i < mAvailableDisplays.length; i++) {
- if (d.equals(mAvailableDisplays[i])) {
- remembered[i] = available = true;
- break;
- }
- }
- if (!available) {
- displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
- d.getDeviceAlias(), false, false, true));
- }
- }
- for (int i = 0; i < mAvailableDisplays.length; i++) {
- WifiDisplay d = mAvailableDisplays[i];
- displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
- d.getDeviceAlias(), true, d.canConnect(), remembered[i]));
- }
- mDisplays = displays.toArray(WifiDisplay.EMPTY_ARRAY);
- }
上面的實現中先從mRememberedDisplays逐個添加wifi display設備到displays數組中,若是在mAvailableDisplays有相同的設備,則不添加到displays數組;後面再把mAvailableDisplays全部元素添加到displays數組,並所有賦值給mDisplays數組。
- static void updateWifiDisplayStatus(WifiDisplayStatus status) {
- WifiDisplay[] displays;
- WifiDisplay activeDisplay;
- if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
- displays = status.getDisplays();
- activeDisplay = status.getActiveDisplay();
- } else {
- displays = WifiDisplay.EMPTY_ARRAY;
- activeDisplay = null;
- }
- String activeDisplayAddress = activeDisplay != null ?
- activeDisplay.getDeviceAddress() : null;
- // Add or update routes.
- for (int i = 0; i < displays.length; i++) {
- final WifiDisplay d = displays[i];
- if (shouldShowWifiDisplay(d, activeDisplay)) {
- RouteInfo route = findWifiDisplayRoute(d);
- if (route == null) {
- route = makeWifiDisplayRoute(d, status);
- addRouteStatic(route);
- } else {
- String address = d.getDeviceAddress();
- boolean disconnected = !address.equals(activeDisplayAddress)
- && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);
- updateWifiDisplayRoute(route, d, status, disconnected);
- }
- if (d.equals(activeDisplay)) {
- selectRouteStatic(route.getSupportedTypes(), route, false);
- }
- }
- }
- // Remove stale routes.
- for (int i = sStatic.mRoutes.size(); i-- > 0; ) {
- RouteInfo route = sStatic.mRoutes.get(i);
- if (route.mDeviceAddress != null) {
- WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);
- if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) {
- removeRouteStatic(route);
- }
- }
- }
- sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;
- }
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
- scheduleUpdate(CHANGE_WIFI_DISPLAY_STATUS);
- }
- }
- };
從MediaRouter中的callback消息也會進入到scheduleUpdate中,只是後面的參數不同,經過callback進來的參數是CHANGE_ROUTES,而broadcast進來的參數是CHANGE_WIFI_DISPLAY_STATUS,來看scheduleUpdate,最終實現是mUpdateRunnable 中:
- private void update(int changes) {
- boolean invalidateOptions = false;
- // Update wifi display state.
- if ((changes & CHANGE_WIFI_DISPLAY_STATUS) != 0) {
- mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
- // The wifi display feature state may have changed.
- invalidateOptions = true;
- }
- // Rebuild the routes.
- final PreferenceScreen preferenceScreen = getPreferenceScreen();
- preferenceScreen.removeAll();
- // Add all known remote display routes.
- final int routeCount = mRouter.getRouteCount();
- for (int i = 0; i < routeCount; i++) {
- MediaRouter.RouteInfo route = mRouter.getRouteAt(i);
- if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) {
- preferenceScreen.addPreference(createRoutePreference(route));
- }
- }
- // Additional features for wifi display routes.
- if (mWifiDisplayStatus != null
- && mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
- // Add all unpaired wifi displays.
- for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) {
- if (!display.isRemembered() && display.isAvailable()
- && !display.equals(mWifiDisplayStatus.getActiveDisplay())) {
- preferenceScreen.addPreference(new UnpairedWifiDisplayPreference(
- getActivity(), display));
- }
- }
- }
- }
上面的代碼比較簡單,一個是從MediaRouter中獲取mRoutes數組中存着的remote display設備;一個是從broadcast中的WifiDisplayStatus對象中獲取mDisplay數組,二者相互合併構建整個listview展示給用戶。至此,wifi display的掃描流程就介紹完了,下面是總體的流程圖: