blob: c8a44d20554f5a06c98cf2fdb2ca474cada30375 [file] [log] [blame]
Jeff Browncbad9762012-09-04 21:57:59 -07001/*
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
Jeff Browna7f9c962012-10-17 15:15:12 -070019import com.android.internal.R;
Jeff Browncbad9762012-09-04 21:57:59 -070020import com.android.internal.util.DumpUtils;
21import com.android.internal.util.IndentingPrintWriter;
22
Jeff Browna7f9c962012-10-17 15:15:12 -070023import android.app.Notification;
24import android.app.NotificationManager;
25import android.app.PendingIntent;
26import android.content.BroadcastReceiver;
Jeff Browncbad9762012-09-04 21:57:59 -070027import android.content.Context;
Jeff Browne08ae382012-09-07 20:36:36 -070028import android.content.Intent;
Jeff Browna7f9c962012-10-17 15:15:12 -070029import android.content.IntentFilter;
30import android.content.res.Resources;
Jeff Browne08ae382012-09-07 20:36:36 -070031import android.hardware.display.DisplayManager;
32import android.hardware.display.WifiDisplay;
33import android.hardware.display.WifiDisplayStatus;
Jeff Browncbad9762012-09-04 21:57:59 -070034import android.media.RemoteDisplay;
35import android.os.Handler;
36import android.os.IBinder;
Jeff Browna7f9c962012-10-17 15:15:12 -070037import android.os.Looper;
38import android.os.Message;
39import android.os.UserHandle;
40import android.provider.Settings;
Jeff Brownbc335452012-09-26 18:34:47 -070041import android.util.Slog;
Jeff Brown92130f62012-10-24 21:28:33 -070042import android.view.Display;
Jeff Browncbad9762012-09-04 21:57:59 -070043import android.view.Surface;
44
45import java.io.PrintWriter;
Jeff Browne08ae382012-09-07 20:36:36 -070046import java.util.Arrays;
Jeff Browncbad9762012-09-04 21:57:59 -070047
Jeff Brown74da1092012-11-07 16:02:13 -080048import libcore.util.Objects;
49
Jeff Browncbad9762012-09-04 21:57:59 -070050/**
51 * Connects to Wifi displays that implement the Miracast protocol.
52 * <p>
53 * The Wifi display protocol relies on Wifi direct for discovering and pairing
54 * with the display. Once connected, the Media Server opens an RTSP socket and accepts
55 * a connection from the display. After session negotiation, the Media Server
56 * streams encoded buffers to the display.
57 * </p><p>
58 * This class is responsible for connecting to Wifi displays and mediating
59 * the interactions between Media Server, Surface Flinger and the Display Manager Service.
60 * </p><p>
61 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
62 * </p>
63 */
64final class WifiDisplayAdapter extends DisplayAdapter {
65 private static final String TAG = "WifiDisplayAdapter";
66
Jeff Brown2444ae72012-10-11 14:30:21 -070067 private static final boolean DEBUG = false;
68
Jeff Browna7f9c962012-10-17 15:15:12 -070069 private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1;
70 private static final int MSG_UPDATE_NOTIFICATION = 2;
71
72 private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT";
73
74 private final WifiDisplayHandler mHandler;
Jeff Brown77aebfd2012-10-01 21:07:03 -070075 private final PersistentDataStore mPersistentDataStore;
76 private final boolean mSupportsProtectedBuffers;
Jeff Browna7f9c962012-10-17 15:15:12 -070077 private final NotificationManager mNotificationManager;
78
Jeff Brown66692502012-10-18 16:13:44 -070079 private PendingIntent mSettingsPendingIntent;
80 private PendingIntent mDisconnectPendingIntent;
Jeff Brown89d55462012-09-19 11:33:42 -070081
Jeff Browncbad9762012-09-04 21:57:59 -070082 private WifiDisplayController mDisplayController;
Jeff Brownf8f0edd2012-09-11 17:05:11 -070083 private WifiDisplayDevice mDisplayDevice;
Jeff Browncbad9762012-09-04 21:57:59 -070084
Jeff Browne08ae382012-09-07 20:36:36 -070085 private WifiDisplayStatus mCurrentStatus;
Jeff Brown89d55462012-09-19 11:33:42 -070086 private int mFeatureState;
Jeff Brown180bbc72012-09-08 23:15:00 -070087 private int mScanState;
88 private int mActiveDisplayState;
89 private WifiDisplay mActiveDisplay;
Jeff Brown89d55462012-09-19 11:33:42 -070090 private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY;
91 private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
Jeff Browne08ae382012-09-07 20:36:36 -070092
93 private boolean mPendingStatusChangeBroadcast;
Jeff Browna7f9c962012-10-17 15:15:12 -070094 private boolean mPendingNotificationUpdate;
Jeff Browne08ae382012-09-07 20:36:36 -070095
Jeff Brown66692502012-10-18 16:13:44 -070096 // Called with SyncRoot lock held.
Jeff Browncbad9762012-09-04 21:57:59 -070097 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Jeff Brown89d55462012-09-19 11:33:42 -070098 Context context, Handler handler, Listener listener,
99 PersistentDataStore persistentDataStore) {
Jeff Browncbad9762012-09-04 21:57:59 -0700100 super(syncRoot, context, handler, listener, TAG);
Jeff Browna7f9c962012-10-17 15:15:12 -0700101 mHandler = new WifiDisplayHandler(handler.getLooper());
Jeff Brown89d55462012-09-19 11:33:42 -0700102 mPersistentDataStore = persistentDataStore;
Jeff Brown77aebfd2012-10-01 21:07:03 -0700103 mSupportsProtectedBuffers = context.getResources().getBoolean(
104 com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
Jeff Browna7f9c962012-10-17 15:15:12 -0700105 mNotificationManager = (NotificationManager)context.getSystemService(
106 Context.NOTIFICATION_SERVICE);
Jeff Browncbad9762012-09-04 21:57:59 -0700107 }
108
109 @Override
110 public void dumpLocked(PrintWriter pw) {
111 super.dumpLocked(pw);
112
Jeff Browne08ae382012-09-07 20:36:36 -0700113 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
Jeff Brown89d55462012-09-19 11:33:42 -0700114 pw.println("mFeatureState=" + mFeatureState);
Jeff Brown180bbc72012-09-08 23:15:00 -0700115 pw.println("mScanState=" + mScanState);
116 pw.println("mActiveDisplayState=" + mActiveDisplayState);
117 pw.println("mActiveDisplay=" + mActiveDisplay);
Jeff Brown89d55462012-09-19 11:33:42 -0700118 pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
119 pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
Jeff Browne08ae382012-09-07 20:36:36 -0700120 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
Jeff Browna7f9c962012-10-17 15:15:12 -0700121 pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
Jeff Brown77aebfd2012-10-01 21:07:03 -0700122 pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
Jeff Browne08ae382012-09-07 20:36:36 -0700123
Jeff Browncbad9762012-09-04 21:57:59 -0700124 // Try to dump the controller state.
125 if (mDisplayController == null) {
126 pw.println("mDisplayController=null");
127 } else {
128 pw.println("mDisplayController:");
129 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
130 ipw.increaseIndent();
131 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200);
132 }
133 }
134
135 @Override
136 public void registerLocked() {
137 super.registerLocked();
138
Jeff Brown89d55462012-09-19 11:33:42 -0700139 updateRememberedDisplaysLocked();
140
Jeff Browncbad9762012-09-04 21:57:59 -0700141 getHandler().post(new Runnable() {
142 @Override
143 public void run() {
144 mDisplayController = new WifiDisplayController(
145 getContext(), getHandler(), mWifiDisplayListener);
Jeff Brown66692502012-10-18 16:13:44 -0700146
147 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
148 new IntentFilter(ACTION_DISCONNECT), null, mHandler);
Jeff Browncbad9762012-09-04 21:57:59 -0700149 }
150 });
151 }
152
Jeff Browne08ae382012-09-07 20:36:36 -0700153 public void requestScanLocked() {
Jeff Brown2444ae72012-10-11 14:30:21 -0700154 if (DEBUG) {
155 Slog.d(TAG, "requestScanLocked");
156 }
157
Jeff Browne08ae382012-09-07 20:36:36 -0700158 getHandler().post(new Runnable() {
159 @Override
160 public void run() {
161 if (mDisplayController != null) {
162 mDisplayController.requestScan();
163 }
164 }
165 });
Jeff Browncbad9762012-09-04 21:57:59 -0700166 }
167
Jeff Brownbc335452012-09-26 18:34:47 -0700168 public void requestConnectLocked(final String address, final boolean trusted) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700169 if (DEBUG) {
170 Slog.d(TAG, "requestConnectLocked: address=" + address + ", trusted=" + trusted);
171 }
172
Jeff Brownbc335452012-09-26 18:34:47 -0700173 if (!trusted) {
174 synchronized (getSyncRoot()) {
175 if (!isRememberedDisplayLocked(address)) {
176 Slog.w(TAG, "Ignoring request by an untrusted client to connect to "
177 + "an unknown wifi display: " + address);
178 return;
179 }
180 }
181 }
182
Jeff Browne08ae382012-09-07 20:36:36 -0700183 getHandler().post(new Runnable() {
184 @Override
185 public void run() {
186 if (mDisplayController != null) {
187 mDisplayController.requestConnect(address);
188 }
189 }
190 });
191 }
192
Jeff Brownbc335452012-09-26 18:34:47 -0700193 private boolean isRememberedDisplayLocked(String address) {
194 for (WifiDisplay display : mRememberedDisplays) {
195 if (display.getDeviceAddress().equals(address)) {
196 return true;
197 }
198 }
199 return false;
200 }
201
Jeff Browne08ae382012-09-07 20:36:36 -0700202 public void requestDisconnectLocked() {
Jeff Brown2444ae72012-10-11 14:30:21 -0700203 if (DEBUG) {
204 Slog.d(TAG, "requestDisconnectedLocked");
205 }
206
Jeff Browne08ae382012-09-07 20:36:36 -0700207 getHandler().post(new Runnable() {
208 @Override
209 public void run() {
210 if (mDisplayController != null) {
211 mDisplayController.requestDisconnect();
212 }
213 }
214 });
215 }
216
Jeff Brown89d55462012-09-19 11:33:42 -0700217 public void requestRenameLocked(String address, String alias) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700218 if (DEBUG) {
219 Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias);
220 }
221
Jeff Brown89d55462012-09-19 11:33:42 -0700222 if (alias != null) {
223 alias = alias.trim();
Jeff Brown2444ae72012-10-11 14:30:21 -0700224 if (alias.isEmpty() || alias.equals(address)) {
Jeff Brown89d55462012-09-19 11:33:42 -0700225 alias = null;
226 }
227 }
228
Jeff Brown74da1092012-11-07 16:02:13 -0800229 WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address);
230 if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) {
231 display = new WifiDisplay(address, display.getDeviceName(), alias);
232 if (mPersistentDataStore.rememberWifiDisplay(display)) {
233 mPersistentDataStore.saveIfNeeded();
234 updateRememberedDisplaysLocked();
235 scheduleStatusChangedBroadcastLocked();
236 }
Jeff Brown89d55462012-09-19 11:33:42 -0700237 }
Jeff Brownee4f0292012-10-15 15:31:59 -0700238
Jeff Brown74da1092012-11-07 16:02:13 -0800239 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
240 renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName());
Jeff Brownee4f0292012-10-15 15:31:59 -0700241 }
Jeff Brown89d55462012-09-19 11:33:42 -0700242 }
243
244 public void requestForgetLocked(String address) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700245 if (DEBUG) {
246 Slog.d(TAG, "requestForgetLocked: address=" + address);
247 }
248
Jeff Brown89d55462012-09-19 11:33:42 -0700249 if (mPersistentDataStore.forgetWifiDisplay(address)) {
250 mPersistentDataStore.saveIfNeeded();
251 updateRememberedDisplaysLocked();
252 scheduleStatusChangedBroadcastLocked();
253 }
254
255 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
256 requestDisconnectLocked();
257 }
258 }
259
Jeff Browne08ae382012-09-07 20:36:36 -0700260 public WifiDisplayStatus getWifiDisplayStatusLocked() {
261 if (mCurrentStatus == null) {
Jeff Brown89d55462012-09-19 11:33:42 -0700262 mCurrentStatus = new WifiDisplayStatus(
263 mFeatureState, mScanState, mActiveDisplayState,
264 mActiveDisplay, mAvailableDisplays, mRememberedDisplays);
Jeff Browne08ae382012-09-07 20:36:36 -0700265 }
Jeff Brown2444ae72012-10-11 14:30:21 -0700266
267 if (DEBUG) {
268 Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus);
269 }
Jeff Browne08ae382012-09-07 20:36:36 -0700270 return mCurrentStatus;
271 }
272
Jeff Brown89d55462012-09-19 11:33:42 -0700273 private void updateRememberedDisplaysLocked() {
274 mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays();
275 mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay);
276 mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
277 }
278
Jeff Brown74da1092012-11-07 16:02:13 -0800279 private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() {
280 // It may happen that a display name has changed since it was remembered.
281 // Consult the list of available displays and update the name if needed.
282 // We don't do anything special for the active display here. The display
283 // controller will send a separate event when it needs to be updates.
284 boolean changed = false;
285 for (int i = 0; i < mRememberedDisplays.length; i++) {
286 WifiDisplay rememberedDisplay = mRememberedDisplays[i];
287 WifiDisplay availableDisplay = findAvailableDisplayLocked(
288 rememberedDisplay.getDeviceAddress());
289 if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) {
290 if (DEBUG) {
291 Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: "
292 + "updating remembered display to " + availableDisplay);
293 }
294 mRememberedDisplays[i] = availableDisplay;
295 changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay);
296 }
297 }
298 if (changed) {
299 mPersistentDataStore.saveIfNeeded();
300 }
301 }
302
303 private WifiDisplay findAvailableDisplayLocked(String address) {
304 for (WifiDisplay display : mAvailableDisplays) {
305 if (display.getDeviceAddress().equals(address)) {
306 return display;
307 }
308 }
309 return null;
310 }
311
312 private void addDisplayDeviceLocked(WifiDisplay display,
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700313 Surface surface, int width, int height, int flags) {
Jeff Brown74da1092012-11-07 16:02:13 -0800314 removeDisplayDeviceLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700315
Jeff Brown89d55462012-09-19 11:33:42 -0700316 if (mPersistentDataStore.rememberWifiDisplay(display)) {
317 mPersistentDataStore.saveIfNeeded();
318 updateRememberedDisplaysLocked();
319 scheduleStatusChangedBroadcastLocked();
320 }
321
Jeff Brownf0681b32012-10-23 17:35:57 -0700322 boolean secure = (flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0;
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700323 int deviceFlags = 0;
Jeff Brownf0681b32012-10-23 17:35:57 -0700324 if (secure) {
Jeff Brown77aebfd2012-10-01 21:07:03 -0700325 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
Jeff Brownf0681b32012-10-23 17:35:57 -0700326 if (mSupportsProtectedBuffers) {
327 deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
328 }
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700329 }
330
331 float refreshRate = 60.0f; // TODO: get this for real
332
Jeff Brown89d55462012-09-19 11:33:42 -0700333 String name = display.getFriendlyDisplayName();
Jeff Brown92130f62012-10-24 21:28:33 -0700334 String address = display.getDeviceAddress();
Jeff Brownf0681b32012-10-23 17:35:57 -0700335 IBinder displayToken = Surface.createDisplay(name, secure);
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700336 mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
Jeff Brown92130f62012-10-24 21:28:33 -0700337 refreshRate, deviceFlags, address, surface);
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700338 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
Jeff Browna7f9c962012-10-17 15:15:12 -0700339
340 scheduleUpdateNotificationLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700341 }
342
Jeff Brown74da1092012-11-07 16:02:13 -0800343 private void removeDisplayDeviceLocked() {
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700344 if (mDisplayDevice != null) {
345 mDisplayDevice.clearSurfaceLocked();
346 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
347 mDisplayDevice = null;
Jeff Browna7f9c962012-10-17 15:15:12 -0700348
349 scheduleUpdateNotificationLocked();
Jeff Browncbad9762012-09-04 21:57:59 -0700350 }
351 }
352
Jeff Brown74da1092012-11-07 16:02:13 -0800353 private void renameDisplayDeviceLocked(String name) {
354 if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) {
355 mDisplayDevice.setNameLocked(name);
356 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
357 }
358 }
359
Jeff Browne08ae382012-09-07 20:36:36 -0700360 private void scheduleStatusChangedBroadcastLocked() {
Jeff Brown89d55462012-09-19 11:33:42 -0700361 mCurrentStatus = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700362 if (!mPendingStatusChangeBroadcast) {
363 mPendingStatusChangeBroadcast = true;
Jeff Browna7f9c962012-10-17 15:15:12 -0700364 mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);
Jeff Browne08ae382012-09-07 20:36:36 -0700365 }
366 }
367
Jeff Browna7f9c962012-10-17 15:15:12 -0700368 private void scheduleUpdateNotificationLocked() {
369 if (!mPendingNotificationUpdate) {
370 mPendingNotificationUpdate = true;
371 mHandler.sendEmptyMessage(MSG_UPDATE_NOTIFICATION);
372 }
373 }
Jeff Browne08ae382012-09-07 20:36:36 -0700374
Jeff Browna7f9c962012-10-17 15:15:12 -0700375 // Runs on the handler.
376 private void handleSendStatusChangeBroadcast() {
377 final Intent intent;
378 synchronized (getSyncRoot()) {
379 if (!mPendingStatusChangeBroadcast) {
380 return;
Jeff Browne08ae382012-09-07 20:36:36 -0700381 }
382
Jeff Browna7f9c962012-10-17 15:15:12 -0700383 mPendingStatusChangeBroadcast = false;
384 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
385 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
386 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
387 getWifiDisplayStatusLocked());
388 }
389
390 // Send protected broadcast about wifi display status to registered receivers.
391 getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
392 }
393
394 // Runs on the handler.
395 private void handleUpdateNotification() {
396 final boolean isConnected;
397 synchronized (getSyncRoot()) {
398 if (!mPendingNotificationUpdate) {
399 return;
400 }
401
402 mPendingNotificationUpdate = false;
403 isConnected = (mDisplayDevice != null);
404 }
405
Jeff Brown66692502012-10-18 16:13:44 -0700406 // Cancel the old notification if there is one.
Jeff Browna7f9c962012-10-17 15:15:12 -0700407 mNotificationManager.cancelAsUser(null,
408 R.string.wifi_display_notification_title, UserHandle.ALL);
409
410 if (isConnected) {
411 Context context = getContext();
412
Jeff Brown66692502012-10-18 16:13:44 -0700413 // Initialize pending intents for the notification outside of the lock because
414 // creating a pending intent requires a call into the activity manager.
415 if (mSettingsPendingIntent == null) {
416 Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
417 settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
418 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
419 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
420 mSettingsPendingIntent = PendingIntent.getActivityAsUser(
421 context, 0, settingsIntent, 0, null, UserHandle.CURRENT);
422 }
423
424 if (mDisconnectPendingIntent == null) {
425 Intent disconnectIntent = new Intent(ACTION_DISCONNECT);
426 mDisconnectPendingIntent = PendingIntent.getBroadcastAsUser(
427 context, 0, disconnectIntent, 0, UserHandle.CURRENT);
428 }
429
430 // Post the notification.
Jeff Browna7f9c962012-10-17 15:15:12 -0700431 Resources r = context.getResources();
432 Notification notification = new Notification.Builder(context)
433 .setContentTitle(r.getString(
434 R.string.wifi_display_notification_title))
435 .setContentText(r.getString(
436 R.string.wifi_display_notification_message))
437 .setContentIntent(mSettingsPendingIntent)
438 .setSmallIcon(R.drawable.ic_notify_wifidisplay)
439 .setOngoing(true)
440 .addAction(android.R.drawable.ic_menu_close_clear_cancel,
441 r.getString(R.string.wifi_display_notification_disconnect),
442 mDisconnectPendingIntent)
443 .build();
444 mNotificationManager.notifyAsUser(null,
445 R.string.wifi_display_notification_title,
446 notification, UserHandle.ALL);
447 }
448 }
449
450 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
451 @Override
452 public void onReceive(Context context, Intent intent) {
453 if (intent.getAction().equals(ACTION_DISCONNECT)) {
454 synchronized (getSyncRoot()) {
455 requestDisconnectLocked();
456 }
457 }
Jeff Browne08ae382012-09-07 20:36:36 -0700458 }
459 };
460
Jeff Browncbad9762012-09-04 21:57:59 -0700461 private final WifiDisplayController.Listener mWifiDisplayListener =
462 new WifiDisplayController.Listener() {
463 @Override
Jeff Brown89d55462012-09-19 11:33:42 -0700464 public void onFeatureStateChanged(int featureState) {
Jeff Browncbad9762012-09-04 21:57:59 -0700465 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700466 if (mFeatureState != featureState) {
467 mFeatureState = featureState;
Jeff Browne08ae382012-09-07 20:36:36 -0700468 scheduleStatusChangedBroadcastLocked();
469 }
470 }
471 }
472
473 @Override
474 public void onScanStarted() {
475 synchronized (getSyncRoot()) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700476 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700477 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
Jeff Browne08ae382012-09-07 20:36:36 -0700478 scheduleStatusChangedBroadcastLocked();
479 }
480 }
481 }
482
Jeff Brown2444ae72012-10-11 14:30:21 -0700483 @Override
Jeff Brown89d55462012-09-19 11:33:42 -0700484 public void onScanFinished(WifiDisplay[] availableDisplays) {
Jeff Browne08ae382012-09-07 20:36:36 -0700485 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700486 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
487 availableDisplays);
488
Jeff Brown180bbc72012-09-08 23:15:00 -0700489 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING
Jeff Brown89d55462012-09-19 11:33:42 -0700490 || !Arrays.equals(mAvailableDisplays, availableDisplays)) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700491 mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
Jeff Brown89d55462012-09-19 11:33:42 -0700492 mAvailableDisplays = availableDisplays;
Jeff Brown74da1092012-11-07 16:02:13 -0800493 fixRememberedDisplayNamesFromAvailableDisplaysLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700494 scheduleStatusChangedBroadcastLocked();
495 }
496 }
497 }
498
499 @Override
500 public void onDisplayConnecting(WifiDisplay display) {
501 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700502 display = mPersistentDataStore.applyWifiDisplayAlias(display);
503
Jeff Brown180bbc72012-09-08 23:15:00 -0700504 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
505 || mActiveDisplay == null
506 || !mActiveDisplay.equals(display)) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700507 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
508 mActiveDisplay = display;
Jeff Browne08ae382012-09-07 20:36:36 -0700509 scheduleStatusChangedBroadcastLocked();
510 }
511 }
512 }
513
514 @Override
515 public void onDisplayConnectionFailed() {
516 synchronized (getSyncRoot()) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700517 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
518 || mActiveDisplay != null) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700519 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
520 mActiveDisplay = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700521 scheduleStatusChangedBroadcastLocked();
522 }
523 }
524 }
525
526 @Override
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700527 public void onDisplayConnected(WifiDisplay display, Surface surface,
528 int width, int height, int flags) {
Jeff Browne08ae382012-09-07 20:36:36 -0700529 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700530 display = mPersistentDataStore.applyWifiDisplayAlias(display);
Jeff Brown74da1092012-11-07 16:02:13 -0800531 addDisplayDeviceLocked(display, surface, width, height, flags);
Jeff Browne08ae382012-09-07 20:36:36 -0700532
Jeff Brown180bbc72012-09-08 23:15:00 -0700533 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
534 || mActiveDisplay == null
535 || !mActiveDisplay.equals(display)) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700536 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
537 mActiveDisplay = display;
Jeff Browne08ae382012-09-07 20:36:36 -0700538 scheduleStatusChangedBroadcastLocked();
539 }
Jeff Browncbad9762012-09-04 21:57:59 -0700540 }
541 }
542
543 @Override
Jeff Brown74da1092012-11-07 16:02:13 -0800544 public void onDisplayChanged(WifiDisplay display) {
545 synchronized (getSyncRoot()) {
546 display = mPersistentDataStore.applyWifiDisplayAlias(display);
547 if (mActiveDisplay != null
548 && mActiveDisplay.hasSameAddress(display)
549 && !mActiveDisplay.equals(display)) {
550 mActiveDisplay = display;
551 renameDisplayDeviceLocked(display.getFriendlyDisplayName());
552 scheduleStatusChangedBroadcastLocked();
553 }
554 }
555 }
556
557 @Override
Jeff Browncbad9762012-09-04 21:57:59 -0700558 public void onDisplayDisconnected() {
559 // Stop listening.
560 synchronized (getSyncRoot()) {
Jeff Brown74da1092012-11-07 16:02:13 -0800561 removeDisplayDeviceLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700562
Jeff Brown180bbc72012-09-08 23:15:00 -0700563 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
564 || mActiveDisplay != null) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700565 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
566 mActiveDisplay = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700567 scheduleStatusChangedBroadcastLocked();
568 }
Jeff Browncbad9762012-09-04 21:57:59 -0700569 }
570 }
571 };
572
573 private final class WifiDisplayDevice extends DisplayDevice {
Jeff Brownee4f0292012-10-15 15:31:59 -0700574 private String mName;
Jeff Browncbad9762012-09-04 21:57:59 -0700575 private final int mWidth;
576 private final int mHeight;
577 private final float mRefreshRate;
578 private final int mFlags;
Jeff Brown92130f62012-10-24 21:28:33 -0700579 private final String mAddress;
Jeff Browncbad9762012-09-04 21:57:59 -0700580
581 private Surface mSurface;
582 private DisplayDeviceInfo mInfo;
583
584 public WifiDisplayDevice(IBinder displayToken, String name,
Jeff Brown92130f62012-10-24 21:28:33 -0700585 int width, int height, float refreshRate, int flags, String address,
Jeff Browncbad9762012-09-04 21:57:59 -0700586 Surface surface) {
587 super(WifiDisplayAdapter.this, displayToken);
588 mName = name;
589 mWidth = width;
590 mHeight = height;
591 mRefreshRate = refreshRate;
592 mFlags = flags;
Jeff Brown92130f62012-10-24 21:28:33 -0700593 mAddress = address;
Jeff Browncbad9762012-09-04 21:57:59 -0700594 mSurface = surface;
595 }
596
597 public void clearSurfaceLocked() {
598 mSurface = null;
599 sendTraversalRequestLocked();
600 }
601
Jeff Brownee4f0292012-10-15 15:31:59 -0700602 public void setNameLocked(String name) {
603 mName = name;
604 mInfo = null;
605 }
606
Jeff Browncbad9762012-09-04 21:57:59 -0700607 @Override
608 public void performTraversalInTransactionLocked() {
609 setSurfaceInTransactionLocked(mSurface);
610 }
611
612 @Override
613 public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
614 if (mInfo == null) {
615 mInfo = new DisplayDeviceInfo();
616 mInfo.name = mName;
617 mInfo.width = mWidth;
618 mInfo.height = mHeight;
619 mInfo.refreshRate = mRefreshRate;
620 mInfo.flags = mFlags;
Jeff Brown92130f62012-10-24 21:28:33 -0700621 mInfo.type = Display.TYPE_WIFI;
622 mInfo.address = mAddress;
Jeff Brownd728bf52012-09-08 18:05:28 -0700623 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
Jeff Browncbad9762012-09-04 21:57:59 -0700624 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
625 }
626 return mInfo;
627 }
628 }
Jeff Browna7f9c962012-10-17 15:15:12 -0700629
630 private final class WifiDisplayHandler extends Handler {
631 public WifiDisplayHandler(Looper looper) {
632 super(looper, null, true /*async*/);
633 }
634
635 @Override
636 public void handleMessage(Message msg) {
637 switch (msg.what) {
638 case MSG_SEND_STATUS_CHANGE_BROADCAST:
639 handleSendStatusChangeBroadcast();
640 break;
641
642 case MSG_UPDATE_NOTIFICATION:
643 handleUpdateNotification();
644 break;
645 }
646 }
647 }
Jeff Browncbad9762012-09-04 21:57:59 -0700648}