System Server是Android系统的核心,他在Dalvik虚拟机启动后立即开始初始化和运行


最近在学习Android 4.4上面的WifiDisplay(Miracast)相关的模块,这里先从WifiDisplay用到的各个Service讲起,然后再从WifiDisplaySettings里面讲解打开wfd的流程。首先看下面的主要几个Service的架构图:

android wifi 开关流程 android wifi display_android wifi 开关流程

相关Service的启动

图中主要有以下几个模块,DisplayManagerService、MediaRouterService、WifiDisplayAdapter和WifiDisplayController。其中:

DisplayManagerService用于管理系统显示设备的生命周期,包含物理屏幕、虚拟屏幕、wifi display等,它用一组DiaplayAdapter来管理这些显示设备。

MediaRouterService用于管理各个应用程序的多媒体播放的行为。

MediaRouter用于和MediaRouterService交互一起管理多媒体的播放行为,并维护当前已经配对上的remote display设备,包括Wifi diplay、蓝牙A2DP设备、chromecast设备。

WifiDisplayAdapter是用于DisplayManagerService管理Wifi display显示的adapter。

WifiDisplayController用于控制扫描wifi display设备、连接、断开等操作。

 

先来顺着上面的架构图看各个Service的启动。首先来看DisplayManagerService,在SystemServer中先创建一个DisplayManagerService对象,然后调用systemReady方法:

 

1. public DisplayManagerService(Context context, Handler mainHandler) {  
2.     mContext = context;  
3. "1");  
4.   
5. new DisplayManagerHandler(mainHandler.getLooper());  
6.     mUiHandler = UiThread.getHandler();  
7. new DisplayAdapterListener();  
8. "persist.demo.singledisplay", false);  
9.   
10.     mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);  
11. }  
12.   
13. publicvoidbooleanboolean onlyCore) {  
14. synchronized (mSyncRoot) {  
15.         mSafeMode = safeMode;  
16.         mOnlyCore = onlyCore;  
17.     }  
18.   
19.     mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);  
20. }

在DisplayManagerService的构造函数中,首先获取SYSTEM_HEADLESS属性,用于表明系统是否支持headless模式,默认为0。然后创建一个DisplayManagerHandler用于处理DisplayManagerService中的消息,mSigleDisplayDemoMode用于开发模式中。然后给自己发送MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER,我们到DisplayManagerHandler看如何处理这个消息:

 

