blob: 4c80cf5c9fefcbcce26f7318a8c647809c436d80 [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;
Mathias Agopian3866f0d2013-02-11 22:08:48 -080044import android.view.SurfaceControl;
Jeff Browncbad9762012-09-04 21:57:59 -070045
46import java.io.PrintWriter;
Jeff Browne08ae382012-09-07 20:36:36 -070047import java.util.Arrays;
Chong Zhangab87a632013-06-11 10:25:49 -070048import java.util.List;
49import java.util.ArrayList;
Jeff Browncbad9762012-09-04 21:57:59 -070050
Jeff Brown74da1092012-11-07 16:02:13 -080051import libcore.util.Objects;
52
Jeff Browncbad9762012-09-04 21:57:59 -070053/**
54 * Connects to Wifi displays that implement the Miracast protocol.
55 * <p>
56 * The Wifi display protocol relies on Wifi direct for discovering and pairing
57 * with the display. Once connected, the Media Server opens an RTSP socket and accepts
58 * a connection from the display. After session negotiation, the Media Server
59 * streams encoded buffers to the display.
60 * </p><p>
61 * This class is responsible for connecting to Wifi displays and mediating
62 * the interactions between Media Server, Surface Flinger and the Display Manager Service.
63 * </p><p>
64 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
65 * </p>
66 */
67final class WifiDisplayAdapter extends DisplayAdapter {
68 private static final String TAG = "WifiDisplayAdapter";
69
Jeff Brown2444ae72012-10-11 14:30:21 -070070 private static final boolean DEBUG = false;
71
Jeff Browna7f9c962012-10-17 15:15:12 -070072 private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1;
73 private static final int MSG_UPDATE_NOTIFICATION = 2;
74
75 private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT";
76
77 private final WifiDisplayHandler mHandler;
Jeff Brown77aebfd2012-10-01 21:07:03 -070078 private final PersistentDataStore mPersistentDataStore;
79 private final boolean mSupportsProtectedBuffers;
Jeff Browna7f9c962012-10-17 15:15:12 -070080 private final NotificationManager mNotificationManager;
81
Jeff Brown66692502012-10-18 16:13:44 -070082 private PendingIntent mSettingsPendingIntent;
83 private PendingIntent mDisconnectPendingIntent;
Jeff Brown89d55462012-09-19 11:33:42 -070084
Jeff Browncbad9762012-09-04 21:57:59 -070085 private WifiDisplayController mDisplayController;
Jeff Brownf8f0edd2012-09-11 17:05:11 -070086 private WifiDisplayDevice mDisplayDevice;
Jeff Browncbad9762012-09-04 21:57:59 -070087
Jeff Browne08ae382012-09-07 20:36:36 -070088 private WifiDisplayStatus mCurrentStatus;
Jeff Brown89d55462012-09-19 11:33:42 -070089 private int mFeatureState;
Jeff Brown180bbc72012-09-08 23:15:00 -070090 private int mScanState;
91 private int mActiveDisplayState;
92 private WifiDisplay mActiveDisplay;
Chong Zhangab87a632013-06-11 10:25:49 -070093 private WifiDisplay[] mDisplays = WifiDisplay.EMPTY_ARRAY;
Jeff Brown89d55462012-09-19 11:33:42 -070094 private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY;
95 private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
Jeff Browne08ae382012-09-07 20:36:36 -070096
97 private boolean mPendingStatusChangeBroadcast;
Jeff Browna7f9c962012-10-17 15:15:12 -070098 private boolean mPendingNotificationUpdate;
Jeff Browne08ae382012-09-07 20:36:36 -070099
Jeff Brown66692502012-10-18 16:13:44 -0700100 // Called with SyncRoot lock held.
Jeff Browncbad9762012-09-04 21:57:59 -0700101 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Jeff Brown89d55462012-09-19 11:33:42 -0700102 Context context, Handler handler, Listener listener,
103 PersistentDataStore persistentDataStore) {
Jeff Browncbad9762012-09-04 21:57:59 -0700104 super(syncRoot, context, handler, listener, TAG);
Jeff Browna7f9c962012-10-17 15:15:12 -0700105 mHandler = new WifiDisplayHandler(handler.getLooper());
Jeff Brown89d55462012-09-19 11:33:42 -0700106 mPersistentDataStore = persistentDataStore;
Jeff Brown77aebfd2012-10-01 21:07:03 -0700107 mSupportsProtectedBuffers = context.getResources().getBoolean(
108 com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
Jeff Browna7f9c962012-10-17 15:15:12 -0700109 mNotificationManager = (NotificationManager)context.getSystemService(
110 Context.NOTIFICATION_SERVICE);
Jeff Browncbad9762012-09-04 21:57:59 -0700111 }
112
113 @Override
114 public void dumpLocked(PrintWriter pw) {
115 super.dumpLocked(pw);
116
Jeff Browne08ae382012-09-07 20:36:36 -0700117 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
Jeff Brown89d55462012-09-19 11:33:42 -0700118 pw.println("mFeatureState=" + mFeatureState);
Jeff Brown180bbc72012-09-08 23:15:00 -0700119 pw.println("mScanState=" + mScanState);
120 pw.println("mActiveDisplayState=" + mActiveDisplayState);
121 pw.println("mActiveDisplay=" + mActiveDisplay);
Chong Zhangab87a632013-06-11 10:25:49 -0700122 pw.println("mDisplays=" + Arrays.toString(mDisplays));
Jeff Brown89d55462012-09-19 11:33:42 -0700123 pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
124 pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
Jeff Browne08ae382012-09-07 20:36:36 -0700125 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
Jeff Browna7f9c962012-10-17 15:15:12 -0700126 pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
Jeff Brown77aebfd2012-10-01 21:07:03 -0700127 pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
Jeff Browne08ae382012-09-07 20:36:36 -0700128
Jeff Browncbad9762012-09-04 21:57:59 -0700129 // Try to dump the controller state.
130 if (mDisplayController == null) {
131 pw.println("mDisplayController=null");
132 } else {
133 pw.println("mDisplayController:");
134 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
135 ipw.increaseIndent();
136 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200);
137 }
138 }
139
140 @Override
141 public void registerLocked() {
142 super.registerLocked();
143
Jeff Brown89d55462012-09-19 11:33:42 -0700144 updateRememberedDisplaysLocked();
145
Jeff Browncbad9762012-09-04 21:57:59 -0700146 getHandler().post(new Runnable() {
147 @Override
148 public void run() {
149 mDisplayController = new WifiDisplayController(
150 getContext(), getHandler(), mWifiDisplayListener);
Jeff Brown66692502012-10-18 16:13:44 -0700151
152 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
153 new IntentFilter(ACTION_DISCONNECT), null, mHandler);
Jeff Browncbad9762012-09-04 21:57:59 -0700154 }
155 });
156 }
157
Jeff Browne08ae382012-09-07 20:36:36 -0700158 public void requestScanLocked() {
Jeff Brown2444ae72012-10-11 14:30:21 -0700159 if (DEBUG) {
160 Slog.d(TAG, "requestScanLocked");
161 }
162
Jeff Browne08ae382012-09-07 20:36:36 -0700163 getHandler().post(new Runnable() {
164 @Override
165 public void run() {
166 if (mDisplayController != null) {
167 mDisplayController.requestScan();
168 }
169 }
170 });
Jeff Browncbad9762012-09-04 21:57:59 -0700171 }
172
Jeff Brownbc335452012-09-26 18:34:47 -0700173 public void requestConnectLocked(final String address, final boolean trusted) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700174 if (DEBUG) {
175 Slog.d(TAG, "requestConnectLocked: address=" + address + ", trusted=" + trusted);
176 }
177
Jeff Brownbc335452012-09-26 18:34:47 -0700178 if (!trusted) {
179 synchronized (getSyncRoot()) {
180 if (!isRememberedDisplayLocked(address)) {
181 Slog.w(TAG, "Ignoring request by an untrusted client to connect to "
182 + "an unknown wifi display: " + address);
183 return;
184 }
185 }
186 }
187
Jeff Browne08ae382012-09-07 20:36:36 -0700188 getHandler().post(new Runnable() {
189 @Override
190 public void run() {
191 if (mDisplayController != null) {
192 mDisplayController.requestConnect(address);
193 }
194 }
195 });
196 }
197
Jeff Brownbc335452012-09-26 18:34:47 -0700198 private boolean isRememberedDisplayLocked(String address) {
199 for (WifiDisplay display : mRememberedDisplays) {
200 if (display.getDeviceAddress().equals(address)) {
201 return true;
202 }
203 }
204 return false;
205 }
206
Jeff Browne08ae382012-09-07 20:36:36 -0700207 public void requestDisconnectLocked() {
Jeff Brown2444ae72012-10-11 14:30:21 -0700208 if (DEBUG) {
209 Slog.d(TAG, "requestDisconnectedLocked");
210 }
211
Jeff Browne08ae382012-09-07 20:36:36 -0700212 getHandler().post(new Runnable() {
213 @Override
214 public void run() {
215 if (mDisplayController != null) {
216 mDisplayController.requestDisconnect();
217 }
218 }
219 });
220 }
221
Jeff Brown89d55462012-09-19 11:33:42 -0700222 public void requestRenameLocked(String address, String alias) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700223 if (DEBUG) {
224 Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias);
225 }
226
Jeff Brown89d55462012-09-19 11:33:42 -0700227 if (alias != null) {
228 alias = alias.trim();
Jeff Brown2444ae72012-10-11 14:30:21 -0700229 if (alias.isEmpty() || alias.equals(address)) {
Jeff Brown89d55462012-09-19 11:33:42 -0700230 alias = null;
231 }
232 }
233
Jeff Brown74da1092012-11-07 16:02:13 -0800234 WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address);
235 if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) {
Chong Zhangab87a632013-06-11 10:25:49 -0700236 display = new WifiDisplay(address, display.getDeviceName(), alias,
237 false, false, false);
Jeff Brown74da1092012-11-07 16:02:13 -0800238 if (mPersistentDataStore.rememberWifiDisplay(display)) {
239 mPersistentDataStore.saveIfNeeded();
240 updateRememberedDisplaysLocked();
241 scheduleStatusChangedBroadcastLocked();
242 }
Jeff Brown89d55462012-09-19 11:33:42 -0700243 }
Jeff Brownee4f0292012-10-15 15:31:59 -0700244
Jeff Brown74da1092012-11-07 16:02:13 -0800245 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
246 renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName());
Jeff Brownee4f0292012-10-15 15:31:59 -0700247 }
Jeff Brown89d55462012-09-19 11:33:42 -0700248 }
249
250 public void requestForgetLocked(String address) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700251 if (DEBUG) {
252 Slog.d(TAG, "requestForgetLocked: address=" + address);
253 }
254
Jeff Brown89d55462012-09-19 11:33:42 -0700255 if (mPersistentDataStore.forgetWifiDisplay(address)) {
256 mPersistentDataStore.saveIfNeeded();
257 updateRememberedDisplaysLocked();
258 scheduleStatusChangedBroadcastLocked();
259 }
260
261 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
262 requestDisconnectLocked();
263 }
264 }
265
Jeff Browne08ae382012-09-07 20:36:36 -0700266 public WifiDisplayStatus getWifiDisplayStatusLocked() {
267 if (mCurrentStatus == null) {
Jeff Brown89d55462012-09-19 11:33:42 -0700268 mCurrentStatus = new WifiDisplayStatus(
269 mFeatureState, mScanState, mActiveDisplayState,
Chong Zhangab87a632013-06-11 10:25:49 -0700270 mActiveDisplay, mDisplays);
Jeff Browne08ae382012-09-07 20:36:36 -0700271 }
Jeff Brown2444ae72012-10-11 14:30:21 -0700272
273 if (DEBUG) {
274 Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus);
275 }
Jeff Browne08ae382012-09-07 20:36:36 -0700276 return mCurrentStatus;
277 }
278
Chong Zhangab87a632013-06-11 10:25:49 -0700279 private void updateDisplaysLocked() {
280 List<WifiDisplay> displays = new ArrayList<WifiDisplay>(
281 mAvailableDisplays.length + mRememberedDisplays.length);
282 boolean[] remembered = new boolean[mAvailableDisplays.length];
283 for (WifiDisplay d : mRememberedDisplays) {
284 boolean available = false;
285 for (int i = 0; i < mAvailableDisplays.length; i++) {
286 if (d.equals(mAvailableDisplays[i])) {
287 remembered[i] = available = true;
288 break;
289 }
290 }
291 if (!available) {
292 displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
293 d.getDeviceAlias(), false, false, true));
294 }
295 }
296 for (int i = 0; i < mAvailableDisplays.length; i++) {
297 WifiDisplay d = mAvailableDisplays[i];
298 displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(),
299 d.getDeviceAlias(), true, d.canConnect(), remembered[i]));
300 }
301 mDisplays = displays.toArray(WifiDisplay.EMPTY_ARRAY);
302 }
303
Jeff Brown89d55462012-09-19 11:33:42 -0700304 private void updateRememberedDisplaysLocked() {
305 mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays();
306 mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay);
307 mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
Chong Zhangab87a632013-06-11 10:25:49 -0700308 updateDisplaysLocked();
Jeff Brown89d55462012-09-19 11:33:42 -0700309 }
310
Jeff Brown74da1092012-11-07 16:02:13 -0800311 private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() {
312 // It may happen that a display name has changed since it was remembered.
313 // Consult the list of available displays and update the name if needed.
314 // We don't do anything special for the active display here. The display
315 // controller will send a separate event when it needs to be updates.
316 boolean changed = false;
317 for (int i = 0; i < mRememberedDisplays.length; i++) {
318 WifiDisplay rememberedDisplay = mRememberedDisplays[i];
319 WifiDisplay availableDisplay = findAvailableDisplayLocked(
320 rememberedDisplay.getDeviceAddress());
321 if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) {
322 if (DEBUG) {
323 Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: "
324 + "updating remembered display to " + availableDisplay);
325 }
326 mRememberedDisplays[i] = availableDisplay;
327 changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay);
328 }
329 }
330 if (changed) {
331 mPersistentDataStore.saveIfNeeded();
332 }
333 }
334
335 private WifiDisplay findAvailableDisplayLocked(String address) {
336 for (WifiDisplay display : mAvailableDisplays) {
337 if (display.getDeviceAddress().equals(address)) {
338 return display;
339 }
340 }
341 return null;
342 }
343
344 private void addDisplayDeviceLocked(WifiDisplay display,
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700345 Surface surface, int width, int height, int flags) {
Jeff Brown74da1092012-11-07 16:02:13 -0800346 removeDisplayDeviceLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700347
Jeff Brown89d55462012-09-19 11:33:42 -0700348 if (mPersistentDataStore.rememberWifiDisplay(display)) {
349 mPersistentDataStore.saveIfNeeded();
350 updateRememberedDisplaysLocked();
351 scheduleStatusChangedBroadcastLocked();
352 }
353
Jeff Brownf0681b32012-10-23 17:35:57 -0700354 boolean secure = (flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0;
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700355 int deviceFlags = 0;
Jeff Brownf0681b32012-10-23 17:35:57 -0700356 if (secure) {
Jeff Brown77aebfd2012-10-01 21:07:03 -0700357 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
Jeff Brownf0681b32012-10-23 17:35:57 -0700358 if (mSupportsProtectedBuffers) {
359 deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
360 }
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700361 }
362
363 float refreshRate = 60.0f; // TODO: get this for real
364
Jeff Brown89d55462012-09-19 11:33:42 -0700365 String name = display.getFriendlyDisplayName();
Jeff Brown92130f62012-10-24 21:28:33 -0700366 String address = display.getDeviceAddress();
Mathias Agopian3866f0d2013-02-11 22:08:48 -0800367 IBinder displayToken = SurfaceControl.createDisplay(name, secure);
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700368 mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
Jeff Brown92130f62012-10-24 21:28:33 -0700369 refreshRate, deviceFlags, address, surface);
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700370 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
Jeff Browna7f9c962012-10-17 15:15:12 -0700371
372 scheduleUpdateNotificationLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700373 }
374
Jeff Brown74da1092012-11-07 16:02:13 -0800375 private void removeDisplayDeviceLocked() {
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700376 if (mDisplayDevice != null) {
377 mDisplayDevice.clearSurfaceLocked();
378 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
379 mDisplayDevice = null;
Jeff Browna7f9c962012-10-17 15:15:12 -0700380
381 scheduleUpdateNotificationLocked();
Jeff Browncbad9762012-09-04 21:57:59 -0700382 }
383 }
384
Jeff Brown74da1092012-11-07 16:02:13 -0800385 private void renameDisplayDeviceLocked(String name) {
386 if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) {
387 mDisplayDevice.setNameLocked(name);
388 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
389 }
390 }
391
Jeff Browne08ae382012-09-07 20:36:36 -0700392 private void scheduleStatusChangedBroadcastLocked() {
Jeff Brown89d55462012-09-19 11:33:42 -0700393 mCurrentStatus = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700394 if (!mPendingStatusChangeBroadcast) {
395 mPendingStatusChangeBroadcast = true;
Jeff Browna7f9c962012-10-17 15:15:12 -0700396 mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);
Jeff Browne08ae382012-09-07 20:36:36 -0700397 }
398 }
399
Jeff Browna7f9c962012-10-17 15:15:12 -0700400 private void scheduleUpdateNotificationLocked() {
401 if (!mPendingNotificationUpdate) {
402 mPendingNotificationUpdate = true;
403 mHandler.sendEmptyMessage(MSG_UPDATE_NOTIFICATION);
404 }
405 }
Jeff Browne08ae382012-09-07 20:36:36 -0700406
Jeff Browna7f9c962012-10-17 15:15:12 -0700407 // Runs on the handler.
408 private void handleSendStatusChangeBroadcast() {
409 final Intent intent;
410 synchronized (getSyncRoot()) {
411 if (!mPendingStatusChangeBroadcast) {
412 return;
Jeff Browne08ae382012-09-07 20:36:36 -0700413 }
414
Jeff Browna7f9c962012-10-17 15:15:12 -0700415 mPendingStatusChangeBroadcast = false;
416 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
417 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
418 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
419 getWifiDisplayStatusLocked());
420 }
421
422 // Send protected broadcast about wifi display status to registered receivers.
423 getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
424 }
425
426 // Runs on the handler.
427 private void handleUpdateNotification() {
428 final boolean isConnected;
429 synchronized (getSyncRoot()) {
430 if (!mPendingNotificationUpdate) {
431 return;
432 }
433
434 mPendingNotificationUpdate = false;
435 isConnected = (mDisplayDevice != null);
436 }
437
Jeff Brown66692502012-10-18 16:13:44 -0700438 // Cancel the old notification if there is one.
Jeff Browna7f9c962012-10-17 15:15:12 -0700439 mNotificationManager.cancelAsUser(null,
440 R.string.wifi_display_notification_title, UserHandle.ALL);
441
442 if (isConnected) {
443 Context context = getContext();
444
Jeff Brown66692502012-10-18 16:13:44 -0700445 // Initialize pending intents for the notification outside of the lock because
446 // creating a pending intent requires a call into the activity manager.
447 if (mSettingsPendingIntent == null) {
448 Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
449 settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
450 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
451 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
452 mSettingsPendingIntent = PendingIntent.getActivityAsUser(
453 context, 0, settingsIntent, 0, null, UserHandle.CURRENT);
454 }
455
456 if (mDisconnectPendingIntent == null) {
457 Intent disconnectIntent = new Intent(ACTION_DISCONNECT);
458 mDisconnectPendingIntent = PendingIntent.getBroadcastAsUser(
459 context, 0, disconnectIntent, 0, UserHandle.CURRENT);
460 }
461
462 // Post the notification.
Jeff Browna7f9c962012-10-17 15:15:12 -0700463 Resources r = context.getResources();
464 Notification notification = new Notification.Builder(context)
465 .setContentTitle(r.getString(
466 R.string.wifi_display_notification_title))
467 .setContentText(r.getString(
468 R.string.wifi_display_notification_message))
469 .setContentIntent(mSettingsPendingIntent)
470 .setSmallIcon(R.drawable.ic_notify_wifidisplay)
471 .setOngoing(true)
472 .addAction(android.R.drawable.ic_menu_close_clear_cancel,
473 r.getString(R.string.wifi_display_notification_disconnect),
474 mDisconnectPendingIntent)
475 .build();
476 mNotificationManager.notifyAsUser(null,
477 R.string.wifi_display_notification_title,
478 notification, UserHandle.ALL);
479 }
480 }
481
482 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
483 @Override
484 public void onReceive(Context context, Intent intent) {
485 if (intent.getAction().equals(ACTION_DISCONNECT)) {
486 synchronized (getSyncRoot()) {
487 requestDisconnectLocked();
488 }
489 }
Jeff Browne08ae382012-09-07 20:36:36 -0700490 }
491 };
492
Jeff Browncbad9762012-09-04 21:57:59 -0700493 private final WifiDisplayController.Listener mWifiDisplayListener =
494 new WifiDisplayController.Listener() {
495 @Override
Jeff Brown89d55462012-09-19 11:33:42 -0700496 public void onFeatureStateChanged(int featureState) {
Jeff Browncbad9762012-09-04 21:57:59 -0700497 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700498 if (mFeatureState != featureState) {
499 mFeatureState = featureState;
Jeff Browne08ae382012-09-07 20:36:36 -0700500 scheduleStatusChangedBroadcastLocked();
501 }
502 }
503 }
504
505 @Override
506 public void onScanStarted() {
507 synchronized (getSyncRoot()) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700508 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700509 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
Jeff Browne08ae382012-09-07 20:36:36 -0700510 scheduleStatusChangedBroadcastLocked();
511 }
512 }
513 }
514
Jeff Brown2444ae72012-10-11 14:30:21 -0700515 @Override
Jeff Brown89d55462012-09-19 11:33:42 -0700516 public void onScanFinished(WifiDisplay[] availableDisplays) {
Jeff Browne08ae382012-09-07 20:36:36 -0700517 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700518 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
519 availableDisplays);
520
Chong Zhangab87a632013-06-11 10:25:49 -0700521 // check if any of the available displays changed canConnect status
522 boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);
523 for (int i = 0; !changed && i<availableDisplays.length; i++) {
524 changed = availableDisplays[i].canConnect()
525 != mAvailableDisplays[i].canConnect();
526 }
527
528 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING || changed) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700529 mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
Jeff Brown89d55462012-09-19 11:33:42 -0700530 mAvailableDisplays = availableDisplays;
Jeff Brown74da1092012-11-07 16:02:13 -0800531 fixRememberedDisplayNamesFromAvailableDisplaysLocked();
Chong Zhangab87a632013-06-11 10:25:49 -0700532 updateDisplaysLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700533 scheduleStatusChangedBroadcastLocked();
534 }
535 }
536 }
537
538 @Override
539 public void onDisplayConnecting(WifiDisplay display) {
540 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700541 display = mPersistentDataStore.applyWifiDisplayAlias(display);
542
Jeff Brown180bbc72012-09-08 23:15:00 -0700543 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
544 || mActiveDisplay == null
545 || !mActiveDisplay.equals(display)) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700546 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
547 mActiveDisplay = display;
Jeff Browne08ae382012-09-07 20:36:36 -0700548 scheduleStatusChangedBroadcastLocked();
549 }
550 }
551 }
552
553 @Override
554 public void onDisplayConnectionFailed() {
555 synchronized (getSyncRoot()) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700556 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
557 || mActiveDisplay != null) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700558 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
559 mActiveDisplay = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700560 scheduleStatusChangedBroadcastLocked();
561 }
562 }
563 }
564
565 @Override
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700566 public void onDisplayConnected(WifiDisplay display, Surface surface,
567 int width, int height, int flags) {
Jeff Browne08ae382012-09-07 20:36:36 -0700568 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700569 display = mPersistentDataStore.applyWifiDisplayAlias(display);
Jeff Brown74da1092012-11-07 16:02:13 -0800570 addDisplayDeviceLocked(display, surface, width, height, flags);
Jeff Browne08ae382012-09-07 20:36:36 -0700571
Jeff Brown180bbc72012-09-08 23:15:00 -0700572 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
573 || mActiveDisplay == null
574 || !mActiveDisplay.equals(display)) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700575 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
576 mActiveDisplay = display;
Jeff Browne08ae382012-09-07 20:36:36 -0700577 scheduleStatusChangedBroadcastLocked();
578 }
Jeff Browncbad9762012-09-04 21:57:59 -0700579 }
580 }
581
582 @Override
Jeff Brown74da1092012-11-07 16:02:13 -0800583 public void onDisplayChanged(WifiDisplay display) {
584 synchronized (getSyncRoot()) {
585 display = mPersistentDataStore.applyWifiDisplayAlias(display);
586 if (mActiveDisplay != null
587 && mActiveDisplay.hasSameAddress(display)
588 && !mActiveDisplay.equals(display)) {
589 mActiveDisplay = display;
590 renameDisplayDeviceLocked(display.getFriendlyDisplayName());
591 scheduleStatusChangedBroadcastLocked();
592 }
593 }
594 }
595
596 @Override
Jeff Browncbad9762012-09-04 21:57:59 -0700597 public void onDisplayDisconnected() {
598 // Stop listening.
599 synchronized (getSyncRoot()) {
Jeff Brown74da1092012-11-07 16:02:13 -0800600 removeDisplayDeviceLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700601
Jeff Brown180bbc72012-09-08 23:15:00 -0700602 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
603 || mActiveDisplay != null) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700604 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
605 mActiveDisplay = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700606 scheduleStatusChangedBroadcastLocked();
607 }
Jeff Browncbad9762012-09-04 21:57:59 -0700608 }
609 }
610 };
611
612 private final class WifiDisplayDevice extends DisplayDevice {
Jeff Brownee4f0292012-10-15 15:31:59 -0700613 private String mName;
Jeff Browncbad9762012-09-04 21:57:59 -0700614 private final int mWidth;
615 private final int mHeight;
616 private final float mRefreshRate;
617 private final int mFlags;
Jeff Brown92130f62012-10-24 21:28:33 -0700618 private final String mAddress;
Jeff Browncbad9762012-09-04 21:57:59 -0700619
620 private Surface mSurface;
621 private DisplayDeviceInfo mInfo;
622
623 public WifiDisplayDevice(IBinder displayToken, String name,
Jeff Brown92130f62012-10-24 21:28:33 -0700624 int width, int height, float refreshRate, int flags, String address,
Jeff Browncbad9762012-09-04 21:57:59 -0700625 Surface surface) {
626 super(WifiDisplayAdapter.this, displayToken);
627 mName = name;
628 mWidth = width;
629 mHeight = height;
630 mRefreshRate = refreshRate;
631 mFlags = flags;
Jeff Brown92130f62012-10-24 21:28:33 -0700632 mAddress = address;
Jeff Browncbad9762012-09-04 21:57:59 -0700633 mSurface = surface;
634 }
635
636 public void clearSurfaceLocked() {
637 mSurface = null;
638 sendTraversalRequestLocked();
639 }
640
Jeff Brownee4f0292012-10-15 15:31:59 -0700641 public void setNameLocked(String name) {
642 mName = name;
643 mInfo = null;
644 }
645
Jeff Browncbad9762012-09-04 21:57:59 -0700646 @Override
647 public void performTraversalInTransactionLocked() {
648 setSurfaceInTransactionLocked(mSurface);
649 }
650
651 @Override
652 public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
653 if (mInfo == null) {
654 mInfo = new DisplayDeviceInfo();
655 mInfo.name = mName;
656 mInfo.width = mWidth;
657 mInfo.height = mHeight;
658 mInfo.refreshRate = mRefreshRate;
659 mInfo.flags = mFlags;
Jeff Brown92130f62012-10-24 21:28:33 -0700660 mInfo.type = Display.TYPE_WIFI;
661 mInfo.address = mAddress;
Jeff Brownd728bf52012-09-08 18:05:28 -0700662 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
Jeff Browncbad9762012-09-04 21:57:59 -0700663 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
664 }
665 return mInfo;
666 }
667 }
Jeff Browna7f9c962012-10-17 15:15:12 -0700668
669 private final class WifiDisplayHandler extends Handler {
670 public WifiDisplayHandler(Looper looper) {
671 super(looper, null, true /*async*/);
672 }
673
674 @Override
675 public void handleMessage(Message msg) {
676 switch (msg.what) {
677 case MSG_SEND_STATUS_CHANGE_BROADCAST:
678 handleSendStatusChangeBroadcast();
679 break;
680
681 case MSG_UPDATE_NOTIFICATION:
682 handleUpdateNotification();
683 break;
684 }
685 }
686 }
Jeff Browncbad9762012-09-04 21:57:59 -0700687}