WifiDisplayAdapter.java revision ee4f029ff4905abbdbc1ce47db2cf2c3510949e9
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        if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)
203                && mDisplayDevice != null) {
204            mDisplayDevice.setNameLocked(mActiveDisplay.getFriendlyDisplayName());
205            sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
206        }
207    }
208
209    public void requestForgetLocked(String address) {
210        if (DEBUG) {
211            Slog.d(TAG, "requestForgetLocked: address=" + address);
212        }
213
214        if (mPersistentDataStore.forgetWifiDisplay(address)) {
215            mPersistentDataStore.saveIfNeeded();
216            updateRememberedDisplaysLocked();
217            scheduleStatusChangedBroadcastLocked();
218        }
219
220        if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
221            requestDisconnectLocked();
222        }
223    }
224
225    public WifiDisplayStatus getWifiDisplayStatusLocked() {
226        if (mCurrentStatus == null) {
227            mCurrentStatus = new WifiDisplayStatus(
228                    mFeatureState, mScanState, mActiveDisplayState,
229                    mActiveDisplay, mAvailableDisplays, mRememberedDisplays);
230        }
231
232        if (DEBUG) {
233            Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus);
234        }
235        return mCurrentStatus;
236    }
237
238    private void updateRememberedDisplaysLocked() {
239        mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays();
240        mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay);
241        mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
242    }
243
244    private void handleConnectLocked(WifiDisplay display,
245            Surface surface, int width, int height, int flags) {
246        handleDisconnectLocked();
247
248        if (mPersistentDataStore.rememberWifiDisplay(display)) {
249            mPersistentDataStore.saveIfNeeded();
250            updateRememberedDisplaysLocked();
251            scheduleStatusChangedBroadcastLocked();
252        }
253
254        int deviceFlags = 0;
255        if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
256            deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
257        }
258        if (mSupportsProtectedBuffers) {
259            deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
260        }
261
262        float refreshRate = 60.0f; // TODO: get this for real
263
264        String name = display.getFriendlyDisplayName();
265        IBinder displayToken = Surface.createDisplay(name);
266        mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
267                refreshRate, deviceFlags, surface);
268        sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
269    }
270
271    private void handleDisconnectLocked() {
272        if (mDisplayDevice != null) {
273            mDisplayDevice.clearSurfaceLocked();
274            sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
275            mDisplayDevice = null;
276        }
277    }
278
279    private void scheduleStatusChangedBroadcastLocked() {
280        mCurrentStatus = null;
281        if (!mPendingStatusChangeBroadcast) {
282            mPendingStatusChangeBroadcast = true;
283            getHandler().post(mStatusChangeBroadcast);
284        }
285    }
286
287    private final Runnable mStatusChangeBroadcast = new Runnable() {
288        @Override
289        public void run() {
290            final Intent intent;
291            synchronized (getSyncRoot()) {
292                if (!mPendingStatusChangeBroadcast) {
293                    return;
294                }
295
296                mPendingStatusChangeBroadcast = false;
297                intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
298                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
299                intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
300                        getWifiDisplayStatusLocked());
301            }
302
303            // Send protected broadcast about wifi display status to registered receivers.
304            getContext().sendBroadcast(intent);
305        }
306    };
307
308    private final WifiDisplayController.Listener mWifiDisplayListener =
309            new WifiDisplayController.Listener() {
310        @Override
311        public void onFeatureStateChanged(int featureState) {
312            synchronized (getSyncRoot()) {
313                if (mFeatureState != featureState) {
314                    mFeatureState = featureState;
315                    scheduleStatusChangedBroadcastLocked();
316                }
317            }
318        }
319
320        @Override
321        public void onScanStarted() {
322            synchronized (getSyncRoot()) {
323                if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
324                    mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
325                    scheduleStatusChangedBroadcastLocked();
326                }
327            }
328        }
329
330        @Override
331        public void onScanFinished(WifiDisplay[] availableDisplays) {
332            synchronized (getSyncRoot()) {
333                availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
334                        availableDisplays);
335
336                if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING
337                        || !Arrays.equals(mAvailableDisplays, availableDisplays)) {
338                    mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
339                    mAvailableDisplays = availableDisplays;
340                    scheduleStatusChangedBroadcastLocked();
341                }
342            }
343        }
344
345        @Override
346        public void onDisplayConnecting(WifiDisplay display) {
347            synchronized (getSyncRoot()) {
348                display = mPersistentDataStore.applyWifiDisplayAlias(display);
349
350                if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
351                        || mActiveDisplay == null
352                        || !mActiveDisplay.equals(display)) {
353                    mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
354                    mActiveDisplay = display;
355                    scheduleStatusChangedBroadcastLocked();
356                }
357            }
358        }
359
360        @Override
361        public void onDisplayConnectionFailed() {
362            synchronized (getSyncRoot()) {
363                if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
364                        || mActiveDisplay != null) {
365                    mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
366                    mActiveDisplay = null;
367                    scheduleStatusChangedBroadcastLocked();
368                }
369            }
370        }
371
372        @Override
373        public void onDisplayConnected(WifiDisplay display, Surface surface,
374                int width, int height, int flags) {
375            synchronized (getSyncRoot()) {
376                display = mPersistentDataStore.applyWifiDisplayAlias(display);
377                handleConnectLocked(display, surface, width, height, flags);
378
379                if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
380                        || mActiveDisplay == null
381                        || !mActiveDisplay.equals(display)) {
382                    mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
383                    mActiveDisplay = display;
384                    scheduleStatusChangedBroadcastLocked();
385                }
386            }
387        }
388
389        @Override
390        public void onDisplayDisconnected() {
391            // Stop listening.
392            synchronized (getSyncRoot()) {
393                handleDisconnectLocked();
394
395                if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
396                        || mActiveDisplay != null) {
397                    mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
398                    mActiveDisplay = null;
399                    scheduleStatusChangedBroadcastLocked();
400                }
401            }
402        }
403    };
404
405    private final class WifiDisplayDevice extends DisplayDevice {
406        private String mName;
407        private final int mWidth;
408        private final int mHeight;
409        private final float mRefreshRate;
410        private final int mFlags;
411
412        private Surface mSurface;
413        private DisplayDeviceInfo mInfo;
414
415        public WifiDisplayDevice(IBinder displayToken, String name,
416                int width, int height, float refreshRate, int flags,
417                Surface surface) {
418            super(WifiDisplayAdapter.this, displayToken);
419            mName = name;
420            mWidth = width;
421            mHeight = height;
422            mRefreshRate = refreshRate;
423            mFlags = flags;
424            mSurface = surface;
425        }
426
427        public void clearSurfaceLocked() {
428            mSurface = null;
429            sendTraversalRequestLocked();
430        }
431
432        public void setNameLocked(String name) {
433            mName = name;
434            mInfo = null;
435        }
436
437        @Override
438        public void performTraversalInTransactionLocked() {
439            setSurfaceInTransactionLocked(mSurface);
440        }
441
442        @Override
443        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
444            if (mInfo == null) {
445                mInfo = new DisplayDeviceInfo();
446                mInfo.name = mName;
447                mInfo.width = mWidth;
448                mInfo.height = mHeight;
449                mInfo.refreshRate = mRefreshRate;
450                mInfo.flags = mFlags;
451                mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
452                mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
453            }
454            return mInfo;
455        }
456    }
457}
458