1. privatefinalclassextends Handler {  
2. public DisplayManagerHandler(Looper looper) {  
3. super(looper, null, true/*async*/);  
4.     }  
5.   
6. @Override  
7. publicvoid handleMessage(Message msg) {  
8. switch (msg.what) {  
9. case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:  
10.                 registerDefaultDisplayAdapter();  
11. break;  
12.   
13. case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:  
14.                 registerAdditionalDisplayAdapters();  
15. break;

处理MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER消息就是调用registerDefaultDisplayAdapter来注册一个默认的DiaplayAdapter,DisplayManagerService维护一组DiaplayAdapter,用于管理这些显示设备。默认的DiaplayAdapter就是系统的物理屏幕,通过Surface flinger来控制输出。

 

 


1. privatevoid registerDefaultDisplayAdapter() {  
2. // Register default display adapter.  
3. synchronized (mSyncRoot) {  
4. if (mHeadless) {  
5. new HeadlessDisplayAdapter(  
6.                     mSyncRoot, mContext, mHandler, mDisplayAdapterListener));  
7. else {  
8. new LocalDisplayAdapter(  
9.                     mSyncRoot, mContext, mHandler, mDisplayAdapterListener));  
10.         }  
11.     }  
12. }  
13.   
14. privatevoid registerDisplayAdapterLocked(DisplayAdapter adapter) {  
15.     mDisplayAdapters.add(adapter);  
16.     adapter.registerLocked();  
17. }


管理surface finger的知识就不讲解了。接着来看systemReady函数中会发送MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS,这里就会调用registerAdditionalDisplayAdapters来注册其它的显示设备:

 

 


1. privatevoid registerAdditionalDisplayAdapters() {  
2. synchronized (mSyncRoot) {  
3. if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {  
4.             registerOverlayDisplayAdapterLocked();  
5.             registerWifiDisplayAdapterLocked();  
6.             registerVirtualDisplayAdapterLocked();  
7.         }  
8.     }  
9. }


这里主要注册三种DisplayAdapter,一种是OverlayDiaplayAdapter用于开发模式用;一种是WifiDisplayAdapter用于wifi display,也是我们接下来要讲的;还有一种是虚拟显示。接下来只看registerWifiDisplayAdapterLocked:

 

 


    1. privatevoid registerWifiDisplayAdapterLocked() {  
    2. if (mContext.getResources().getBoolean(  
    3.             com.android.internal.R.bool.config_enableWifiDisplay)  
    4. 1) == 1) {  
    5. new WifiDisplayAdapter(  
    6.                 mSyncRoot, mContext, mHandler, mDisplayAdapterListener,  
    7.                 mPersistentDataStore);  
    8.         registerDisplayAdapterLocked(mWifiDisplayAdapter);  
    9.     }  
    10. }

    这里会创建WifiDisplayAdapter对象,我们到它的构造函数中去分析,并调用registerDisplayAdapterLocked添加到mDisplayAdapter中,这里会回调WifiDisplayAdapter的registerLocked方法:

     

     


    1. public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,  
    2.         Context context, Handler handler, Listener listener,  
    3.         PersistentDataStore persistentDataStore) {  
    4. super(syncRoot, context, handler, listener, TAG);  
    5. new WifiDisplayHandler(handler.getLooper());  
    6.     mPersistentDataStore = persistentDataStore;  
    7.     mSupportsProtectedBuffers = context.getResources().getBoolean(  
    8.             com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);  
    9.     mNotificationManager = (NotificationManager)context.getSystemService(  
    10.             Context.NOTIFICATION_SERVICE);  
    11. }  
    12.   
    13. publicvoid registerLocked() {  
    14. super.registerLocked();  
    15.   
    16.     updateRememberedDisplaysLocked();  
    17.   
    18. new Runnable() {  
    19. @Override  
    20. publicvoid run() {  
    21. new WifiDisplayController(  
    22.                     getContext(), getHandler(), mWifiDisplayListener);  
    23.   
    24.             getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,  
    25. newnull, mHandler);  
    26.         }  
    27.     });  
    28. }


    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连接/断开等。

     

     

    1. public WifiDisplayController(Context context, Handler handler, Listener listener) {  
    2.     mContext = context;  
    3.     mHandler = handler;  
    4.     mListener = listener;  
    5.   
    6.     mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);  
    7. null);  
    8.   
    9. new IntentFilter();  
    10.     intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);  
    11.     intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);  
    12.     intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);  
    13.     intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);  
    14. null, mHandler);  
    15.   
    16. new ContentObserver(mHandler) {  
    17. @Override  
    18. publicvoidboolean selfChange, Uri uri) {  
    19.             updateSettings();  
    20.         }  
    21.     };  
    22.   
    23. final ContentResolver resolver = mContext.getContentResolver();  
    24.     resolver.registerContentObserver(Settings.Global.getUriFor(  
    25. false, settingsObserver);  
    26.     resolver.registerContentObserver(Settings.Global.getUriFor(  
    27. false, settingsObserver);  
    28.     resolver.registerContentObserver(Settings.Global.getUriFor(  
    29. false, settingsObserver);  
    30.     updateSettings();  
    31. }

    这里主要注册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方法,代码如下:

     


    1. public MediaRouterService(Context context) {  
    2.     mContext = context;  
    3. this);  
    4. }  
    5.   
    6. publicvoid systemRunning() {  
    7. new IntentFilter(Intent.ACTION_USER_SWITCHED);  
    8. new BroadcastReceiver() {  
    9. @Override  
    10. publicvoid onReceive(Context context, Intent intent) {  
    11. if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) {  
    12.                 switchUser();  
    13.             }  
    14.         }  
    15.     }, filter);  
    16.   
    17.     switchUser();  
    18. }


    上面的方法比较简单,主要就是接收ACTION_USER_SWITCHED,这是关于多用户切换的操作。MediaRouterService的工作比较少,主要都是MediaRouter通过AIDL调用完成,接下来去看MediaRouter的部分,在Android官方文档中有说明MediaRouter的调用方法:

     

    A MediaRouter is retrieved through Context.getSystemService()Context.MEDIA_ROUTER_SERVICE. 这样系统是实例化一个MediaRouter对象并返回,下面来看它的构造函数:

     

    1. public MediaRouter(Context context) {  
    2. synchronizedclass) {  
    3. ifnull) {  
    4. final Context appContext = context.getApplicationContext();  
    5. new Static(appContext);  
    6.             sStatic.startMonitoringRoutes(appContext);  
    7.         }  
    8.     }  
    9. }  
    10.   
    11.     Static(Context appContext) {  
    12.         mAppContext = appContext;  
    13.         mResources = Resources.getSystem();  
    14. new Handler(appContext.getMainLooper());  
    15.   
    16.         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);  
    17.         mAudioService = IAudioService.Stub.asInterface(b);  
    18.   
    19.         mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);  
    20.   
    21.         mMediaRouterService = IMediaRouterService.Stub.asInterface(  
    22.                 ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));  
    23.   
    24. new RouteCategory(  
    25.                 com.android.internal.R.string.default_audio_route_category_name,  
    26. false);  
    27. true;  
    28.   
    29.         mCanConfigureWifiDisplays = appContext.checkPermission(  
    30.                 Manifest.permission.CONFIGURE_WIFI_DISPLAY,  
    31.                 Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED;  
    32.     }

    MediaRouter中主要通过Static对象来实现其大多数的方法,Static就是一个单例模式,先看Static的构造函数,也可以通过上面的图看到,MediaRouter包含DisplayManager对象和MediaRouterService的BpBinder引用,MediaRouter还持有AudioService的BpBind,用于控制audio数据的输出设备,例如可以用于蓝牙A2DP中使用。接着看Static的startMonitoringRoutes方法:

     

    1. void startMonitoringRoutes(Context appContext) {  
    2. new RouteInfo(mSystemCategory);  
    3.     mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;  
    4.     mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;  
    5.     mDefaultAudioVideo.updatePresentationDisplay();  
    6.     addRouteStatic(mDefaultAudioVideo);  
    7.   
    8. // This will select the active wifi display route if there is one.  
    9.     updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());  
    10.   
    11. new WifiDisplayStatusChangedReceiver(),  
    12. new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));  
    13. new VolumeChangeReceiver(),  
    14. new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));  
    15.   
    16. this, mHandler);  
    17.   
    18.   
    19. // Bind to the media router service.  
    20.     rebindAsUser(UserHandle.myUserId());  
    21.   
    22. // Select the default route if the above didn't sync us up  
    23. // appropriately with relevant system state.  
    24. ifnull) {  
    25.         selectDefaultRouteStatic();  
    26.     }  
    27. }


     

    首先注册系统中默认的AudioVideo输出设备,如果有处于活动状态的wifi display连接,就记录下当前处于活动连接的设备,默认为空。上面会注册两个broadcastReceiver,一个用于接收ACTION_WIFI_DISPLAY_STATUS_CHANGED,另一个接收VOLUME_CHANGED_ACTION,我们主要看前面接收ACTION_WIFI_DISPLAY_STATUS_CHANGED的receiver,如下:


    1. staticclassextends BroadcastReceiver {  
    2. @Override  
    3. publicvoid onReceive(Context context, Intent intent) {  
    4. if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {  
    5.             updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(  
    6.                     DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));  
    7.         }  
    8.     }

    上面接收ACTION_WIFI_DISPLAY_STATUS_CHANGED,从Intent里面取出WifiDisplayStatus对象,WifiDisplayStatus内部的变量如下:

     

     

    mFeatureState

    表明现在wifi display是关闭还是打开状态

    mScanState

    表现现在wifi display是否在scanning状态

    mActiveDisplayState

    表明现在wifi display是在连接还是无连接状态

    mActiveDisplay

    处于正在连接或者连接中的WifiDisplay对象

    mDisplays

    扫描到的WifiDisplay对象数组

    mSessionInfo

    用于过Miracast认证时用

     

     

    然后向DisplayManager注册一个回调函数,当有显示设备增加、删除或者改变的时候,就会有相应的回调函数来通知Static对象。接着绑定MediaRouterService:

     


    1. voidint userId) {  
    2. if0null) {  
    3.   
    4.         mCurrentUserId = userId;  
    5.   
    6. try {  
    7. new Client();  
    8.             mMediaRouterService.registerClientAsUser(client,  
    9.                     mAppContext.getPackageName(), userId);  
    10.             mClient = client;  
    11. catch (RemoteException ex) {  
    12. "Unable to register media router client.", ex);  
    13.         }  
    14.   
    15.         publishClientDiscoveryRequest();  
    16. false);  
    17.         updateClientState();  
    18.     }  
    19. }

     

     

    Enable WifiDisplay

    当用户进入WifiDisplaySettings界面,会调用其对应的onCreate和onStart方法:

      1. publicvoid onCreate(Bundle icicle) {  
      2. super.onCreate(icicle);  
      3.   
      4. final Context context = getActivity();  
      5.     mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);  
      6.     mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);  
      7.     mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);  
      8. null);  
      9.   
      10.     addPreferencesFromResource(R.xml.wifi_display_settings);  
      11. true);  
      12. }  
      13.   
      14. publicvoid onStart() {  
      15. super.onStart();  
      16. true;  
      17.   
      18. final Context context = getActivity();  
      19. new IntentFilter();  
      20.     filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);  
      21.     context.registerReceiver(mReceiver, filter);  
      22.   
      23.     getContentResolver().registerContentObserver(Settings.Global.getUriFor(  
      24. false, mSettingsObserver);  
      25.     getContentResolver().registerContentObserver(Settings.Global.getUriFor(  
      26. false, mSettingsObserver);  
      27.     getContentResolver().registerContentObserver(Settings.Global.getUriFor(  
      28. false, mSettingsObserver);  
      29.   
      30.     mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback,  
      31.             MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);  
      32.   
      33.     update(CHANGE_ALL);  
      34. }

      首先注册对ACTION_WIFI_DISPLAY_STATUS_CHANGED的receiver,这个broadcast会在WifiDisplayAdapter里面当wifi display的状态发送改变时发送,包括扫描到新的设备、开始连接、连接成功、断开等消息都会被这个receiver接收到,后面我们会来分析这个receiver干了什么,然后在onStart中想MediaRouter对象注册一个callback函数,用于获取系统中remote display的相关回调信息。然后类似WifiDisplayController一样,注册一些对数据库改变的ContentObserver。接着来看MediaRouter.addCallback的实现:


        1. publicvoidintint flags) {  
        2.     CallbackInfo info;  
        3. int index = findCallbackInfo(cb);  
        4. if0) {  
        5.         info = sStatic.mCallbacks.get(index);  
        6.         info.type |= types;  
        7.         info.flags |= flags;  
        8. else {  
        9. newthis);  
        10.         sStatic.mCallbacks.add(info);  
        11.     }  
        12.     sStatic.updateDiscoveryRequest();  
        13. }

        Static的mCallbacks是一个CopyOnWriteArrayList数组,记录所有注册到MediaRouter中的回调函数。如果已经向MediaRouter注册过这个callback,则更新相关的type和flag;如果没有注册,则新建一个CallbackInfo对象并添加到mCallbacks数组中。然后调用Static的updateDiscoveryRequest去更新是否需要发送Discovery request请求:

         


        1. void updateDiscoveryRequest() {  
        2. finalint count = mCallbacks.size();  
        3. forint0; i < count; i++) {  
        4.                 CallbackInfo cbi = mCallbacks.get(i);  
        5. if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN  
        6. 0) {  
        7. // Discovery explicitly requested.  
        8.                     routeTypes |= cbi.type;  
        9. elseif0) {  
        10. // Discovery only passively requested.  
        11.                     passiveRouteTypes |= cbi.type;  
        12. else {  
        13. // Legacy case since applications don't specify the discovery flag.  
        14. // Unfortunately we just have to assume they always need discovery  
        15. // whenever they have a callback registered.  
        16.                     routeTypes |= cbi.type;  
        17.                 }  
        18. if0) {  
        19. true;  
        20. if0) {  
        21. true;  
        22.                     }  
        23.                 }  
        24.             }  
        25. if0 || activeScan) {  
        26. // If someone else requests discovery then enable the passive listeners.  
        27. // This is used by the MediaRouteButton and MediaRouteActionProvider since  
        28. // they don't receive lifecycle callbacks from the Activity.  
        29.                 routeTypes |= passiveRouteTypes;  
        30.             }  
        31.   
        32. // Update wifi display scanning.  
        33. // TODO: All of this should be managed by the media router service.  
        34. if (mCanConfigureWifiDisplays) {  
        35. ifnull  
        36.                         && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {  
        37. // Don't scan while already connected to a remote display since  
        38. // it may interfere with the ongoing transmission.  
        39. false;  
        40.                 }  
        41. if (activeScanWifiDisplay) {  
        42. if (!mActivelyScanningWifiDisplays) {  
        43. true;  
        44.                         mDisplayService.startWifiDisplayScan();  
        45.                     }  
        46. else {  
        47. if (mActivelyScanningWifiDisplays) {  
        48. false;  
        49.                         mDisplayService.stopWifiDisplayScan();  
        50.                     }  
        51.                 }  
        52.             }  
        53.         }


        这个函数体比较长,主要通过注册的一系列的callback类型来决定是否要进行wifiDisplay scan的动作,根据在WifiDisplaySettings里面注册callback的方法:    mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback,

                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN),上面函数中的activeScanWifiDisplay会为true,接着会调用DisplayManagerService中的startWifiDisplayScan,如下图。

        这里会通过WifiDisplayAdapter调用到WifiDisplayController的updateScanState动作,我们到updateScanState中去分析:

         


        1. privatevoid updateScanState() {  
        2. ifnull) {  
        3. if (!mDiscoverPeersInProgress) {  
        4. "Starting Wifi display scan.");  
        5. true;  
        6.             handleScanStarted();  
        7.             tryDiscoverPeers();  
        8.         }  
        9. else {  
        10. if (mDiscoverPeersInProgress) {  
        11. // Cancel automatic retry right away.  
        12.             mHandler.removeCallbacks(mDiscoverPeers);  
        13.   
        14. ifnull || mDesiredDevice == mConnectedDevice) {  
        15. "Stopping Wifi display scan.");  
        16. false;  
        17.                 stopPeerDiscovery();  
        18.                 handleScanFinished();  
        19.             }  
        20.         }  
        21.     }  
        22. }


        当初次进入到WifiDisplaySettings中,并没有去optionMenu中enable wifi display时,上面code中的mWfdEnabled为false,所以会跳出前面的if语句;后面的else语句中mDiscoverPeersInProgress也为false,因为这个变量只有在scan时才会被置为true。

        接着来分析当用户点击了optionMenu中enable wifi display后的流程,先看WifiDisplaySettings的代码:

         


        1. publicboolean onOptionsItemSelected(MenuItem item) {  
        2. switch (item.getItemId()) {  
        3. case MENU_ID_ENABLE_WIFI_DISPLAY:  
        4.             mWifiDisplayOnSetting = !item.isChecked();  
        5.             item.setChecked(mWifiDisplayOnSetting);  
        6.             Settings.Global.putInt(getContentResolver(),  
        7. 10);


        这里首先改变OptionMenu的状态,并置mWifiDisplayOnSetting为上次MenuItem相反的状态,然后改变Settings.Global数据库中WIFI_DISPLAY_ON的指为1。前面我们介绍过,在WifiDisplaySettings和WifiDisplayController都有注册ContentObserver来监控这个值的变化。其中WifiDisplaySettings在监控到这个值的变化后,主要是调用MediaRouter和DisplayManager的方法去获取系统中已经扫描到的remote display设备,并更新到listview列表上,显然这时候还没有开始scan,所以listview列表为空。接着看WifiDisplayController处理ContentOberver的代码:

         


        1. privatevoid updateSettings() {  
        2. final ContentResolver resolver = mContext.getContentResolver();  
        3.     mWifiDisplayOnSetting = Settings.Global.getInt(resolver,  
        4. 0) != 0;  
        5.     mWifiDisplayCertMode = Settings.Global.getInt(resolver,  
        6. 0) != 0;  
        7.   
        8.     mWifiDisplayWpsConfig = WpsInfo.INVALID;  
        9. if (mWifiDisplayCertMode) {  
        10.         mWifiDisplayWpsConfig = Settings.Global.getInt(resolver,  
        11.               Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID);  
        12.     }  
        13.   
        14.     updateWfdEnableState();  
        15. }


        这里主要置mWifiDisplayOnSetting为true,然后就调用updateWfdEnableState去更新wfd的状态:

         


        1. privatevoid updateWfdEnableState() {  
        2. if (mWifiDisplayOnSetting && mWifiP2pEnabled) {  
        3. // WFD should be enabled.  
        4. if (!mWfdEnabled && !mWfdEnabling) {  
        5. true;  
        6.   
        7. new WifiP2pWfdInfo();  
        8. true);  
        9.             wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE);  
        10. true);  
        11.             wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);  
        12.             wfdInfo.setMaxThroughput(MAX_THROUGHPUT);  
        13. new ActionListener() {  
        14. @Override  
        15. publicvoid onSuccess() {  
        16. if (DEBUG) {  
        17. "Successfully set WFD info.");  
        18.                     }  
        19. if (mWfdEnabling) {  
        20. false;  
        21. true;  
        22.                         reportFeatureState();  
        23.                         updateScanState();  
        24.                     }  
        25.                 }  
        26.   
        27. @Override  
        28. publicvoidint reason) {  
        29. if (DEBUG) {  
        30. "Failed to set WFD info with reason "".");  
        31.                     }  
        32. false;  
        33.                 }  
        34.             });  
        35.         }


        首先调用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,如下图:

         

         

        通过上面的图我们可以看到实线部分是调用关系,虚线部分是回调关系。接着我们来看reportFeatureState的实现:

         


        1. privatevoid reportFeatureState() {  
        2. finalint featureState = computeFeatureState();  
        3. new Runnable() {  
        4. @Override  
        5. publicvoid run() {  
        6.             mListener.onFeatureStateChanged(featureState);  
        7.         }  
        8.     });  
        9. }  
        10.   
        11. privateint computeFeatureState() {  
        12. if (!mWifiP2pEnabled) {  
        13. return WifiDisplayStatus.FEATURE_STATE_DISABLED;  
        14.     }  
        15. return mWifiDisplayOnSetting ? WifiDisplayStatus.FEATURE_STATE_ON :  
        16.             WifiDisplayStatus.FEATURE_STATE_OFF;  
        17. }

         

        直接回调WifiDisplayListener的onFeatureStateChanged,从上面的图我们可以看着WifiDisplayListener会由WifiDisplayAdapter注册的,去看这部分的实现:

         


        1. publicvoidint featureState) {  
        2. synchronized (getSyncRoot()) {  
        3. if (mFeatureState != featureState) {  
        4.                 mFeatureState = featureState;  
        5.                 scheduleStatusChangedBroadcastLocked();  
        6.             }  
        7.         }  
        8.     }  
        9.   
        10. privatevoid scheduleStatusChangedBroadcastLocked() {  
        11. null;  
        12. if (!mPendingStatusChangeBroadcast) {  
        13. true;  
        14.         mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);  
        15.     }  
        16. }

         

        这里最后通过WifiDisplayHandler的sendEmptyMessage的方法实现,目的是不要卡住了WifiDisplayController后面代码的执行,来看WifiDisplayHandler如何处理MSG_SEND_STATUS_CHANGE_BROADCAST:

         

          1. publicvoid handleMessage(Message msg) {  
          2. switch (msg.what) {  
          3. case MSG_SEND_STATUS_CHANGE_BROADCAST:  
          4.             handleSendStatusChangeBroadcast();  
          5. break;  
          6.   
          7. case MSG_UPDATE_NOTIFICATION:  
          8.             handleUpdateNotification();  
          9. break;  
          10.     }  
          11.   
          12. ate void handleSendStatusChangeBroadcast() {  
          13. final Intent intent;  
          14. synchronized (getSyncRoot()) {  
          15. if (!mPendingStatusChangeBroadcast) {  
          16. return;  
          17.     }  
          18.   
          19. false;  
          20. new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);  
          21.     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);  
          22.     intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,  
          23.             getWifiDisplayStatusLocked());  
          24. }  
          25.   
          26. // Send protected broadcast about wifi display status to registered receivers.  
          27. 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设备:


          1. privatevoid updateScanState() {  
          2. ifnull) {  
          3. if (!mDiscoverPeersInProgress) {  
          4. "Starting Wifi display scan.");  
          5. true;  
          6.             handleScanStarted();  
          7.             tryDiscoverPeers();  
          8.         }  
          9.     }


          handleScanStarted用于通知WifiDisplayAdapter扫描开始了,当然WifiDisplayAdapter也会发broadcast给MediaRouter。接着会调用tryDiscoverPeers:

           

          这里调用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去获取所有扫描到的设备列表,下面来看这个函数的实现:

          1. privatevoid tryDiscoverPeers() {  
          2. new ActionListener() {  
          3. @Override  
          4. publicvoid onSuccess() {  
          5. if (DEBUG) {  
          6. "Discover peers succeeded.  Requesting peers now.");  
          7.             }  
          8. if (mDiscoverPeersInProgress) {  
          9.                 requestPeers();  
          10.             }  
          11.         }  
          12.     mHandler.postDelayed(mDiscoverPeers, DISCOVER_PEERS_INTERVAL_MILLIS);  
          13. }

           


          1. privatevoid requestPeers() {  
          2. new PeerListListener() {  
          3. @Override  
          4. publicvoid onPeersAvailable(WifiP2pDeviceList peers) {  
          5. if (DEBUG) {  
          6. "Received list of peers.");  
          7.             }  
          8.   
          9.             mAvailableWifiDisplayPeers.clear();  
          10. for (WifiP2pDevice device : peers.getDeviceList()) {  
          11. if (DEBUG) {  
          12. "  " + describeWifiP2pDevice(device));  
          13.                 }  
          14.   
          15. if (isWifiDisplay(device)) {  
          16.                     mAvailableWifiDisplayPeers.add(device);  
          17.                 }  
          18.             }  
          19.   
          20. if (mDiscoverPeersInProgress) {  
          21.                 handleScanResults();  
          22.             }  
          23.         }  
          24.     });  
          25. }


          首先从扫描的设备列表中过滤掉不能做wifi display的设备,主要从三个方面过滤,一是纯粹的P2P设备,不会待用WfdInfo;第二是带有WfdInfo,但是暂时没有被enable;三是只能是PrimarySinkDevice,看起来Android还不支持SecondSink。并将过滤掉剩下的设备加入到mAvailableWifiDisplayPeers列表中,接着调用handleScanResults来组装WifiDisplay列表数组并notify给WifiDisplayAdapter:

           


          1. privatevoid handleScanResults() {  
          2. finalint count = mAvailableWifiDisplayPeers.size();  
          3. final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);  
          4. forint0; i < count; i++) {  
          5.         WifiP2pDevice device = mAvailableWifiDisplayPeers.get(i);  
          6.         displays[i] = createWifiDisplay(device);  
          7.         updateDesiredDevice(device);  
          8.     }  
          9.   
          10. new Runnable() {  
          11. @Override  
          12. publicvoid run() {  
          13.             mListener.onScanResults(displays);  
          14.         }  
          15.     });  
          16. }


          这里首先根据mAvailableWifiDisplayPeers的数目创建一个WifiDisplay数组,然后一个个构造WifiDisplay对象,WifiDiplay对象包含以下几个变量:

          mDeviceAddress

          设备的Mac地址

          mDeviceName

          设备的名字

          mDeviceAlias

          设备的别名,一般为NULL

          mIsAvailable

          是否可用状态

          mCanConnect

          WfdInfo中的SessionAvailable是否为1

          mIsRemembered

          是否被记录的

          接着调用updateDesiredDevice用于判断扫描到的这个设备是否是现在正在连接或者连接上的设备,如果是,则更新它的一些信息,以后在连接Wifi display的时候再来分析这一块。接着就会向WifiDisplayAdapter回调onScanResults,回调函数中带有已经扫描到的wifi display设备列表(如果有):

           

            1. publicvoid onScanResults(WifiDisplay[] availableDisplays) {  
            2. synchronized (getSyncRoot()) {  
            3.         availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(  
            4.                 availableDisplays);  
            5.   
            6. boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);  
            7.   
            8. // Check whether any of the available displays changed canConnect status.  
            9. forint0; !changed && i<availableDisplays.length; i++) {  
            10.             changed = availableDisplays[i].canConnect()  
            11.                     != mAvailableDisplays[i].canConnect();  
            12.         }  
            13.   
            14. if (changed) {  
            15.             mAvailableDisplays = availableDisplays;  
            16.             fixRememberedDisplayNamesFromAvailableDisplaysLocked();  
            17.             updateDisplaysLocked();  
            18.             scheduleStatusChangedBroadcastLocked();  
            19.         }  
            20.     }  
            21. }

            这里首先调用PersistentDateStore的applyWifiDisplayAliases方法去判断扫描到的设备中有没有以前连接过并记录下来的wifi display设备,比较方法是比较两者的MAC地址,如果在PersistentDateStore中找到,再比较两者的别名(Alias),如果不相同则更新results列表,细节的代码可以看applyWifiDisplayAlias中的实现。

             

              1. public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {  
              2.     WifiDisplay[] results = displays;  
              3. ifnull) {  
              4. int count = displays.length;  
              5. forint0; i < count; i++) {  
              6.             WifiDisplay result = applyWifiDisplayAlias(displays[i]);  
              7. if (result != displays[i]) {  
              8. if (results == displays) {  
              9. new WifiDisplay[count];  
              10. 0, results, 0, count);  
              11.                 }  
              12.                 results[i] = result;  
              13.             }  
              14.         }  
              15.     }  
              16. return results;  
              17. }

              回到上面的onScanResults中,接着判断刚扫描到的设备列表(availableDisplays)和之前存储的设备列表(mAvailableDisplays)之间有没有变化,可以数组内容以及是否可连两个方面检查。如果有变化,则把刚扫描到的设备列表(availableDisplays)赋值给存储的设备列表(mAvailableDisplays)。接下来调用fixRememberedDisplayNamesFromAvailableDisplaysLocked来更新PersistentDateStore中存储的已经连接过的wifi display设备,更新的条件是设备的MAC地址一样,但设备的DeviceName和DeviceAlias有变化,这是就要更新到PersistentDateStore中,代码如下:

               


              1. privatevoid fixRememberedDisplayNamesFromAvailableDisplaysLocked() {  
              2. booleanfalse;  
              3. forint0; i < mRememberedDisplays.length; i++) {  
              4.         WifiDisplay rememberedDisplay = mRememberedDisplays[i];  
              5.         WifiDisplay availableDisplay = findAvailableDisplayLocked(  
              6.                 rememberedDisplay.getDeviceAddress());  
              7. ifnull && !rememberedDisplay.equals(availableDisplay)) {  
              8.             mRememberedDisplays[i] = availableDisplay;  
              9.             changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay);  
              10.         }  
              11.     }  
              12. if (changed) {  
              13.         mPersistentDataStore.saveIfNeeded();  
              14.     }  
              15. }

               

              回到onScanResults中,接下来会调用updateDisplaysLocked来更新返回给MediaRouter的设备列表信息,在这里会把扫描到的设备以及之前存储下来的设备做一次合并,共同保存到mDisplays数组中,后面在发送broadcast的时候,就会把mDisplays保存到WifiDisplayStatus对象中,并在broadcast带上这个对象。

              1. privatevoid updateDisplaysLocked() {  
              2. new ArrayList<WifiDisplay>(  
              3.             mAvailableDisplays.length + mRememberedDisplays.length);  
              4. boolean[] remembered = newboolean[mAvailableDisplays.length];  
              5. for (WifiDisplay d : mRememberedDisplays) {  
              6. booleanfalse;  
              7. forint0; i < mAvailableDisplays.length; i++) {  
              8. if (d.equals(mAvailableDisplays[i])) {  
              9. true;  
              10. break;  
              11.             }  
              12.         }  
              13. if (!available) {  
              14. new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),  
              15. false, false, true));  
              16.         }  
              17.     }  
              18. forint0; i < mAvailableDisplays.length; i++) {  
              19.         WifiDisplay d = mAvailableDisplays[i];  
              20. new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),  
              21. true, d.canConnect(), remembered[i]));  
              22.     }  
              23.     mDisplays = displays.toArray(WifiDisplay.EMPTY_ARRAY);  
              24. }


              上面的实现中先从mRememberedDisplays逐个添加wifi display设备到displays数组中,如果在mAvailableDisplays有相同的设备,则不添加到displays数组;后面再把mAvailableDisplays所有元素添加到displays数组,并全部赋值给mDisplays数组。

               

              再回到onScanResults中,就会调用scheduleStatusChangedBroadcastLocked向WifiDisplayHandler发送MSG_SEND_STATUS_CHANGE_BROADCAST消息,这个我们在前面已经讲过了,然后会发送broadcast,并带上一个WifiDisplayStatus对象。现在我们再到MediaRouter和WifiDisplaySettings中看如何处理这个broadcast,先来看MediaRouter如何解析WifiDisplayStatus对象。updateWifiDisplayStatus的实现如下:

               


              1. staticvoid updateWifiDisplayStatus(WifiDisplayStatus status) {  
              2.     WifiDisplay[] displays;  
              3.     WifiDisplay activeDisplay;  
              4. if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {  
              5.         displays = status.getDisplays();  
              6.         activeDisplay = status.getActiveDisplay();  
              7. else {  
              8.         displays = WifiDisplay.EMPTY_ARRAY;  
              9. null;  
              10.     }  
              11. null ?  
              12. null;  
              13.   
              14. // Add or update routes.  
              15. forint0; i < displays.length; i++) {  
              16. final WifiDisplay d = displays[i];  
              17. if (shouldShowWifiDisplay(d, activeDisplay)) {  
              18.             RouteInfo route = findWifiDisplayRoute(d);  
              19. ifnull) {  
              20.                 route = makeWifiDisplayRoute(d, status);  
              21.                 addRouteStatic(route);  
              22. else {  
              23.                 String address = d.getDeviceAddress();  
              24. boolean disconnected = !address.equals(activeDisplayAddress)  
              25.                         && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);  
              26.                 updateWifiDisplayRoute(route, d, status, disconnected);  
              27.             }  
              28. if (d.equals(activeDisplay)) {  
              29. false);  
              30.             }  
              31.         }  
              32.     }  
              33.   
              34. // Remove stale routes.  
              35. forint0; ) {  
              36.         RouteInfo route = sStatic.mRoutes.get(i);  
              37. ifnull) {  
              38.             WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);  
              39. ifnull || !shouldShowWifiDisplay(d, activeDisplay)) {  
              40.                 removeRouteStatic(route);  
              41.             }  
              42.         }  
              43.     }  
              44.   
              45.     sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;  
              46. }

               

              上面的代码中,首先从WifiDisplayStatus取出已经扫描到的WifiDisplay设备数组和当前处于连接状态的WifiDisplay设备,然后shouldShowWifiDisplay用于过滤是否将这个wifi display设备加入到mRoutes数组中,判断条件是这个设备已经连过并且有保存在PersistentDateStore或者这个设备就是当前正在连接中的设备,对于其它的设备并没有加入到mRoutes中,这里就有个疑问了,其它没连过的设备将在哪里加入呢? 我们后面分析WifiDisplaySettings再来看这部分。如果在mRoutes没有找到相同的wifi display设备,就会把这个设备加入到mRoutes中,并通知WifiDisplaySettings相应的变化;如果在mRoutes存在相同的wifi display设备,则检查它的名字或者状态(available、canConnect)有没有变化,如果有变化,则通知WifiDisplaySettings相应的改变。selectRouteStatic用于更新是否默认的router并dispatch相应的回调消息。最后会从mRoutes踢出有错误的wifi display设备。

               

              我的一些简单理解:MediaRouter只保存已经配对上的remote display设备,包括Wifi diplay、蓝牙A2DP设备、chromecast设备等,用于提供给其它应用程序使用,比如youtube可以直接chromecast,当我们前面有成功和一个chromecast设备配对过后,youtube应用就可以从MediaRouter对象中获取到当前已经配对的chromecast设备信息,并可以把youtube的视频推送到chromecast上面播放;再举个例子,百度视频应用可以访问MediaRouter中的wifi display设备,当我们设备中有已经连接或已经保存的wifi display设备时,就可以很方便的从直接百度视频上面直接开始wifi display,而不需要用户再去Settings里面扫描连接。

               

              再来看WifiDisplaySettings中如何处理MSG_SEND_STATUS_CHANGE_BROADCAST:


                1. privatefinalnew BroadcastReceiver() {  
                2. @Override  
                3. publicvoid onReceive(Context context, Intent intent) {  
                4.         String action = intent.getAction();  
                5. if (action.equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {  
                6.             scheduleUpdate(CHANGE_WIFI_DISPLAY_STATUS);  
                7.         }  
                8.     }  
                9. };

                从MediaRouter中的callback消息也会进入到scheduleUpdate中,只是后面的参数不一样,通过callback进来的参数是CHANGE_ROUTES,而broadcast进来的参数是CHANGE_WIFI_DISPLAY_STATUS,来看scheduleUpdate,最终实现是mUpdateRunnable中:



                1. privatevoidint changes) {  
                2. booleanfalse;  
                3.   
                4. // Update wifi display state.  
                5. if0) {  
                6.         mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();  
                7.   
                8. // The wifi display feature state may have changed.  
                9. true;  
                10.     }  
                11.   
                12. // Rebuild the routes.  
                13. final PreferenceScreen preferenceScreen = getPreferenceScreen();  
                14.     preferenceScreen.removeAll();  
                15.   
                16. // Add all known remote display routes.  
                17. finalint routeCount = mRouter.getRouteCount();  
                18. forint0; i < routeCount; i++) {  
                19.         MediaRouter.RouteInfo route = mRouter.getRouteAt(i);  
                20. if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) {  
                21.             preferenceScreen.addPreference(createRoutePreference(route));  
                22.         }  
                23.     }  
                24.   
                25. // Additional features for wifi display routes.  
                26. ifnull  
                27.             && mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {  
                28. // Add all unpaired wifi displays.  
                29. for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) {  
                30. if (!display.isRemembered() && display.isAvailable()  
                31.                     && !display.equals(mWifiDisplayStatus.getActiveDisplay())) {  
                32. new UnpairedWifiDisplayPreference(  
                33.                         getActivity(), display));  
                34.             }  
                35.         }  
                36.     }  
                37.   
                38. }


                上面的代码比较简单,一个是从MediaRouter中获取mRoutes数组中存着的remote display设备;一个是从broadcast中的WifiDisplayStatus对象中获取mDisplay数组,两者相互合并构建整个listview展现给用户。至此,wifi display的扫描流程就介绍完了,下面是整体的流程图: