• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.display;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.pm.PackageManager;
24 import android.hardware.display.DisplayManager;
25 import android.hardware.display.WifiDisplay;
26 import android.hardware.display.WifiDisplaySessionInfo;
27 import android.hardware.display.WifiDisplayStatus;
28 import android.media.RemoteDisplay;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.UserHandle;
34 import android.util.Slog;
35 import android.view.Display;
36 import android.view.DisplayAddress;
37 import android.view.Surface;
38 import android.view.SurfaceControl;
39 
40 import com.android.internal.util.DumpUtils;
41 import com.android.internal.util.IndentingPrintWriter;
42 
43 import java.io.PrintWriter;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.List;
47 import java.util.Objects;
48 
49 /**
50  * Connects to Wifi displays that implement the Miracast protocol.
51  * <p>
52  * The Wifi display protocol relies on Wifi direct for discovering and pairing
53  * with the display.  Once connected, the Media Server opens an RTSP socket and accepts
54  * a connection from the display.  After session negotiation, the Media Server
55  * streams encoded buffers to the display.
56  * </p><p>
57  * This class is responsible for connecting to Wifi displays and mediating
58  * the interactions between Media Server, Surface Flinger and the Display Manager Service.
59  * </p><p>
60  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
61  * </p>
62  */
63 final class WifiDisplayAdapter extends DisplayAdapter {
64     private static final String TAG = "WifiDisplayAdapter";
65 
66     private static final boolean DEBUG = false;
67 
68     private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1;
69 
70     private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT";
71 
72     // Unique id prefix for wifi displays
73     private static final String DISPLAY_NAME_PREFIX = "wifi:";
74 
75     private final WifiDisplayHandler mHandler;
76     private final PersistentDataStore mPersistentDataStore;
77     private final boolean mSupportsProtectedBuffers;
78 
79     private WifiDisplayController mDisplayController;
80     private WifiDisplayDevice mDisplayDevice;
81 
82     private WifiDisplayStatus mCurrentStatus;
83     private int mFeatureState;
84     private int mScanState;
85     private int mActiveDisplayState;
86     private WifiDisplay mActiveDisplay;
87     private WifiDisplay[] mDisplays = WifiDisplay.EMPTY_ARRAY;
88     private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY;
89     private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
90     private WifiDisplaySessionInfo mSessionInfo;
91 
92     private boolean mPendingStatusChangeBroadcast;
93 
94     // Called with SyncRoot lock held.
WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, PersistentDataStore persistentDataStore)95     public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
96             Context context, Handler handler, Listener listener,
97             PersistentDataStore persistentDataStore) {
98         super(syncRoot, context, handler, listener, TAG);
99 
100         if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
101             throw new RuntimeException("WiFi display was requested, "
102                     + "but there is no WiFi Direct feature");
103         }
104 
105         mHandler = new WifiDisplayHandler(handler.getLooper());
106         mPersistentDataStore = persistentDataStore;
107         mSupportsProtectedBuffers = context.getResources().getBoolean(
108                 com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
109     }
110 
111     @Override
dumpLocked(PrintWriter pw)112     public void dumpLocked(PrintWriter pw) {
113         super.dumpLocked(pw);
114 
115         pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
116         pw.println("mFeatureState=" + mFeatureState);
117         pw.println("mScanState=" + mScanState);
118         pw.println("mActiveDisplayState=" + mActiveDisplayState);
119         pw.println("mActiveDisplay=" + mActiveDisplay);
120         pw.println("mDisplays=" + Arrays.toString(mDisplays));
121         pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
122         pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
123         pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
124         pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
125 
126         // Try to dump the controller state.
127         if (mDisplayController == null) {
128             pw.println("mDisplayController=null");
129         } else {
130             pw.println("mDisplayController:");
131             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
132             ipw.increaseIndent();
133             DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, "", 200);
134         }
135     }
136 
137     @Override
registerLocked()138     public void registerLocked() {
139         super.registerLocked();
140 
141         updateRememberedDisplaysLocked();
142 
143         getHandler().post(new Runnable() {
144             @Override
145             public void run() {
146                 mDisplayController = new WifiDisplayController(
147                         getContext(), getHandler(), mWifiDisplayListener);
148 
149                 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
150                         new IntentFilter(ACTION_DISCONNECT), null, mHandler,
151                         Context.RECEIVER_NOT_EXPORTED);
152             }
153         });
154     }
155 
requestStartScanLocked()156     public void requestStartScanLocked() {
157         if (DEBUG) {
158             Slog.d(TAG, "requestStartScanLocked");
159         }
160 
161         getHandler().post(new Runnable() {
162             @Override
163             public void run() {
164                 if (mDisplayController != null) {
165                     mDisplayController.requestStartScan();
166                 }
167             }
168         });
169     }
170 
requestStopScanLocked()171     public void requestStopScanLocked() {
172         if (DEBUG) {
173             Slog.d(TAG, "requestStopScanLocked");
174         }
175 
176         getHandler().post(new Runnable() {
177             @Override
178             public void run() {
179                 if (mDisplayController != null) {
180                     mDisplayController.requestStopScan();
181                 }
182             }
183         });
184     }
185 
requestConnectLocked(final String address)186     public void requestConnectLocked(final String address) {
187         if (DEBUG) {
188             Slog.d(TAG, "requestConnectLocked: address=" + address);
189         }
190 
191         getHandler().post(new Runnable() {
192             @Override
193             public void run() {
194                 if (mDisplayController != null) {
195                     mDisplayController.requestConnect(address);
196                 }
197             }
198         });
199     }
200 
requestPauseLocked()201     public void requestPauseLocked() {
202         if (DEBUG) {
203             Slog.d(TAG, "requestPauseLocked");
204         }
205 
206         getHandler().post(new Runnable() {
207             @Override
208             public void run() {
209                 if (mDisplayController != null) {
210                     mDisplayController.requestPause();
211                 }
212             }
213         });
214       }
215 
requestResumeLocked()216     public void requestResumeLocked() {
217         if (DEBUG) {
218             Slog.d(TAG, "requestResumeLocked");
219         }
220 
221         getHandler().post(new Runnable() {
222             @Override
223             public void run() {
224                 if (mDisplayController != null) {
225                     mDisplayController.requestResume();
226                 }
227             }
228         });
229     }
230 
requestDisconnectLocked()231     public void requestDisconnectLocked() {
232         if (DEBUG) {
233             Slog.d(TAG, "requestDisconnectedLocked");
234         }
235 
236         getHandler().post(new Runnable() {
237             @Override
238             public void run() {
239                 if (mDisplayController != null) {
240                     mDisplayController.requestDisconnect();
241                 }
242             }
243         });
244     }
245 
requestRenameLocked(String address, String alias)246     public void requestRenameLocked(String address, String alias) {
247         if (DEBUG) {
248             Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias);
249         }
250 
251         if (alias != null) {
252             alias = alias.trim();
253             if (alias.isEmpty() || alias.equals(address)) {
254                 alias = null;
255             }
256         }
257 
258         WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address);
259         if (display != null && !Objects.equals(display.getDeviceAlias(), alias)) {
260             display = new WifiDisplay(address, display.getDeviceName(), alias,
261                     false, false, false);
262             if (mPersistentDataStore.rememberWifiDisplay(display)) {
263                 mPersistentDataStore.saveIfNeeded();
264                 updateRememberedDisplaysLocked();
265                 scheduleStatusChangedBroadcastLocked();
266             }
267         }
268 
269         if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
270             renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName());
271         }
272     }
273 
requestForgetLocked(String address)274     public void requestForgetLocked(String address) {
275         if (DEBUG) {
276             Slog.d(TAG, "requestForgetLocked: address=" + address);
277         }
278 
279         if (mPersistentDataStore.forgetWifiDisplay(address)) {
280             mPersistentDataStore.saveIfNeeded();
281             updateRememberedDisplaysLocked();
282             scheduleStatusChangedBroadcastLocked();
283         }
284 
285         if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
286             requestDisconnectLocked();
287         }
288     }
289 
getWifiDisplayStatusLocked()290     public WifiDisplayStatus getWifiDisplayStatusLocked() {
291         if (mCurrentStatus == null) {
292             mCurrentStatus = new WifiDisplayStatus(
293                     mFeatureState, mScanState, mActiveDisplayState,
294                     mActiveDisplay, mDisplays, mSessionInfo);
295         }
296 
297         if (DEBUG) {
298             Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus);
299         }
300         return mCurrentStatus;
301     }
302 
updateDisplaysLocked()303     private void updateDisplaysLocked() {
304         List<WifiDisplay> displays = new ArrayList<WifiDisplay>(
305                 mAvailableDisplays.length + mRememberedDisplays.length);
306         boolean[] remembered = new boolean[mAvailableDisplays.length];
307         for (WifiDisplay d : mRememberedDisplays) {
308             boolean available = false;
309             for (int i = 0; i < mAvailableDisplays.length; i++) {
310                 if (d.equals(mAvailableDisplays[i])) {
311                     remembered[i] = available = true;
312                     break;
313                 }
314             }
315             if (!available) {
316                 displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
317                         d.getDeviceAlias(), false, false, true));
318             }
319         }
320         for (int i = 0; i < mAvailableDisplays.length; i++) {
321             WifiDisplay d = mAvailableDisplays[i];
322             displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
323                     d.getDeviceAlias(), true, d.canConnect(), remembered[i]));
324         }
325         mDisplays = displays.toArray(WifiDisplay.EMPTY_ARRAY);
326     }
327 
updateRememberedDisplaysLocked()328     private void updateRememberedDisplaysLocked() {
329         mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays();
330         mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay);
331         mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
332         updateDisplaysLocked();
333     }
334 
fixRememberedDisplayNamesFromAvailableDisplaysLocked()335     private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() {
336         // It may happen that a display name has changed since it was remembered.
337         // Consult the list of available displays and update the name if needed.
338         // We don't do anything special for the active display here.  The display
339         // controller will send a separate event when it needs to be updates.
340         boolean changed = false;
341         for (int i = 0; i < mRememberedDisplays.length; i++) {
342             WifiDisplay rememberedDisplay = mRememberedDisplays[i];
343             WifiDisplay availableDisplay = findAvailableDisplayLocked(
344                     rememberedDisplay.getDeviceAddress());
345             if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) {
346                 if (DEBUG) {
347                     Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: "
348                             + "updating remembered display to " + availableDisplay);
349                 }
350                 mRememberedDisplays[i] = availableDisplay;
351                 changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay);
352             }
353         }
354         if (changed) {
355             mPersistentDataStore.saveIfNeeded();
356         }
357     }
358 
findAvailableDisplayLocked(String address)359     private WifiDisplay findAvailableDisplayLocked(String address) {
360         for (WifiDisplay display : mAvailableDisplays) {
361             if (display.getDeviceAddress().equals(address)) {
362                 return display;
363             }
364         }
365         return null;
366     }
367 
addDisplayDeviceLocked(WifiDisplay display, Surface surface, int width, int height, int flags)368     private void addDisplayDeviceLocked(WifiDisplay display,
369             Surface surface, int width, int height, int flags) {
370         removeDisplayDeviceLocked();
371 
372         if (mPersistentDataStore.rememberWifiDisplay(display)) {
373             mPersistentDataStore.saveIfNeeded();
374             updateRememberedDisplaysLocked();
375             scheduleStatusChangedBroadcastLocked();
376         }
377 
378         boolean secure = (flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0;
379         int deviceFlags = DisplayDeviceInfo.FLAG_PRESENTATION;
380         if (secure) {
381             deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
382             if (mSupportsProtectedBuffers) {
383                 deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
384             }
385         }
386 
387         float refreshRate = 60.0f; // TODO: get this for real
388 
389         String name = display.getFriendlyDisplayName();
390         String address = display.getDeviceAddress();
391         IBinder displayToken = SurfaceControl.createDisplay(name, secure);
392         mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
393                 refreshRate, deviceFlags, address, surface);
394         sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
395     }
396 
removeDisplayDeviceLocked()397     private void removeDisplayDeviceLocked() {
398         if (mDisplayDevice != null) {
399             mDisplayDevice.destroyLocked();
400             sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
401             mDisplayDevice = null;
402         }
403     }
404 
renameDisplayDeviceLocked(String name)405     private void renameDisplayDeviceLocked(String name) {
406         if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) {
407             mDisplayDevice.setNameLocked(name);
408             sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
409         }
410     }
411 
scheduleStatusChangedBroadcastLocked()412     private void scheduleStatusChangedBroadcastLocked() {
413         mCurrentStatus = null;
414         if (!mPendingStatusChangeBroadcast) {
415             mPendingStatusChangeBroadcast = true;
416             mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);
417         }
418     }
419 
420     // Runs on the handler.
handleSendStatusChangeBroadcast()421     private void handleSendStatusChangeBroadcast() {
422         final Intent intent;
423         synchronized (getSyncRoot()) {
424             if (!mPendingStatusChangeBroadcast) {
425                 return;
426             }
427 
428             mPendingStatusChangeBroadcast = false;
429             intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
430             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
431             intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
432                     getWifiDisplayStatusLocked());
433         }
434 
435         // Send protected broadcast about wifi display status to registered receivers.
436         getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
437     }
438 
439     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
440         @Override
441         public void onReceive(Context context, Intent intent) {
442             if (intent.getAction().equals(ACTION_DISCONNECT)) {
443                 synchronized (getSyncRoot()) {
444                     requestDisconnectLocked();
445                 }
446             }
447         }
448     };
449 
450     private final WifiDisplayController.Listener mWifiDisplayListener =
451             new WifiDisplayController.Listener() {
452         @Override
453         public void onFeatureStateChanged(int featureState) {
454             synchronized (getSyncRoot()) {
455                 if (mFeatureState != featureState) {
456                     mFeatureState = featureState;
457                     scheduleStatusChangedBroadcastLocked();
458                 }
459             }
460         }
461 
462         @Override
463         public void onScanStarted() {
464             synchronized (getSyncRoot()) {
465                 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
466                     mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
467                     scheduleStatusChangedBroadcastLocked();
468                 }
469             }
470         }
471 
472         @Override
473         public void onScanResults(WifiDisplay[] availableDisplays) {
474             synchronized (getSyncRoot()) {
475                 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
476                         availableDisplays);
477 
478                 boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);
479 
480                 // Check whether any of the available displays changed canConnect status.
481                 for (int i = 0; !changed && i<availableDisplays.length; i++) {
482                     changed = availableDisplays[i].canConnect()
483                             != mAvailableDisplays[i].canConnect();
484                 }
485 
486                 if (changed) {
487                     mAvailableDisplays = availableDisplays;
488                     fixRememberedDisplayNamesFromAvailableDisplaysLocked();
489                     updateDisplaysLocked();
490                     scheduleStatusChangedBroadcastLocked();
491                 }
492             }
493         }
494 
495         @Override
496         public void onScanFinished() {
497             synchronized (getSyncRoot()) {
498                 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING) {
499                     mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
500                     scheduleStatusChangedBroadcastLocked();
501                 }
502             }
503         }
504 
505         @Override
506         public void onDisplayConnecting(WifiDisplay display) {
507             synchronized (getSyncRoot()) {
508                 display = mPersistentDataStore.applyWifiDisplayAlias(display);
509 
510                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
511                         || mActiveDisplay == null
512                         || !mActiveDisplay.equals(display)) {
513                     mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
514                     mActiveDisplay = display;
515                     scheduleStatusChangedBroadcastLocked();
516                 }
517             }
518         }
519 
520         @Override
521         public void onDisplayConnectionFailed() {
522             synchronized (getSyncRoot()) {
523                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
524                         || mActiveDisplay != null) {
525                     mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
526                     mActiveDisplay = null;
527                     scheduleStatusChangedBroadcastLocked();
528                 }
529             }
530         }
531 
532         @Override
533         public void onDisplayConnected(WifiDisplay display, Surface surface,
534                 int width, int height, int flags) {
535             synchronized (getSyncRoot()) {
536                 display = mPersistentDataStore.applyWifiDisplayAlias(display);
537                 addDisplayDeviceLocked(display, surface, width, height, flags);
538 
539                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
540                         || mActiveDisplay == null
541                         || !mActiveDisplay.equals(display)) {
542                     mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
543                     mActiveDisplay = display;
544                     scheduleStatusChangedBroadcastLocked();
545                 }
546             }
547         }
548 
549         @Override
550         public void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo) {
551             synchronized (getSyncRoot()) {
552                 mSessionInfo = sessionInfo;
553                 scheduleStatusChangedBroadcastLocked();
554             }
555         }
556 
557         @Override
558         public void onDisplayChanged(WifiDisplay display) {
559             synchronized (getSyncRoot()) {
560                 display = mPersistentDataStore.applyWifiDisplayAlias(display);
561                 if (mActiveDisplay != null
562                         && mActiveDisplay.hasSameAddress(display)
563                         && !mActiveDisplay.equals(display)) {
564                     mActiveDisplay = display;
565                     renameDisplayDeviceLocked(display.getFriendlyDisplayName());
566                     scheduleStatusChangedBroadcastLocked();
567                 }
568             }
569         }
570 
571         @Override
572         public void onDisplayDisconnected() {
573             // Stop listening.
574             synchronized (getSyncRoot()) {
575                 removeDisplayDeviceLocked();
576 
577                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
578                         || mActiveDisplay != null) {
579                     mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
580                     mActiveDisplay = null;
581                     scheduleStatusChangedBroadcastLocked();
582                 }
583             }
584         }
585     };
586 
587     private final class WifiDisplayDevice extends DisplayDevice {
588         private String mName;
589         private final int mWidth;
590         private final int mHeight;
591         private final float mRefreshRate;
592         private final int mFlags;
593         private final DisplayAddress mAddress;
594         private final Display.Mode mMode;
595 
596         private Surface mSurface;
597         private DisplayDeviceInfo mInfo;
598 
WifiDisplayDevice(IBinder displayToken, String name, int width, int height, float refreshRate, int flags, String address, Surface surface)599         public WifiDisplayDevice(IBinder displayToken, String name,
600                 int width, int height, float refreshRate, int flags, String address,
601                 Surface surface) {
602             super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address,
603                     getContext());
604             mName = name;
605             mWidth = width;
606             mHeight = height;
607             mRefreshRate = refreshRate;
608             mFlags = flags;
609             mAddress = DisplayAddress.fromMacAddress(address);
610             mSurface = surface;
611             mMode = createMode(width, height, refreshRate);
612         }
613 
614         @Override
hasStableUniqueId()615         public boolean hasStableUniqueId() {
616             return true;
617         }
618 
destroyLocked()619         public void destroyLocked() {
620             if (mSurface != null) {
621                 mSurface.release();
622                 mSurface = null;
623             }
624             SurfaceControl.destroyDisplay(getDisplayTokenLocked());
625         }
626 
setNameLocked(String name)627         public void setNameLocked(String name) {
628             mName = name;
629             mInfo = null;
630         }
631 
632         @Override
performTraversalLocked(SurfaceControl.Transaction t)633         public void performTraversalLocked(SurfaceControl.Transaction t) {
634             if (mSurface != null) {
635                 setSurfaceLocked(t, mSurface);
636             }
637         }
638 
639         @Override
getDisplayDeviceInfoLocked()640         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
641             if (mInfo == null) {
642                 mInfo = new DisplayDeviceInfo();
643                 mInfo.name = mName;
644                 mInfo.uniqueId = getUniqueId();
645                 mInfo.width = mWidth;
646                 mInfo.height = mHeight;
647                 mInfo.modeId = mMode.getModeId();
648                 mInfo.defaultModeId = mMode.getModeId();
649                 mInfo.supportedModes = new Display.Mode[] { mMode };
650                 mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame
651                 mInfo.flags = mFlags;
652                 mInfo.type = Display.TYPE_WIFI;
653                 mInfo.address = mAddress;
654                 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
655                 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
656                 // The display is trusted since it is created by system.
657                 mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
658             }
659             return mInfo;
660         }
661     }
662 
663     private final class WifiDisplayHandler extends Handler {
WifiDisplayHandler(Looper looper)664         public WifiDisplayHandler(Looper looper) {
665             super(looper, null, true /*async*/);
666         }
667 
668         @Override
handleMessage(Message msg)669         public void handleMessage(Message msg) {
670             switch (msg.what) {
671                 case MSG_SEND_STATUS_CHANGE_BROADCAST:
672                     handleSendStatusChangeBroadcast();
673                     break;
674             }
675         }
676     }
677 }
678