blob: cd57941bcc3edd795be5101d4808c963626de2b8 [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;
Chong Zhang1f3ecaa2013-05-03 15:55:36 -070033import android.hardware.display.WifiDisplaySessionInfo;
Jeff Browne08ae382012-09-07 20:36:36 -070034import android.hardware.display.WifiDisplayStatus;
Jeff Browncbad9762012-09-04 21:57:59 -070035import android.media.RemoteDisplay;
36import android.os.Handler;
37import android.os.IBinder;
Jeff Browna7f9c962012-10-17 15:15:12 -070038import android.os.Looper;
39import android.os.Message;
40import android.os.UserHandle;
41import android.provider.Settings;
Jeff Brownbc335452012-09-26 18:34:47 -070042import android.util.Slog;
Jeff Brown92130f62012-10-24 21:28:33 -070043import android.view.Display;
Jeff Browncbad9762012-09-04 21:57:59 -070044import android.view.Surface;
Mathias Agopian3866f0d2013-02-11 22:08:48 -080045import android.view.SurfaceControl;
Jeff Browncbad9762012-09-04 21:57:59 -070046
47import java.io.PrintWriter;
Jeff Browne08ae382012-09-07 20:36:36 -070048import java.util.Arrays;
Chong Zhangab87a632013-06-11 10:25:49 -070049import java.util.List;
50import java.util.ArrayList;
Jeff Browncbad9762012-09-04 21:57:59 -070051
Jeff Brown74da1092012-11-07 16:02:13 -080052import libcore.util.Objects;
53
Jeff Browncbad9762012-09-04 21:57:59 -070054/**
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 */
68final class WifiDisplayAdapter extends DisplayAdapter {
69 private static final String TAG = "WifiDisplayAdapter";
70
Jeff Brown2444ae72012-10-11 14:30:21 -070071 private static final boolean DEBUG = false;
72
Jeff Browna7f9c962012-10-17 15:15:12 -070073 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;
Jeff Brown77aebfd2012-10-01 21:07:03 -070079 private final PersistentDataStore mPersistentDataStore;
80 private final boolean mSupportsProtectedBuffers;
Jeff Browna7f9c962012-10-17 15:15:12 -070081 private final NotificationManager mNotificationManager;
82
Jeff Brown66692502012-10-18 16:13:44 -070083 private PendingIntent mSettingsPendingIntent;
84 private PendingIntent mDisconnectPendingIntent;
Jeff Brown89d55462012-09-19 11:33:42 -070085
Jeff Browncbad9762012-09-04 21:57:59 -070086 private WifiDisplayController mDisplayController;
Jeff Brownf8f0edd2012-09-11 17:05:11 -070087 private WifiDisplayDevice mDisplayDevice;
Jeff Browncbad9762012-09-04 21:57:59 -070088
Jeff Browne08ae382012-09-07 20:36:36 -070089 private WifiDisplayStatus mCurrentStatus;
Jeff Brown89d55462012-09-19 11:33:42 -070090 private int mFeatureState;
Jeff Brown180bbc72012-09-08 23:15:00 -070091 private int mScanState;
92 private int mActiveDisplayState;
93 private WifiDisplay mActiveDisplay;
Chong Zhangab87a632013-06-11 10:25:49 -070094 private WifiDisplay[] mDisplays = WifiDisplay.EMPTY_ARRAY;
Jeff Brown89d55462012-09-19 11:33:42 -070095 private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY;
96 private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
Chong Zhang1f3ecaa2013-05-03 15:55:36 -070097 private WifiDisplaySessionInfo mSessionInfo;
Jeff Browne08ae382012-09-07 20:36:36 -070098
99 private boolean mPendingStatusChangeBroadcast;
Jeff Browna7f9c962012-10-17 15:15:12 -0700100 private boolean mPendingNotificationUpdate;
Jeff Browne08ae382012-09-07 20:36:36 -0700101
Jeff Brown66692502012-10-18 16:13:44 -0700102 // Called with SyncRoot lock held.
Jeff Browncbad9762012-09-04 21:57:59 -0700103 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Jeff Brown89d55462012-09-19 11:33:42 -0700104 Context context, Handler handler, Listener listener,
105 PersistentDataStore persistentDataStore) {
Jeff Browncbad9762012-09-04 21:57:59 -0700106 super(syncRoot, context, handler, listener, TAG);
Jeff Browna7f9c962012-10-17 15:15:12 -0700107 mHandler = new WifiDisplayHandler(handler.getLooper());
Jeff Brown89d55462012-09-19 11:33:42 -0700108 mPersistentDataStore = persistentDataStore;
Jeff Brown77aebfd2012-10-01 21:07:03 -0700109 mSupportsProtectedBuffers = context.getResources().getBoolean(
110 com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
Jeff Browna7f9c962012-10-17 15:15:12 -0700111 mNotificationManager = (NotificationManager)context.getSystemService(
112 Context.NOTIFICATION_SERVICE);
Jeff Browncbad9762012-09-04 21:57:59 -0700113 }
114
115 @Override
116 public void dumpLocked(PrintWriter pw) {
117 super.dumpLocked(pw);
118
Jeff Browne08ae382012-09-07 20:36:36 -0700119 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
Jeff Brown89d55462012-09-19 11:33:42 -0700120 pw.println("mFeatureState=" + mFeatureState);
Jeff Brown180bbc72012-09-08 23:15:00 -0700121 pw.println("mScanState=" + mScanState);
122 pw.println("mActiveDisplayState=" + mActiveDisplayState);
123 pw.println("mActiveDisplay=" + mActiveDisplay);
Chong Zhangab87a632013-06-11 10:25:49 -0700124 pw.println("mDisplays=" + Arrays.toString(mDisplays));
Jeff Brown89d55462012-09-19 11:33:42 -0700125 pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
126 pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
Jeff Browne08ae382012-09-07 20:36:36 -0700127 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
Jeff Browna7f9c962012-10-17 15:15:12 -0700128 pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
Jeff Brown77aebfd2012-10-01 21:07:03 -0700129 pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
Jeff Brownce468a32013-11-21 16:42:03 -0800130
Jeff Browncbad9762012-09-04 21:57:59 -0700131 // 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
Jeff Brown89d55462012-09-19 11:33:42 -0700146 updateRememberedDisplaysLocked();
147
Jeff Browncbad9762012-09-04 21:57:59 -0700148 getHandler().post(new Runnable() {
149 @Override
150 public void run() {
151 mDisplayController = new WifiDisplayController(
152 getContext(), getHandler(), mWifiDisplayListener);
Jeff Brown66692502012-10-18 16:13:44 -0700153
154 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
155 new IntentFilter(ACTION_DISCONNECT), null, mHandler);
Jeff Browncbad9762012-09-04 21:57:59 -0700156 }
157 });
158 }
159
Jeff Brownce468a32013-11-21 16:42:03 -0800160 public void requestStartScanLocked() {
Jeff Brown2444ae72012-10-11 14:30:21 -0700161 if (DEBUG) {
Jeff Brownce468a32013-11-21 16:42:03 -0800162 Slog.d(TAG, "requestStartScanLocked");
Jeff Brown2444ae72012-10-11 14:30:21 -0700163 }
164
Jeff Browne08ae382012-09-07 20:36:36 -0700165 getHandler().post(new Runnable() {
166 @Override
167 public void run() {
168 if (mDisplayController != null) {
Jeff Brownce468a32013-11-21 16:42:03 -0800169 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();
Jeff Browne08ae382012-09-07 20:36:36 -0700185 }
186 }
187 });
Jeff Browncbad9762012-09-04 21:57:59 -0700188 }
189
Jeff Brownaf574182013-11-14 18:16:08 -0800190 public void requestConnectLocked(final String address) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700191 if (DEBUG) {
Jeff Brownaf574182013-11-14 18:16:08 -0800192 Slog.d(TAG, "requestConnectLocked: address=" + address);
Jeff Brownbc335452012-09-26 18:34:47 -0700193 }
194
Jeff Browne08ae382012-09-07 20:36:36 -0700195 getHandler().post(new Runnable() {
196 @Override
197 public void run() {
198 if (mDisplayController != null) {
199 mDisplayController.requestConnect(address);
200 }
201 }
202 });
203 }
204
Chong Zhang1f3ecaa2013-05-03 15:55:36 -0700205 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
Jeff Browne08ae382012-09-07 20:36:36 -0700235 public void requestDisconnectLocked() {
Jeff Brown2444ae72012-10-11 14:30:21 -0700236 if (DEBUG) {
237 Slog.d(TAG, "requestDisconnectedLocked");
238 }
239
Jeff Browne08ae382012-09-07 20:36:36 -0700240 getHandler().post(new Runnable() {
241 @Override
242 public void run() {
243 if (mDisplayController != null) {
244 mDisplayController.requestDisconnect();
245 }
246 }
247 });
248 }
249
Jeff Brown89d55462012-09-19 11:33:42 -0700250 public void requestRenameLocked(String address, String alias) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700251 if (DEBUG) {
252 Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias);
253 }
254
Jeff Brown89d55462012-09-19 11:33:42 -0700255 if (alias != null) {
256 alias = alias.trim();
Jeff Brown2444ae72012-10-11 14:30:21 -0700257 if (alias.isEmpty() || alias.equals(address)) {
Jeff Brown89d55462012-09-19 11:33:42 -0700258 alias = null;
259 }
260 }
261
Jeff Brown74da1092012-11-07 16:02:13 -0800262 WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address);
263 if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) {
Chong Zhangab87a632013-06-11 10:25:49 -0700264 display = new WifiDisplay(address, display.getDeviceName(), alias,
265 false, false, false);
Jeff Brown74da1092012-11-07 16:02:13 -0800266 if (mPersistentDataStore.rememberWifiDisplay(display)) {
267 mPersistentDataStore.saveIfNeeded();
268 updateRememberedDisplaysLocked();
269 scheduleStatusChangedBroadcastLocked();
270 }
Jeff Brown89d55462012-09-19 11:33:42 -0700271 }
Jeff Brownee4f0292012-10-15 15:31:59 -0700272
Jeff Brown74da1092012-11-07 16:02:13 -0800273 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
274 renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName());
Jeff Brownee4f0292012-10-15 15:31:59 -0700275 }
Jeff Brown89d55462012-09-19 11:33:42 -0700276 }
277
278 public void requestForgetLocked(String address) {
Jeff Brown2444ae72012-10-11 14:30:21 -0700279 if (DEBUG) {
280 Slog.d(TAG, "requestForgetLocked: address=" + address);
281 }
282
Jeff Brown89d55462012-09-19 11:33:42 -0700283 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
Jeff Browne08ae382012-09-07 20:36:36 -0700294 public WifiDisplayStatus getWifiDisplayStatusLocked() {
295 if (mCurrentStatus == null) {
Jeff Brown89d55462012-09-19 11:33:42 -0700296 mCurrentStatus = new WifiDisplayStatus(
297 mFeatureState, mScanState, mActiveDisplayState,
Chong Zhang1f3ecaa2013-05-03 15:55:36 -0700298 mActiveDisplay, mDisplays, mSessionInfo);
Jeff Browne08ae382012-09-07 20:36:36 -0700299 }
Jeff Brown2444ae72012-10-11 14:30:21 -0700300
301 if (DEBUG) {
302 Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus);
303 }
Jeff Browne08ae382012-09-07 20:36:36 -0700304 return mCurrentStatus;
305 }
306
Chong Zhangab87a632013-06-11 10:25:49 -0700307 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
Jeff Brown89d55462012-09-19 11:33:42 -0700332 private void updateRememberedDisplaysLocked() {
333 mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays();
334 mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay);
335 mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
Chong Zhangab87a632013-06-11 10:25:49 -0700336 updateDisplaysLocked();
Jeff Brown89d55462012-09-19 11:33:42 -0700337 }
338
Jeff Brown74da1092012-11-07 16:02:13 -0800339 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,
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700373 Surface surface, int width, int height, int flags) {
Jeff Brown74da1092012-11-07 16:02:13 -0800374 removeDisplayDeviceLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700375
Jeff Brown89d55462012-09-19 11:33:42 -0700376 if (mPersistentDataStore.rememberWifiDisplay(display)) {
377 mPersistentDataStore.saveIfNeeded();
378 updateRememberedDisplaysLocked();
379 scheduleStatusChangedBroadcastLocked();
380 }
381
Jeff Brownf0681b32012-10-23 17:35:57 -0700382 boolean secure = (flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0;
Jeff Brown7d00aff2013-08-02 19:03:49 -0700383 int deviceFlags = DisplayDeviceInfo.FLAG_PRESENTATION;
Jeff Brownf0681b32012-10-23 17:35:57 -0700384 if (secure) {
Jeff Brown77aebfd2012-10-01 21:07:03 -0700385 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
Jeff Brownf0681b32012-10-23 17:35:57 -0700386 if (mSupportsProtectedBuffers) {
387 deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
388 }
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700389 }
390
391 float refreshRate = 60.0f; // TODO: get this for real
392
Jeff Brown89d55462012-09-19 11:33:42 -0700393 String name = display.getFriendlyDisplayName();
Jeff Brown92130f62012-10-24 21:28:33 -0700394 String address = display.getDeviceAddress();
Mathias Agopian3866f0d2013-02-11 22:08:48 -0800395 IBinder displayToken = SurfaceControl.createDisplay(name, secure);
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700396 mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
Jeff Brown92130f62012-10-24 21:28:33 -0700397 refreshRate, deviceFlags, address, surface);
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700398 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
Jeff Browne08ae382012-09-07 20:36:36 -0700399 }
400
Jeff Brown74da1092012-11-07 16:02:13 -0800401 private void removeDisplayDeviceLocked() {
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700402 if (mDisplayDevice != null) {
Jesse Hall6a6bc212013-08-08 12:15:03 -0700403 mDisplayDevice.destroyLocked();
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700404 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
405 mDisplayDevice = null;
Jeff Browncbad9762012-09-04 21:57:59 -0700406 }
407 }
408
Jeff Brown74da1092012-11-07 16:02:13 -0800409 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
Jeff Browne08ae382012-09-07 20:36:36 -0700416 private void scheduleStatusChangedBroadcastLocked() {
Jeff Brown89d55462012-09-19 11:33:42 -0700417 mCurrentStatus = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700418 if (!mPendingStatusChangeBroadcast) {
419 mPendingStatusChangeBroadcast = true;
Jeff Browna7f9c962012-10-17 15:15:12 -0700420 mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);
Jeff Browne08ae382012-09-07 20:36:36 -0700421 }
422 }
423
Jeff Browna7f9c962012-10-17 15:15:12 -0700424 private void scheduleUpdateNotificationLocked() {
425 if (!mPendingNotificationUpdate) {
426 mPendingNotificationUpdate = true;
427 mHandler.sendEmptyMessage(MSG_UPDATE_NOTIFICATION);
428 }
429 }
Jeff Browne08ae382012-09-07 20:36:36 -0700430
Jeff Browna7f9c962012-10-17 15:15:12 -0700431 // Runs on the handler.
432 private void handleSendStatusChangeBroadcast() {
433 final Intent intent;
434 synchronized (getSyncRoot()) {
435 if (!mPendingStatusChangeBroadcast) {
436 return;
Jeff Browne08ae382012-09-07 20:36:36 -0700437 }
438
Jeff Browna7f9c962012-10-17 15:15:12 -0700439 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() {
Jeff Browne7ae6442013-11-13 00:45:18 -0800452 final int state;
453 final WifiDisplay display;
Jeff Browna7f9c962012-10-17 15:15:12 -0700454 synchronized (getSyncRoot()) {
455 if (!mPendingNotificationUpdate) {
456 return;
457 }
458
459 mPendingNotificationUpdate = false;
Jeff Browne7ae6442013-11-13 00:45:18 -0800460 state = mActiveDisplayState;
461 display = mActiveDisplay;
Jeff Browna7f9c962012-10-17 15:15:12 -0700462 }
463
Jeff Brown66692502012-10-18 16:13:44 -0700464 // Cancel the old notification if there is one.
Jeff Browna7f9c962012-10-17 15:15:12 -0700465 mNotificationManager.cancelAsUser(null,
Jeff Browne7ae6442013-11-13 00:45:18 -0800466 R.string.wifi_display_notification_disconnect, UserHandle.ALL);
Jeff Browna7f9c962012-10-17 15:15:12 -0700467
Jeff Browne7ae6442013-11-13 00:45:18 -0800468 if (state == WifiDisplayStatus.DISPLAY_STATE_CONNECTING
469 || state == WifiDisplayStatus.DISPLAY_STATE_CONNECTED) {
Jeff Browna7f9c962012-10-17 15:15:12 -0700470 Context context = getContext();
471
Jeff Brown66692502012-10-18 16:13:44 -0700472 // 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.
Jeff Browna7f9c962012-10-17 15:15:12 -0700490 Resources r = context.getResources();
Jeff Browne7ae6442013-11-13 00:45:18 -0800491 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 }
Jeff Browna7f9c962012-10-17 15:15:12 -0700521 mNotificationManager.notifyAsUser(null,
Jeff Browne7ae6442013-11-13 00:45:18 -0800522 R.string.wifi_display_notification_disconnect,
Jeff Browna7f9c962012-10-17 15:15:12 -0700523 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 }
Jeff Browne08ae382012-09-07 20:36:36 -0700535 }
536 };
537
Jeff Browncbad9762012-09-04 21:57:59 -0700538 private final WifiDisplayController.Listener mWifiDisplayListener =
539 new WifiDisplayController.Listener() {
540 @Override
Jeff Brown89d55462012-09-19 11:33:42 -0700541 public void onFeatureStateChanged(int featureState) {
Jeff Browncbad9762012-09-04 21:57:59 -0700542 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700543 if (mFeatureState != featureState) {
544 mFeatureState = featureState;
Jeff Browne08ae382012-09-07 20:36:36 -0700545 scheduleStatusChangedBroadcastLocked();
546 }
547 }
548 }
549
550 @Override
551 public void onScanStarted() {
552 synchronized (getSyncRoot()) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700553 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700554 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
Jeff Browne08ae382012-09-07 20:36:36 -0700555 scheduleStatusChangedBroadcastLocked();
556 }
557 }
558 }
559
Jeff Brown2444ae72012-10-11 14:30:21 -0700560 @Override
Jeff Brownce468a32013-11-21 16:42:03 -0800561 public void onScanResults(WifiDisplay[] availableDisplays) {
Jeff Browne08ae382012-09-07 20:36:36 -0700562 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700563 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
564 availableDisplays);
565
Chong Zhangab87a632013-06-11 10:25:49 -0700566 boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);
Jeff Brownce468a32013-11-21 16:42:03 -0800567
568 // Check whether any of the available displays changed canConnect status.
Chong Zhangab87a632013-06-11 10:25:49 -0700569 for (int i = 0; !changed && i<availableDisplays.length; i++) {
570 changed = availableDisplays[i].canConnect()
571 != mAvailableDisplays[i].canConnect();
572 }
573
Jeff Brownce468a32013-11-21 16:42:03 -0800574 if (changed) {
Jeff Brown89d55462012-09-19 11:33:42 -0700575 mAvailableDisplays = availableDisplays;
Jeff Brown74da1092012-11-07 16:02:13 -0800576 fixRememberedDisplayNamesFromAvailableDisplaysLocked();
Chong Zhangab87a632013-06-11 10:25:49 -0700577 updateDisplaysLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700578 scheduleStatusChangedBroadcastLocked();
579 }
580 }
581 }
582
583 @Override
Jeff Brownce468a32013-11-21 16:42:03 -0800584 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
Jeff Browne08ae382012-09-07 20:36:36 -0700594 public void onDisplayConnecting(WifiDisplay display) {
595 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700596 display = mPersistentDataStore.applyWifiDisplayAlias(display);
597
Jeff Brown180bbc72012-09-08 23:15:00 -0700598 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
599 || mActiveDisplay == null
600 || !mActiveDisplay.equals(display)) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700601 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
602 mActiveDisplay = display;
Jeff Browne08ae382012-09-07 20:36:36 -0700603 scheduleStatusChangedBroadcastLocked();
Jeff Browne7ae6442013-11-13 00:45:18 -0800604 scheduleUpdateNotificationLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700605 }
606 }
607 }
608
609 @Override
610 public void onDisplayConnectionFailed() {
611 synchronized (getSyncRoot()) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700612 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
613 || mActiveDisplay != null) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700614 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
615 mActiveDisplay = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700616 scheduleStatusChangedBroadcastLocked();
Jeff Browne7ae6442013-11-13 00:45:18 -0800617 scheduleUpdateNotificationLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700618 }
619 }
620 }
621
622 @Override
Jeff Brownf8f0edd2012-09-11 17:05:11 -0700623 public void onDisplayConnected(WifiDisplay display, Surface surface,
624 int width, int height, int flags) {
Jeff Browne08ae382012-09-07 20:36:36 -0700625 synchronized (getSyncRoot()) {
Jeff Brown89d55462012-09-19 11:33:42 -0700626 display = mPersistentDataStore.applyWifiDisplayAlias(display);
Jeff Brown74da1092012-11-07 16:02:13 -0800627 addDisplayDeviceLocked(display, surface, width, height, flags);
Jeff Browne08ae382012-09-07 20:36:36 -0700628
Jeff Brown180bbc72012-09-08 23:15:00 -0700629 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
630 || mActiveDisplay == null
631 || !mActiveDisplay.equals(display)) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700632 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
633 mActiveDisplay = display;
Jeff Browne08ae382012-09-07 20:36:36 -0700634 scheduleStatusChangedBroadcastLocked();
Jeff Browne7ae6442013-11-13 00:45:18 -0800635 scheduleUpdateNotificationLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700636 }
Jeff Browncbad9762012-09-04 21:57:59 -0700637 }
638 }
639
640 @Override
Chong Zhang1f3ecaa2013-05-03 15:55:36 -0700641 public void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo) {
642 synchronized (getSyncRoot()) {
643 mSessionInfo = sessionInfo;
644 scheduleStatusChangedBroadcastLocked();
645 }
646 }
647
648 @Override
Jeff Brown74da1092012-11-07 16:02:13 -0800649 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();
Jeff Browne7ae6442013-11-13 00:45:18 -0800658 scheduleUpdateNotificationLocked();
Jeff Brown74da1092012-11-07 16:02:13 -0800659 }
660 }
661 }
662
663 @Override
Jeff Browncbad9762012-09-04 21:57:59 -0700664 public void onDisplayDisconnected() {
665 // Stop listening.
666 synchronized (getSyncRoot()) {
Jeff Brown74da1092012-11-07 16:02:13 -0800667 removeDisplayDeviceLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700668
Jeff Brown180bbc72012-09-08 23:15:00 -0700669 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
670 || mActiveDisplay != null) {
Jeff Brown180bbc72012-09-08 23:15:00 -0700671 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
672 mActiveDisplay = null;
Jeff Browne08ae382012-09-07 20:36:36 -0700673 scheduleStatusChangedBroadcastLocked();
Jeff Browne7ae6442013-11-13 00:45:18 -0800674 scheduleUpdateNotificationLocked();
Jeff Browne08ae382012-09-07 20:36:36 -0700675 }
Jeff Browncbad9762012-09-04 21:57:59 -0700676 }
677 }
678 };
679
680 private final class WifiDisplayDevice extends DisplayDevice {
Jeff Brownee4f0292012-10-15 15:31:59 -0700681 private String mName;
Jeff Browncbad9762012-09-04 21:57:59 -0700682 private final int mWidth;
683 private final int mHeight;
684 private final float mRefreshRate;
685 private final int mFlags;
Jeff Brown92130f62012-10-24 21:28:33 -0700686 private final String mAddress;
Jeff Browncbad9762012-09-04 21:57:59 -0700687
688 private Surface mSurface;
689 private DisplayDeviceInfo mInfo;
690
691 public WifiDisplayDevice(IBinder displayToken, String name,
Jeff Brown92130f62012-10-24 21:28:33 -0700692 int width, int height, float refreshRate, int flags, String address,
Jeff Browncbad9762012-09-04 21:57:59 -0700693 Surface surface) {
694 super(WifiDisplayAdapter.this, displayToken);
695 mName = name;
696 mWidth = width;
697 mHeight = height;
698 mRefreshRate = refreshRate;
699 mFlags = flags;
Jeff Brown92130f62012-10-24 21:28:33 -0700700 mAddress = address;
Jeff Browncbad9762012-09-04 21:57:59 -0700701 mSurface = surface;
702 }
703
Jesse Hall6a6bc212013-08-08 12:15:03 -0700704 public void destroyLocked() {
705 if (mSurface != null) {
706 mSurface.release();
707 mSurface = null;
708 }
709 SurfaceControl.destroyDisplay(getDisplayTokenLocked());
Jeff Browncbad9762012-09-04 21:57:59 -0700710 }
711
Jeff Brownee4f0292012-10-15 15:31:59 -0700712 public void setNameLocked(String name) {
713 mName = name;
714 mInfo = null;
715 }
716
Jeff Browncbad9762012-09-04 21:57:59 -0700717 @Override
718 public void performTraversalInTransactionLocked() {
Jesse Hall6a6bc212013-08-08 12:15:03 -0700719 if (mSurface != null) {
720 setSurfaceInTransactionLocked(mSurface);
721 }
Jeff Browncbad9762012-09-04 21:57:59 -0700722 }
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;
Jeff Brown92130f62012-10-24 21:28:33 -0700733 mInfo.type = Display.TYPE_WIFI;
734 mInfo.address = mAddress;
Jeff Brownd728bf52012-09-08 18:05:28 -0700735 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
Jeff Browncbad9762012-09-04 21:57:59 -0700736 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
737 }
738 return mInfo;
739 }
740 }
Jeff Browna7f9c962012-10-17 15:15:12 -0700741
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 }
Jeff Browncbad9762012-09-04 21:57:59 -0700760}