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