WifiDisplayAdapter.java revision 89d5546d7fd3a3bb19820c42e8b4527013dd6545
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.display; 18 19import com.android.internal.util.DumpUtils; 20import com.android.internal.util.IndentingPrintWriter; 21 22import android.content.Context; 23import android.content.Intent; 24import android.hardware.display.DisplayManager; 25import android.hardware.display.WifiDisplay; 26import android.hardware.display.WifiDisplayStatus; 27import android.media.RemoteDisplay; 28import android.os.Handler; 29import android.os.IBinder; 30import android.view.Surface; 31 32import java.io.PrintWriter; 33import java.util.Arrays; 34 35/** 36 * Connects to Wifi displays that implement the Miracast protocol. 37 * <p> 38 * The Wifi display protocol relies on Wifi direct for discovering and pairing 39 * with the display. Once connected, the Media Server opens an RTSP socket and accepts 40 * a connection from the display. After session negotiation, the Media Server 41 * streams encoded buffers to the display. 42 * </p><p> 43 * This class is responsible for connecting to Wifi displays and mediating 44 * the interactions between Media Server, Surface Flinger and the Display Manager Service. 45 * </p><p> 46 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 47 * </p> 48 */ 49final class WifiDisplayAdapter extends DisplayAdapter { 50 private static final String TAG = "WifiDisplayAdapter"; 51 52 private PersistentDataStore mPersistentDataStore; 53 54 private WifiDisplayController mDisplayController; 55 private WifiDisplayDevice mDisplayDevice; 56 57 private WifiDisplayStatus mCurrentStatus; 58 private int mFeatureState; 59 private int mScanState; 60 private int mActiveDisplayState; 61 private WifiDisplay mActiveDisplay; 62 private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY; 63 private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY; 64 65 private boolean mPendingStatusChangeBroadcast; 66 67 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 68 Context context, Handler handler, Listener listener, 69 PersistentDataStore persistentDataStore) { 70 super(syncRoot, context, handler, listener, TAG); 71 mPersistentDataStore = persistentDataStore; 72 } 73 74 @Override 75 public void dumpLocked(PrintWriter pw) { 76 super.dumpLocked(pw); 77 78 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked()); 79 pw.println("mFeatureState=" + mFeatureState); 80 pw.println("mScanState=" + mScanState); 81 pw.println("mActiveDisplayState=" + mActiveDisplayState); 82 pw.println("mActiveDisplay=" + mActiveDisplay); 83 pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays)); 84 pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays)); 85 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); 86 87 // Try to dump the controller state. 88 if (mDisplayController == null) { 89 pw.println("mDisplayController=null"); 90 } else { 91 pw.println("mDisplayController:"); 92 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 93 ipw.increaseIndent(); 94 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200); 95 } 96 } 97 98 @Override 99 public void registerLocked() { 100 super.registerLocked(); 101 102 updateRememberedDisplaysLocked(); 103 104 getHandler().post(new Runnable() { 105 @Override 106 public void run() { 107 mDisplayController = new WifiDisplayController( 108 getContext(), getHandler(), mWifiDisplayListener); 109 } 110 }); 111 } 112 113 public void requestScanLocked() { 114 getHandler().post(new Runnable() { 115 @Override 116 public void run() { 117 if (mDisplayController != null) { 118 mDisplayController.requestScan(); 119 } 120 } 121 }); 122 } 123 124 public void requestConnectLocked(final String address) { 125 getHandler().post(new Runnable() { 126 @Override 127 public void run() { 128 if (mDisplayController != null) { 129 mDisplayController.requestConnect(address); 130 } 131 } 132 }); 133 } 134 135 public void requestDisconnectLocked() { 136 getHandler().post(new Runnable() { 137 @Override 138 public void run() { 139 if (mDisplayController != null) { 140 mDisplayController.requestDisconnect(); 141 } 142 } 143 }); 144 } 145 146 public void requestRenameLocked(String address, String alias) { 147 if (alias != null) { 148 alias = alias.trim(); 149 if (alias.isEmpty()) { 150 alias = null; 151 } 152 } 153 154 if (mPersistentDataStore.renameWifiDisplay(address, alias)) { 155 mPersistentDataStore.saveIfNeeded(); 156 updateRememberedDisplaysLocked(); 157 scheduleStatusChangedBroadcastLocked(); 158 } 159 } 160 161 public void requestForgetLocked(String address) { 162 if (mPersistentDataStore.forgetWifiDisplay(address)) { 163 mPersistentDataStore.saveIfNeeded(); 164 updateRememberedDisplaysLocked(); 165 scheduleStatusChangedBroadcastLocked(); 166 } 167 168 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) { 169 requestDisconnectLocked(); 170 } 171 } 172 173 public WifiDisplayStatus getWifiDisplayStatusLocked() { 174 if (mCurrentStatus == null) { 175 mCurrentStatus = new WifiDisplayStatus( 176 mFeatureState, mScanState, mActiveDisplayState, 177 mActiveDisplay, mAvailableDisplays, mRememberedDisplays); 178 } 179 return mCurrentStatus; 180 } 181 182 private void updateRememberedDisplaysLocked() { 183 mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays(); 184 mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay); 185 mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays); 186 } 187 188 private void handleConnectLocked(WifiDisplay display, 189 Surface surface, int width, int height, int flags) { 190 handleDisconnectLocked(); 191 192 if (mPersistentDataStore.rememberWifiDisplay(display)) { 193 mPersistentDataStore.saveIfNeeded(); 194 updateRememberedDisplaysLocked(); 195 scheduleStatusChangedBroadcastLocked(); 196 } 197 198 int deviceFlags = 0; 199 if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) { 200 deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT; 201 } 202 203 float refreshRate = 60.0f; // TODO: get this for real 204 205 String name = display.getFriendlyDisplayName(); 206 IBinder displayToken = Surface.createDisplay(name); 207 mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height, 208 refreshRate, deviceFlags, surface); 209 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED); 210 } 211 212 private void handleDisconnectLocked() { 213 if (mDisplayDevice != null) { 214 mDisplayDevice.clearSurfaceLocked(); 215 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); 216 mDisplayDevice = null; 217 } 218 } 219 220 private void scheduleStatusChangedBroadcastLocked() { 221 mCurrentStatus = null; 222 if (!mPendingStatusChangeBroadcast) { 223 mPendingStatusChangeBroadcast = true; 224 getHandler().post(mStatusChangeBroadcast); 225 } 226 } 227 228 private final Runnable mStatusChangeBroadcast = new Runnable() { 229 @Override 230 public void run() { 231 final Intent intent; 232 synchronized (getSyncRoot()) { 233 if (!mPendingStatusChangeBroadcast) { 234 return; 235 } 236 237 mPendingStatusChangeBroadcast = false; 238 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); 239 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 240 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, 241 getWifiDisplayStatusLocked()); 242 } 243 244 // Send protected broadcast about wifi display status to receivers that 245 // have the required permission. 246 getContext().sendBroadcast(intent, 247 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY); 248 } 249 }; 250 251 private final WifiDisplayController.Listener mWifiDisplayListener = 252 new WifiDisplayController.Listener() { 253 @Override 254 public void onFeatureStateChanged(int featureState) { 255 synchronized (getSyncRoot()) { 256 if (mFeatureState != featureState) { 257 mFeatureState = featureState; 258 scheduleStatusChangedBroadcastLocked(); 259 } 260 } 261 } 262 263 @Override 264 public void onScanStarted() { 265 synchronized (getSyncRoot()) { 266 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) { 267 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING; 268 scheduleStatusChangedBroadcastLocked(); 269 } 270 } 271 } 272 273 public void onScanFinished(WifiDisplay[] availableDisplays) { 274 synchronized (getSyncRoot()) { 275 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases( 276 availableDisplays); 277 278 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING 279 || !Arrays.equals(mAvailableDisplays, availableDisplays)) { 280 mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; 281 mAvailableDisplays = availableDisplays; 282 scheduleStatusChangedBroadcastLocked(); 283 } 284 } 285 } 286 287 @Override 288 public void onDisplayConnecting(WifiDisplay display) { 289 synchronized (getSyncRoot()) { 290 display = mPersistentDataStore.applyWifiDisplayAlias(display); 291 292 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING 293 || mActiveDisplay == null 294 || !mActiveDisplay.equals(display)) { 295 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING; 296 mActiveDisplay = display; 297 scheduleStatusChangedBroadcastLocked(); 298 } 299 } 300 } 301 302 @Override 303 public void onDisplayConnectionFailed() { 304 synchronized (getSyncRoot()) { 305 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 306 || mActiveDisplay != null) { 307 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 308 mActiveDisplay = null; 309 scheduleStatusChangedBroadcastLocked(); 310 } 311 } 312 } 313 314 @Override 315 public void onDisplayConnected(WifiDisplay display, Surface surface, 316 int width, int height, int flags) { 317 synchronized (getSyncRoot()) { 318 display = mPersistentDataStore.applyWifiDisplayAlias(display); 319 handleConnectLocked(display, surface, width, height, flags); 320 321 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED 322 || mActiveDisplay == null 323 || !mActiveDisplay.equals(display)) { 324 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED; 325 mActiveDisplay = display; 326 scheduleStatusChangedBroadcastLocked(); 327 } 328 } 329 } 330 331 @Override 332 public void onDisplayDisconnected() { 333 // Stop listening. 334 synchronized (getSyncRoot()) { 335 handleDisconnectLocked(); 336 337 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 338 || mActiveDisplay != null) { 339 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 340 mActiveDisplay = null; 341 scheduleStatusChangedBroadcastLocked(); 342 } 343 } 344 } 345 }; 346 347 private final class WifiDisplayDevice extends DisplayDevice { 348 private final String mName; 349 private final int mWidth; 350 private final int mHeight; 351 private final float mRefreshRate; 352 private final int mFlags; 353 354 private Surface mSurface; 355 private DisplayDeviceInfo mInfo; 356 357 public WifiDisplayDevice(IBinder displayToken, String name, 358 int width, int height, float refreshRate, int flags, 359 Surface surface) { 360 super(WifiDisplayAdapter.this, displayToken); 361 mName = name; 362 mWidth = width; 363 mHeight = height; 364 mRefreshRate = refreshRate; 365 mFlags = flags; 366 mSurface = surface; 367 } 368 369 public void clearSurfaceLocked() { 370 mSurface = null; 371 sendTraversalRequestLocked(); 372 } 373 374 @Override 375 public void performTraversalInTransactionLocked() { 376 setSurfaceInTransactionLocked(mSurface); 377 } 378 379 @Override 380 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 381 if (mInfo == null) { 382 mInfo = new DisplayDeviceInfo(); 383 mInfo.name = mName; 384 mInfo.width = mWidth; 385 mInfo.height = mHeight; 386 mInfo.refreshRate = mRefreshRate; 387 mInfo.flags = mFlags; 388 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 389 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight); 390 } 391 return mInfo; 392 } 393 } 394} 395