blob: abf0d27d223e59c548150c1abe605920c8c46cc8 [file] [log] [blame]
Jeff Browncbad9762012-09-04 21:57:59 -07001/*
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;
Jeff Browne08ae382012-09-07 20:36:36 -070023import android.content.Intent;
24import android.hardware.display.DisplayManager;
25import android.hardware.display.WifiDisplay;
26import android.hardware.display.WifiDisplayStatus;
Jeff Browncbad9762012-09-04 21:57:59 -070027import android.media.RemoteDisplay;
28import android.os.Handler;
29import android.os.IBinder;
30import android.util.Slog;
31import android.view.Surface;
32
33import java.io.PrintWriter;
Jeff Browne08ae382012-09-07 20:36:36 -070034import java.util.Arrays;
Jeff Browncbad9762012-09-04 21:57:59 -070035
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 WifiDisplayHandle mDisplayHandle;
54 private WifiDisplayController mDisplayController;
55
Jeff Browne08ae382012-09-07 20:36:36 -070056 private WifiDisplayStatus mCurrentStatus;
57 private boolean mEnabled;
58 private WifiDisplay mConnectedDisplay;
59 private WifiDisplay[] mKnownDisplays = WifiDisplay.EMPTY_ARRAY;
60 private boolean mScanInProgress;
61 private boolean mConnectionInProgress;
62
63 private boolean mPendingStatusChangeBroadcast;
64
Jeff Browncbad9762012-09-04 21:57:59 -070065 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
66 Context context, Handler handler, Listener listener) {
67 super(syncRoot, context, handler, listener, TAG);
68 }
69
70 @Override
71 public void dumpLocked(PrintWriter pw) {
72 super.dumpLocked(pw);
73
74 if (mDisplayHandle == null) {
75 pw.println("mDisplayHandle=null");
76 } else {
77 pw.println("mDisplayHandle:");
78 mDisplayHandle.dumpLocked(pw);
79 }
80
Jeff Browne08ae382012-09-07 20:36:36 -070081 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
82 pw.println("mEnabled=" + mEnabled);
83 pw.println("mConnectedDisplay=" + mConnectedDisplay);
84 pw.println("mKnownDisplays=" + Arrays.toString(mKnownDisplays));
85 pw.println("mScanInProgress=" + mScanInProgress);
86 pw.println("mConnectionInProgress=" + mConnectionInProgress);
87 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
88
Jeff Browncbad9762012-09-04 21:57:59 -070089 // Try to dump the controller state.
90 if (mDisplayController == null) {
91 pw.println("mDisplayController=null");
92 } else {
93 pw.println("mDisplayController:");
94 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
95 ipw.increaseIndent();
96 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200);
97 }
98 }
99
100 @Override
101 public void registerLocked() {
102 super.registerLocked();
103
104 getHandler().post(new Runnable() {
105 @Override
106 public void run() {
107 mDisplayController = new WifiDisplayController(
108 getContext(), getHandler(), mWifiDisplayListener);
109 }
110 });
111 }
112
Jeff Browne08ae382012-09-07 20:36:36 -0700113 public void requestScanLocked() {
114 getHandler().post(new Runnable() {
115 @Override
116 public void run() {
117 if (mDisplayController != null) {
118 mDisplayController.requestScan();
119 }
120 }
121 });
Jeff Browncbad9762012-09-04 21:57:59 -0700122 }
123
Jeff Browne08ae382012-09-07 20:36:36 -0700124 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 WifiDisplayStatus getWifiDisplayStatusLocked() {
147 if (mCurrentStatus == null) {
148 mCurrentStatus = new WifiDisplayStatus(mEnabled,
149 mConnectedDisplay, mKnownDisplays,
150 mScanInProgress, mConnectionInProgress);
151 }
152 return mCurrentStatus;
153 }
154
155 private void handleConnectLocked(WifiDisplay display, String iface) {
156 handleDisconnectLocked();
157
158 mDisplayHandle = new WifiDisplayHandle(display.getDeviceName(), iface);
159 }
160
161 private void handleDisconnectLocked() {
Jeff Browncbad9762012-09-04 21:57:59 -0700162 if (mDisplayHandle != null) {
163 mDisplayHandle.disposeLocked();
164 mDisplayHandle = null;
165 }
166 }
167
Jeff Browne08ae382012-09-07 20:36:36 -0700168 private void scheduleStatusChangedBroadcastLocked() {
169 if (!mPendingStatusChangeBroadcast) {
170 mPendingStatusChangeBroadcast = true;
171 getHandler().post(mStatusChangeBroadcast);
172 }
173 }
174
175 private final Runnable mStatusChangeBroadcast = new Runnable() {
176 @Override
177 public void run() {
178 final Intent intent;
179 synchronized (getSyncRoot()) {
180 if (!mPendingStatusChangeBroadcast) {
181 return;
182 }
183
184 mPendingStatusChangeBroadcast = false;
185 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
186 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
187 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
188 getWifiDisplayStatusLocked());
189 }
190
191 // Send protected broadcast about wifi display status to receivers that
192 // have the required permission.
193 getContext().sendBroadcast(intent,
194 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY);
195 }
196 };
197
Jeff Browncbad9762012-09-04 21:57:59 -0700198 private final WifiDisplayController.Listener mWifiDisplayListener =
199 new WifiDisplayController.Listener() {
200 @Override
Jeff Browne08ae382012-09-07 20:36:36 -0700201 public void onEnablementChanged(boolean enabled) {
Jeff Browncbad9762012-09-04 21:57:59 -0700202 synchronized (getSyncRoot()) {
Jeff Browne08ae382012-09-07 20:36:36 -0700203 if (mEnabled != enabled) {
204 mCurrentStatus = null;
205 mEnabled = enabled;
206 scheduleStatusChangedBroadcastLocked();
207 }
208 }
209 }
210
211 @Override
212 public void onScanStarted() {
213 synchronized (getSyncRoot()) {
214 if (!mScanInProgress) {
215 mCurrentStatus = null;
216 mScanInProgress = true;
217 scheduleStatusChangedBroadcastLocked();
218 }
219 }
220 }
221
222 public void onScanFinished(WifiDisplay[] knownDisplays) {
223 synchronized (getSyncRoot()) {
224 if (!Arrays.equals(mKnownDisplays, knownDisplays) || mScanInProgress) {
225 mCurrentStatus = null;
226 mKnownDisplays = knownDisplays;
227 mScanInProgress = false;
228 scheduleStatusChangedBroadcastLocked();
229 }
230 }
231 }
232
233 @Override
234 public void onDisplayConnecting(WifiDisplay display) {
235 synchronized (getSyncRoot()) {
236 if (!mConnectionInProgress) {
237 mCurrentStatus = null;
238 mConnectionInProgress = true;
239 scheduleStatusChangedBroadcastLocked();
240 }
241 }
242 }
243
244 @Override
245 public void onDisplayConnectionFailed() {
246 synchronized (getSyncRoot()) {
247 if (mConnectionInProgress) {
248 mCurrentStatus = null;
249 mConnectionInProgress = false;
250 scheduleStatusChangedBroadcastLocked();
251 }
252 }
253 }
254
255 @Override
256 public void onDisplayConnected(WifiDisplay display, String iface) {
257 synchronized (getSyncRoot()) {
258 handleConnectLocked(display, iface);
259
260 if (mConnectedDisplay == null || !mConnectedDisplay.equals(display)
261 || mConnectionInProgress) {
262 mCurrentStatus = null;
263 mConnectedDisplay = display;
264 mConnectionInProgress = false;
265 scheduleStatusChangedBroadcastLocked();
266 }
Jeff Browncbad9762012-09-04 21:57:59 -0700267 }
268 }
269
270 @Override
271 public void onDisplayDisconnected() {
272 // Stop listening.
273 synchronized (getSyncRoot()) {
Jeff Browne08ae382012-09-07 20:36:36 -0700274 handleDisconnectLocked();
275
276 if (mConnectedDisplay != null || mConnectionInProgress) {
277 mCurrentStatus = null;
278 mConnectedDisplay = null;
279 mConnectionInProgress = false;
280 scheduleStatusChangedBroadcastLocked();
281 }
Jeff Browncbad9762012-09-04 21:57:59 -0700282 }
283 }
284 };
285
286 private final class WifiDisplayDevice extends DisplayDevice {
287 private final String mName;
288 private final int mWidth;
289 private final int mHeight;
290 private final float mRefreshRate;
291 private final int mFlags;
292
293 private Surface mSurface;
294 private DisplayDeviceInfo mInfo;
295
296 public WifiDisplayDevice(IBinder displayToken, String name,
297 int width, int height, float refreshRate, int flags,
298 Surface surface) {
299 super(WifiDisplayAdapter.this, displayToken);
300 mName = name;
301 mWidth = width;
302 mHeight = height;
303 mRefreshRate = refreshRate;
304 mFlags = flags;
305 mSurface = surface;
306 }
307
308 public void clearSurfaceLocked() {
309 mSurface = null;
310 sendTraversalRequestLocked();
311 }
312
313 @Override
314 public void performTraversalInTransactionLocked() {
315 setSurfaceInTransactionLocked(mSurface);
316 }
317
318 @Override
319 public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
320 if (mInfo == null) {
321 mInfo = new DisplayDeviceInfo();
322 mInfo.name = mName;
323 mInfo.width = mWidth;
324 mInfo.height = mHeight;
325 mInfo.refreshRate = mRefreshRate;
326 mInfo.flags = mFlags;
327 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
328 }
329 return mInfo;
330 }
331 }
332
333 private final class WifiDisplayHandle implements RemoteDisplay.Listener {
334 private final String mName;
335 private final String mIface;
336 private final RemoteDisplay mRemoteDisplay;
337
338 private WifiDisplayDevice mDevice;
339 private int mLastError;
340
341 public WifiDisplayHandle(String name, String iface) {
342 mName = name;
343 mIface = iface;
344 mRemoteDisplay = RemoteDisplay.listen(iface, this, getHandler());
345
346 Slog.i(TAG, "Listening for Wifi display connections on " + iface
347 + " from " + mName);
348 }
349
350 public void disposeLocked() {
351 Slog.i(TAG, "Stopped listening for Wifi display connections on " + mIface
352 + " from " + mName);
353
354 removeDisplayLocked();
355 mRemoteDisplay.dispose();
356 }
357
358 public void dumpLocked(PrintWriter pw) {
359 pw.println(" " + mName + ": " + (mDevice != null ? "connected" : "disconnected"));
360 pw.println(" mIface=" + mIface);
361 pw.println(" mLastError=" + mLastError);
362 }
363
364 // Called on the handler thread.
365 @Override
366 public void onDisplayConnected(Surface surface, int width, int height, int flags) {
367 synchronized (getSyncRoot()) {
368 mLastError = 0;
369 removeDisplayLocked();
370 addDisplayLocked(surface, width, height, flags);
371
372 Slog.i(TAG, "Wifi display connected: " + mName);
373 }
374 }
375
376 // Called on the handler thread.
377 @Override
378 public void onDisplayDisconnected() {
379 synchronized (getSyncRoot()) {
380 mLastError = 0;
381 removeDisplayLocked();
382
383 Slog.i(TAG, "Wifi display disconnected: " + mName);
384 }
385 }
386
387 // Called on the handler thread.
388 @Override
389 public void onDisplayError(int error) {
390 synchronized (getSyncRoot()) {
391 mLastError = error;
392 removeDisplayLocked();
393
394 Slog.i(TAG, "Wifi display disconnected due to error " + error + ": " + mName);
395 }
396 }
397
398 private void addDisplayLocked(Surface surface, int width, int height, int flags) {
399 int deviceFlags = 0;
400 if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
401 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
402 }
403
404 float refreshRate = 60.0f; // TODO: get this for real
405
406 IBinder displayToken = Surface.createDisplay(mName);
407 mDevice = new WifiDisplayDevice(displayToken, mName, width, height,
408 refreshRate, deviceFlags, surface);
409 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
410 }
411
412 private void removeDisplayLocked() {
413 if (mDevice != null) {
414 mDevice.clearSurfaceLocked();
415 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
416 mDevice = null;
417 }
418 }
419 }
420}