WifiDisplayAdapter.java revision 2444ae7e2b8658a4a90f996e678423558744b4a2
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.util.Slog; 31import android.view.Surface; 32 33import java.io.PrintWriter; 34import java.util.Arrays; 35 36/** 37 * Connects to Wifi displays that implement the Miracast protocol. 38 * <p> 39 * The Wifi display protocol relies on Wifi direct for discovering and pairing 40 * with the display. Once connected, the Media Server opens an RTSP socket and accepts 41 * a connection from the display. After session negotiation, the Media Server 42 * streams encoded buffers to the display. 43 * </p><p> 44 * This class is responsible for connecting to Wifi displays and mediating 45 * the interactions between Media Server, Surface Flinger and the Display Manager Service. 46 * </p><p> 47 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 48 * </p> 49 */ 50final class WifiDisplayAdapter extends DisplayAdapter { 51 private static final String TAG = "WifiDisplayAdapter"; 52 53 private static final boolean DEBUG = false; 54 55 private final PersistentDataStore mPersistentDataStore; 56 private final boolean mSupportsProtectedBuffers; 57 58 private WifiDisplayController mDisplayController; 59 private WifiDisplayDevice mDisplayDevice; 60 61 private WifiDisplayStatus mCurrentStatus; 62 private int mFeatureState; 63 private int mScanState; 64 private int mActiveDisplayState; 65 private WifiDisplay mActiveDisplay; 66 private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY; 67 private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY; 68 69 private boolean mPendingStatusChangeBroadcast; 70 71 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 72 Context context, Handler handler, Listener listener, 73 PersistentDataStore persistentDataStore) { 74 super(syncRoot, context, handler, listener, TAG); 75 mPersistentDataStore = persistentDataStore; 76 mSupportsProtectedBuffers = context.getResources().getBoolean( 77 com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers); 78 } 79 80 @Override 81 public void dumpLocked(PrintWriter pw) { 82 super.dumpLocked(pw); 83 84 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked()); 85 pw.println("mFeatureState=" + mFeatureState); 86 pw.println("mScanState=" + mScanState); 87 pw.println("mActiveDisplayState=" + mActiveDisplayState); 88 pw.println("mActiveDisplay=" + mActiveDisplay); 89 pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays)); 90 pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays)); 91 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); 92 pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers); 93 94 // Try to dump the controller state. 95 if (mDisplayController == null) { 96 pw.println("mDisplayController=null"); 97 } else { 98 pw.println("mDisplayController:"); 99 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 100 ipw.increaseIndent(); 101 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200); 102 } 103 } 104 105 @Override 106 public void registerLocked() { 107 super.registerLocked(); 108 109 updateRememberedDisplaysLocked(); 110 111 getHandler().post(new Runnable() { 112 @Override 113 public void run() { 114 mDisplayController = new WifiDisplayController( 115 getContext(), getHandler(), mWifiDisplayListener); 116 } 117 }); 118 } 119 120 public void requestScanLocked() { 121 if (DEBUG) { 122 Slog.d(TAG, "requestScanLocked"); 123 } 124 125 getHandler().post(new Runnable() { 126 @Override 127 public void run() { 128 if (mDisplayController != null) { 129 mDisplayController.requestScan(); 130 } 131 } 132 }); 133 } 134 135 public void requestConnectLocked(final String address, final boolean trusted) { 136 if (DEBUG) { 137 Slog.d(TAG, "requestConnectLocked: address=" + address + ", trusted=" + trusted); 138 } 139 140 if (!trusted) { 141 synchronized (getSyncRoot()) { 142 if (!isRememberedDisplayLocked(address)) { 143 Slog.w(TAG, "Ignoring request by an untrusted client to connect to " 144 + "an unknown wifi display: " + address); 145 return; 146 } 147 } 148 } 149 150 getHandler().post(new Runnable() { 151 @Override 152 public void run() { 153 if (mDisplayController != null) { 154 mDisplayController.requestConnect(address); 155 } 156 } 157 }); 158 } 159 160 private boolean isRememberedDisplayLocked(String address) { 161 for (WifiDisplay display : mRememberedDisplays) { 162 if (display.getDeviceAddress().equals(address)) { 163 return true; 164 } 165 } 166 return false; 167 } 168 169 public void requestDisconnectLocked() { 170 if (DEBUG) { 171 Slog.d(TAG, "requestDisconnectedLocked"); 172 } 173 174 getHandler().post(new Runnable() { 175 @Override 176 public void run() { 177 if (mDisplayController != null) { 178 mDisplayController.requestDisconnect(); 179 } 180 } 181 }); 182 } 183 184 public void requestRenameLocked(String address, String alias) { 185 if (DEBUG) { 186 Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias); 187 } 188 189 if (alias != null) { 190 alias = alias.trim(); 191 if (alias.isEmpty() || alias.equals(address)) { 192 alias = null; 193 } 194 } 195 196 if (mPersistentDataStore.renameWifiDisplay(address, alias)) { 197 mPersistentDataStore.saveIfNeeded(); 198 updateRememberedDisplaysLocked(); 199 scheduleStatusChangedBroadcastLocked(); 200 } 201 } 202 203 public void requestForgetLocked(String address) { 204 if (DEBUG) { 205 Slog.d(TAG, "requestForgetLocked: address=" + address); 206 } 207 208 if (mPersistentDataStore.forgetWifiDisplay(address)) { 209 mPersistentDataStore.saveIfNeeded(); 210 updateRememberedDisplaysLocked(); 211 scheduleStatusChangedBroadcastLocked(); 212 } 213 214 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) { 215 requestDisconnectLocked(); 216 } 217 } 218 219 public WifiDisplayStatus getWifiDisplayStatusLocked() { 220 if (mCurrentStatus == null) { 221 mCurrentStatus = new WifiDisplayStatus( 222 mFeatureState, mScanState, mActiveDisplayState, 223 mActiveDisplay, mAvailableDisplays, mRememberedDisplays); 224 } 225 226 if (DEBUG) { 227 Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus); 228 } 229 return mCurrentStatus; 230 } 231 232 private void updateRememberedDisplaysLocked() { 233 mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays(); 234 mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay); 235 mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays); 236 } 237 238 private void handleConnectLocked(WifiDisplay display, 239 Surface surface, int width, int height, int flags) { 240 handleDisconnectLocked(); 241 242 if (mPersistentDataStore.rememberWifiDisplay(display)) { 243 mPersistentDataStore.saveIfNeeded(); 244 updateRememberedDisplaysLocked(); 245 scheduleStatusChangedBroadcastLocked(); 246 } 247 248 int deviceFlags = 0; 249 if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) { 250 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE; 251 } 252 if (mSupportsProtectedBuffers) { 253 deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 254 } 255 256 float refreshRate = 60.0f; // TODO: get this for real 257 258 String name = display.getFriendlyDisplayName(); 259 IBinder displayToken = Surface.createDisplay(name); 260 mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height, 261 refreshRate, deviceFlags, surface); 262 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED); 263 } 264 265 private void handleDisconnectLocked() { 266 if (mDisplayDevice != null) { 267 mDisplayDevice.clearSurfaceLocked(); 268 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); 269 mDisplayDevice = null; 270 } 271 } 272 273 private void scheduleStatusChangedBroadcastLocked() { 274 mCurrentStatus = null; 275 if (!mPendingStatusChangeBroadcast) { 276 mPendingStatusChangeBroadcast = true; 277 getHandler().post(mStatusChangeBroadcast); 278 } 279 } 280 281 private final Runnable mStatusChangeBroadcast = new Runnable() { 282 @Override 283 public void run() { 284 final Intent intent; 285 synchronized (getSyncRoot()) { 286 if (!mPendingStatusChangeBroadcast) { 287 return; 288 } 289 290 mPendingStatusChangeBroadcast = false; 291 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); 292 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 293 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, 294 getWifiDisplayStatusLocked()); 295 } 296 297 // Send protected broadcast about wifi display status to registered receivers. 298 getContext().sendBroadcast(intent); 299 } 300 }; 301 302 private final WifiDisplayController.Listener mWifiDisplayListener = 303 new WifiDisplayController.Listener() { 304 @Override 305 public void onFeatureStateChanged(int featureState) { 306 synchronized (getSyncRoot()) { 307 if (mFeatureState != featureState) { 308 mFeatureState = featureState; 309 scheduleStatusChangedBroadcastLocked(); 310 } 311 } 312 } 313 314 @Override 315 public void onScanStarted() { 316 synchronized (getSyncRoot()) { 317 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) { 318 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING; 319 scheduleStatusChangedBroadcastLocked(); 320 } 321 } 322 } 323 324 @Override 325 public void onScanFinished(WifiDisplay[] availableDisplays) { 326 synchronized (getSyncRoot()) { 327 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases( 328 availableDisplays); 329 330 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING 331 || !Arrays.equals(mAvailableDisplays, availableDisplays)) { 332 mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; 333 mAvailableDisplays = availableDisplays; 334 scheduleStatusChangedBroadcastLocked(); 335 } 336 } 337 } 338 339 @Override 340 public void onDisplayConnecting(WifiDisplay display) { 341 synchronized (getSyncRoot()) { 342 display = mPersistentDataStore.applyWifiDisplayAlias(display); 343 344 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING 345 || mActiveDisplay == null 346 || !mActiveDisplay.equals(display)) { 347 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING; 348 mActiveDisplay = display; 349 scheduleStatusChangedBroadcastLocked(); 350 } 351 } 352 } 353 354 @Override 355 public void onDisplayConnectionFailed() { 356 synchronized (getSyncRoot()) { 357 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 358 || mActiveDisplay != null) { 359 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 360 mActiveDisplay = null; 361 scheduleStatusChangedBroadcastLocked(); 362 } 363 } 364 } 365 366 @Override 367 public void onDisplayConnected(WifiDisplay display, Surface surface, 368 int width, int height, int flags) { 369 synchronized (getSyncRoot()) { 370 display = mPersistentDataStore.applyWifiDisplayAlias(display); 371 handleConnectLocked(display, surface, width, height, flags); 372 373 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED 374 || mActiveDisplay == null 375 || !mActiveDisplay.equals(display)) { 376 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED; 377 mActiveDisplay = display; 378 scheduleStatusChangedBroadcastLocked(); 379 } 380 } 381 } 382 383 @Override 384 public void onDisplayDisconnected() { 385 // Stop listening. 386 synchronized (getSyncRoot()) { 387 handleDisconnectLocked(); 388 389 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 390 || mActiveDisplay != null) { 391 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 392 mActiveDisplay = null; 393 scheduleStatusChangedBroadcastLocked(); 394 } 395 } 396 } 397 }; 398 399 private final class WifiDisplayDevice extends DisplayDevice { 400 private final String mName; 401 private final int mWidth; 402 private final int mHeight; 403 private final float mRefreshRate; 404 private final int mFlags; 405 406 private Surface mSurface; 407 private DisplayDeviceInfo mInfo; 408 409 public WifiDisplayDevice(IBinder displayToken, String name, 410 int width, int height, float refreshRate, int flags, 411 Surface surface) { 412 super(WifiDisplayAdapter.this, displayToken); 413 mName = name; 414 mWidth = width; 415 mHeight = height; 416 mRefreshRate = refreshRate; 417 mFlags = flags; 418 mSurface = surface; 419 } 420 421 public void clearSurfaceLocked() { 422 mSurface = null; 423 sendTraversalRequestLocked(); 424 } 425 426 @Override 427 public void performTraversalInTransactionLocked() { 428 setSurfaceInTransactionLocked(mSurface); 429 } 430 431 @Override 432 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 433 if (mInfo == null) { 434 mInfo = new DisplayDeviceInfo(); 435 mInfo.name = mName; 436 mInfo.width = mWidth; 437 mInfo.height = mHeight; 438 mInfo.refreshRate = mRefreshRate; 439 mInfo.flags = mFlags; 440 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 441 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight); 442 } 443 return mInfo; 444 } 445 } 446} 447