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