WifiDisplayAdapter.java revision f8f0eddd07d22ab815d97dd32ae6ed52dc31a80c
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 WifiDisplayController mDisplayController;
53    private WifiDisplayDevice mDisplayDevice;
54
55    private WifiDisplayStatus mCurrentStatus;
56    private boolean mEnabled;
57    private int mScanState;
58    private int mActiveDisplayState;
59    private WifiDisplay mActiveDisplay;
60    private WifiDisplay[] mKnownDisplays = WifiDisplay.EMPTY_ARRAY;
61
62    private boolean mPendingStatusChangeBroadcast;
63
64    public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
65            Context context, Handler handler, Listener listener) {
66        super(syncRoot, context, handler, listener, TAG);
67    }
68
69    @Override
70    public void dumpLocked(PrintWriter pw) {
71        super.dumpLocked(pw);
72
73        pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
74        pw.println("mEnabled=" + mEnabled);
75        pw.println("mScanState=" + mScanState);
76        pw.println("mActiveDisplayState=" + mActiveDisplayState);
77        pw.println("mActiveDisplay=" + mActiveDisplay);
78        pw.println("mKnownDisplays=" + Arrays.toString(mKnownDisplays));
79        pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
80
81        // Try to dump the controller state.
82        if (mDisplayController == null) {
83            pw.println("mDisplayController=null");
84        } else {
85            pw.println("mDisplayController:");
86            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
87            ipw.increaseIndent();
88            DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200);
89        }
90    }
91
92    @Override
93    public void registerLocked() {
94        super.registerLocked();
95
96        getHandler().post(new Runnable() {
97            @Override
98            public void run() {
99                mDisplayController = new WifiDisplayController(
100                        getContext(), getHandler(), mWifiDisplayListener);
101            }
102        });
103    }
104
105    public void requestScanLocked() {
106        getHandler().post(new Runnable() {
107            @Override
108            public void run() {
109                if (mDisplayController != null) {
110                    mDisplayController.requestScan();
111                }
112            }
113        });
114    }
115
116    public void requestConnectLocked(final String address) {
117        getHandler().post(new Runnable() {
118            @Override
119            public void run() {
120                if (mDisplayController != null) {
121                    mDisplayController.requestConnect(address);
122                }
123            }
124        });
125    }
126
127    public void requestDisconnectLocked() {
128        getHandler().post(new Runnable() {
129            @Override
130            public void run() {
131                if (mDisplayController != null) {
132                    mDisplayController.requestDisconnect();
133                }
134            }
135        });
136    }
137
138    public WifiDisplayStatus getWifiDisplayStatusLocked() {
139        if (mCurrentStatus == null) {
140            mCurrentStatus = new WifiDisplayStatus(mEnabled, mScanState, mActiveDisplayState,
141                    mActiveDisplay, mKnownDisplays);
142        }
143        return mCurrentStatus;
144    }
145
146    private void handleConnectLocked(WifiDisplay display,
147            Surface surface, int width, int height, int flags) {
148        handleDisconnectLocked();
149
150        int deviceFlags = 0;
151        if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
152            deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
153        }
154
155        float refreshRate = 60.0f; // TODO: get this for real
156
157        String name = display.getDeviceName();
158        IBinder displayToken = Surface.createDisplay(name);
159        mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
160                refreshRate, deviceFlags, surface);
161        sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
162    }
163
164    private void handleDisconnectLocked() {
165        if (mDisplayDevice != null) {
166            mDisplayDevice.clearSurfaceLocked();
167            sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
168            mDisplayDevice = null;
169        }
170    }
171
172    private void scheduleStatusChangedBroadcastLocked() {
173        if (!mPendingStatusChangeBroadcast) {
174            mPendingStatusChangeBroadcast = true;
175            getHandler().post(mStatusChangeBroadcast);
176        }
177    }
178
179    private final Runnable mStatusChangeBroadcast = new Runnable() {
180        @Override
181        public void run() {
182            final Intent intent;
183            synchronized (getSyncRoot()) {
184                if (!mPendingStatusChangeBroadcast) {
185                    return;
186                }
187
188                mPendingStatusChangeBroadcast = false;
189                intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
190                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
191                intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
192                        getWifiDisplayStatusLocked());
193            }
194
195            // Send protected broadcast about wifi display status to receivers that
196            // have the required permission.
197            getContext().sendBroadcast(intent,
198                    android.Manifest.permission.CONFIGURE_WIFI_DISPLAY);
199        }
200    };
201
202    private final WifiDisplayController.Listener mWifiDisplayListener =
203            new WifiDisplayController.Listener() {
204        @Override
205        public void onEnablementChanged(boolean enabled) {
206            synchronized (getSyncRoot()) {
207                if (mEnabled != enabled) {
208                    mCurrentStatus = null;
209                    mEnabled = enabled;
210                    scheduleStatusChangedBroadcastLocked();
211                }
212            }
213        }
214
215        @Override
216        public void onScanStarted() {
217            synchronized (getSyncRoot()) {
218                if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
219                    mCurrentStatus = null;
220                    mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
221                    scheduleStatusChangedBroadcastLocked();
222                }
223            }
224        }
225
226        public void onScanFinished(WifiDisplay[] knownDisplays) {
227            synchronized (getSyncRoot()) {
228                if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING
229                        || !Arrays.equals(mKnownDisplays, knownDisplays)) {
230                    mCurrentStatus = null;
231                    mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
232                    mKnownDisplays = knownDisplays;
233                    scheduleStatusChangedBroadcastLocked();
234                }
235            }
236        }
237
238        @Override
239        public void onDisplayConnecting(WifiDisplay display) {
240            synchronized (getSyncRoot()) {
241                if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
242                        || mActiveDisplay == null
243                        || !mActiveDisplay.equals(display)) {
244                    mCurrentStatus = null;
245                    mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
246                    mActiveDisplay = display;
247                    scheduleStatusChangedBroadcastLocked();
248                }
249            }
250        }
251
252        @Override
253        public void onDisplayConnectionFailed() {
254            synchronized (getSyncRoot()) {
255                if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
256                        || mActiveDisplay != null) {
257                    mCurrentStatus = null;
258                    mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
259                    mActiveDisplay = null;
260                    scheduleStatusChangedBroadcastLocked();
261                }
262            }
263        }
264
265        @Override
266        public void onDisplayConnected(WifiDisplay display, Surface surface,
267                int width, int height, int flags) {
268            synchronized (getSyncRoot()) {
269                handleConnectLocked(display, surface, width, height, flags);
270
271                if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
272                        || mActiveDisplay == null
273                        || !mActiveDisplay.equals(display)) {
274                    mCurrentStatus = null;
275                    mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
276                    mActiveDisplay = display;
277                    scheduleStatusChangedBroadcastLocked();
278                }
279            }
280        }
281
282        @Override
283        public void onDisplayDisconnected() {
284            // Stop listening.
285            synchronized (getSyncRoot()) {
286                handleDisconnectLocked();
287
288                if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
289                        || mActiveDisplay != null) {
290                    mCurrentStatus = null;
291                    mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
292                    mActiveDisplay = null;
293                    scheduleStatusChangedBroadcastLocked();
294                }
295            }
296        }
297    };
298
299    private final class WifiDisplayDevice extends DisplayDevice {
300        private final String mName;
301        private final int mWidth;
302        private final int mHeight;
303        private final float mRefreshRate;
304        private final int mFlags;
305
306        private Surface mSurface;
307        private DisplayDeviceInfo mInfo;
308
309        public WifiDisplayDevice(IBinder displayToken, String name,
310                int width, int height, float refreshRate, int flags,
311                Surface surface) {
312            super(WifiDisplayAdapter.this, displayToken);
313            mName = name;
314            mWidth = width;
315            mHeight = height;
316            mRefreshRate = refreshRate;
317            mFlags = flags;
318            mSurface = surface;
319        }
320
321        public void clearSurfaceLocked() {
322            mSurface = null;
323            sendTraversalRequestLocked();
324        }
325
326        @Override
327        public void performTraversalInTransactionLocked() {
328            setSurfaceInTransactionLocked(mSurface);
329        }
330
331        @Override
332        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
333            if (mInfo == null) {
334                mInfo = new DisplayDeviceInfo();
335                mInfo.name = mName;
336                mInfo.width = mWidth;
337                mInfo.height = mHeight;
338                mInfo.refreshRate = mRefreshRate;
339                mInfo.flags = mFlags;
340                mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
341                mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
342            }
343            return mInfo;
344        }
345    }
346}
347