最近在学习Android 4.4上面的WifiDisplay(Miracast)相关的模块,这里先从WifiDisplay用到的各个Service讲起,然后再从WifiDisplaySettings里面讲解打开wfd的流程。首先看下面的主要几个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方法:
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);
} 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;
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();
} private void registerAdditionalDisplayAdapters() {
synchronized (mSyncRoot) {
if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
registerOverlayDisplayAdapterLocked();
registerWifiDisplayAdapterLocked();
registerVirtualDisplayAdapterLocked();
}
}
} 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);
}
} 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);
}
});
} 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方法,代码如下:
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();
}A MediaRouter is retrieved through Context.getSystemService() of
a Context.MEDIA_ROUTER_SERVICE. 这样系统是实例化一个MediaRouter对象并返回,下面来看它的构造函数:
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;
} 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));
}
}上面接收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:
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();
}
}
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);
} 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();
}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();
}
}
}
} 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();
}
}
}
} 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); 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();
} 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;
}
});
} 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;
}
private 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);
} private void updateScanState() {
if (mScanRequested && mWfdEnabled && mDesiredDevice == null) {
if (!mDiscoverPeersInProgress) {
Slog.i(TAG, "Starting Wifi display scan.");
mDiscoverPeersInProgress = true;
handleScanStarted();
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);
} 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();
}
}
});
} 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);
}
});
}| 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();
}
}
} 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;
} 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();
}
}如果扫描到的设备列表中有wifi display设备的名字或者别名发生了变化,就会调用到PersistentDataStore.saveIfNeeded方法把数据写到/data/system/display-manager-state.xml中。 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);
} 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);
}
}
}; 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));
}
}
}
}Android WifiDisplay分析一:相关Service的启动,布布扣,bubuko.com
Android WifiDisplay分析一:相关Service的启动
原文:http://blog.csdn.net/lilian0118/article/details/22849249