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