blob: fbad8dede87d69d3a2ccb3243d3682f18d24525e [file] [log] [blame]
John Spurlockf4f6b4c2012-08-25 12:08:03 -04001/*
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
Jeff Browncef440f2012-09-25 18:58:48 -070017package com.android.server.dreams;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040018
Chris Wren9bb290b2015-06-29 12:02:13 -040019import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010020import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Chris Wren9bb290b2015-06-29 12:02:13 -040021
John Spurlockf4f6b4c2012-08-25 12:08:03 -040022import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.ServiceConnection;
26import android.os.Binder;
Adrian Roos9ede1d22016-09-23 14:08:09 -070027import android.os.Bundle;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040028import android.os.Handler;
29import android.os.IBinder;
Adrian Roos9ede1d22016-09-23 14:08:09 -070030import android.os.IRemoteCallback;
Adrian Roos7445c0b2016-09-06 16:45:46 -070031import android.os.PowerManager;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040032import android.os.RemoteException;
33import android.os.IBinder.DeathRecipient;
Chris Wren9bb290b2015-06-29 12:02:13 -040034import android.os.SystemClock;
Jeff Brown3edf5272014-08-14 19:25:14 -070035import android.os.Trace;
Amith Yamasanicd757062012-10-19 18:23:52 -070036import android.os.UserHandle;
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070037import android.service.dreams.DreamService;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040038import android.service.dreams.IDreamService;
39import android.util.Slog;
40import android.view.IWindowManager;
41import android.view.WindowManager;
42import android.view.WindowManagerGlobal;
43
John Spurlockf4f6b4c2012-08-25 12:08:03 -040044import java.io.PrintWriter;
45import java.util.NoSuchElementException;
46
Wale Ogunwaleac2561e2016-11-01 15:43:46 -070047import static android.view.Display.DEFAULT_DISPLAY;
48import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
49
John Spurlockf4f6b4c2012-08-25 12:08:03 -040050/**
51 * Internal controller for starting and stopping the current dream and managing related state.
52 *
Jeff Brown62c82e42012-09-26 01:30:41 -070053 * Assumes all operations are called from the dream handler thread.
John Spurlockf4f6b4c2012-08-25 12:08:03 -040054 */
55final class DreamController {
Jeff Brown62c82e42012-09-26 01:30:41 -070056 private static final String TAG = "DreamController";
John Spurlockf4f6b4c2012-08-25 12:08:03 -040057
John Spurlock006f5672012-12-03 08:42:25 -050058 // How long we wait for a newly bound dream to create the service connection
59 private static final int DREAM_CONNECTION_TIMEOUT = 5 * 1000;
60
Jeff Brownf6d46682014-07-17 22:44:20 -070061 // Time to allow the dream to perform an exit transition when waking up.
62 private static final int DREAM_FINISH_TIMEOUT = 5 * 1000;
63
John Spurlockf4f6b4c2012-08-25 12:08:03 -040064 private final Context mContext;
Jeff Brown62c82e42012-09-26 01:30:41 -070065 private final Handler mHandler;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040066 private final Listener mListener;
Jeff Brown62c82e42012-09-26 01:30:41 -070067 private final IWindowManager mIWindowManager;
Chris Wren9bb290b2015-06-29 12:02:13 -040068 private long mDreamStartTime;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040069
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070070 private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
Jeff Brown62c82e42012-09-26 01:30:41 -070071 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070072 private final Intent mDreamingStoppedIntent = new Intent(Intent.ACTION_DREAMING_STOPPED)
Jeff Brown62c82e42012-09-26 01:30:41 -070073 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
John Spurlockf4f6b4c2012-08-25 12:08:03 -040074
Dianne Hackborn57dd7372015-07-27 18:11:14 -070075 private final Intent mCloseNotificationShadeIntent;
John Spurlock591a9e82012-09-28 12:15:08 -040076
Jeff Brown62c82e42012-09-26 01:30:41 -070077 private DreamRecord mCurrentDream;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040078
John Spurlock006f5672012-12-03 08:42:25 -050079 private final Runnable mStopUnconnectedDreamRunnable = new Runnable() {
80 @Override
81 public void run() {
82 if (mCurrentDream != null && mCurrentDream.mBound && !mCurrentDream.mConnected) {
83 Slog.w(TAG, "Bound dream did not connect in the time allotted");
Jeff Brownf6d46682014-07-17 22:44:20 -070084 stopDream(true /*immediate*/);
John Spurlock006f5672012-12-03 08:42:25 -050085 }
86 }
87 };
88
Jeff Brownf6d46682014-07-17 22:44:20 -070089 private final Runnable mStopStubbornDreamRunnable = new Runnable() {
90 @Override
91 public void run() {
92 Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted");
93 stopDream(true /*immediate*/);
94 }
95 };
96
Jeff Brown62c82e42012-09-26 01:30:41 -070097 public DreamController(Context context, Handler handler, Listener listener) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -040098 mContext = context;
Jeff Brown62c82e42012-09-26 01:30:41 -070099 mHandler = handler;
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400100 mListener = listener;
101 mIWindowManager = WindowManagerGlobal.getWindowManagerService();
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700102 mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
103 mCloseNotificationShadeIntent.putExtra("reason", "dream");
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400104 }
105
Jeff Brown62c82e42012-09-26 01:30:41 -0700106 public void dump(PrintWriter pw) {
107 pw.println("Dreamland:");
108 if (mCurrentDream != null) {
109 pw.println(" mCurrentDream:");
110 pw.println(" mToken=" + mCurrentDream.mToken);
111 pw.println(" mName=" + mCurrentDream.mName);
112 pw.println(" mIsTest=" + mCurrentDream.mIsTest);
Jeff Brown26875502014-01-30 21:47:47 -0800113 pw.println(" mCanDoze=" + mCurrentDream.mCanDoze);
Jeff Brown62c82e42012-09-26 01:30:41 -0700114 pw.println(" mUserId=" + mCurrentDream.mUserId);
115 pw.println(" mBound=" + mCurrentDream.mBound);
116 pw.println(" mService=" + mCurrentDream.mService);
117 pw.println(" mSentStartBroadcast=" + mCurrentDream.mSentStartBroadcast);
Jeff Brownf6d46682014-07-17 22:44:20 -0700118 pw.println(" mWakingGently=" + mCurrentDream.mWakingGently);
Jeff Brown62c82e42012-09-26 01:30:41 -0700119 } else {
120 pw.println(" mCurrentDream: null");
121 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400122 }
123
Jeff Brown26875502014-01-30 21:47:47 -0800124 public void startDream(Binder token, ComponentName name,
Adrian Roos7445c0b2016-09-06 16:45:46 -0700125 boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
Jeff Brownf6d46682014-07-17 22:44:20 -0700126 stopDream(true /*immediate*/);
Jeff Brown62c82e42012-09-26 01:30:41 -0700127
Jeff Brown3edf5272014-08-14 19:25:14 -0700128 Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
Jeff Brown62c82e42012-09-26 01:30:41 -0700129 try {
Adrian Roos7445c0b2016-09-06 16:45:46 -0700130 // Close the notification shade. No need to send to all, but better to be explicit.
Jeff Brown3edf5272014-08-14 19:25:14 -0700131 mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400132
Jeff Brown3edf5272014-08-14 19:25:14 -0700133 Slog.i(TAG, "Starting dream: name=" + name
134 + ", isTest=" + isTest + ", canDoze=" + canDoze
135 + ", userId=" + userId);
136
Adrian Roos7445c0b2016-09-06 16:45:46 -0700137 mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock);
Jeff Brown3edf5272014-08-14 19:25:14 -0700138
Chris Wren9bb290b2015-06-29 12:02:13 -0400139 mDreamStartTime = SystemClock.elapsedRealtime();
140 MetricsLogger.visible(mContext,
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500141 mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING);
Chris Wren9bb290b2015-06-29 12:02:13 -0400142
Jeff Brown3edf5272014-08-14 19:25:14 -0700143 try {
Wale Ogunwaleac2561e2016-11-01 15:43:46 -0700144 mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY);
Jeff Brown3edf5272014-08-14 19:25:14 -0700145 } catch (RemoteException ex) {
146 Slog.e(TAG, "Unable to add window token for dream.", ex);
Jeff Brownf6d46682014-07-17 22:44:20 -0700147 stopDream(true /*immediate*/);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400148 return;
149 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400150
Jeff Brown3edf5272014-08-14 19:25:14 -0700151 Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
152 intent.setComponent(name);
153 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
154 try {
155 if (!mContext.bindServiceAsUser(intent, mCurrentDream,
Dianne Hackbornd69e4c12015-04-24 09:54:54 -0700156 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
157 new UserHandle(userId))) {
Jeff Brown3edf5272014-08-14 19:25:14 -0700158 Slog.e(TAG, "Unable to bind dream service: " + intent);
159 stopDream(true /*immediate*/);
160 return;
161 }
162 } catch (SecurityException ex) {
163 Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
164 stopDream(true /*immediate*/);
165 return;
166 }
167
168 mCurrentDream.mBound = true;
169 mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
170 } finally {
171 Trace.traceEnd(Trace.TRACE_TAG_POWER);
172 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400173 }
174
Jeff Brownf6d46682014-07-17 22:44:20 -0700175 public void stopDream(boolean immediate) {
Jeff Brown62c82e42012-09-26 01:30:41 -0700176 if (mCurrentDream == null) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400177 return;
178 }
179
Jeff Brown3edf5272014-08-14 19:25:14 -0700180 Trace.traceBegin(Trace.TRACE_TAG_POWER, "stopDream");
181 try {
182 if (!immediate) {
183 if (mCurrentDream.mWakingGently) {
184 return; // already waking gently
185 }
Jeff Brownf6d46682014-07-17 22:44:20 -0700186
Jeff Brown3edf5272014-08-14 19:25:14 -0700187 if (mCurrentDream.mService != null) {
188 // Give the dream a moment to wake up and finish itself gently.
189 mCurrentDream.mWakingGently = true;
190 try {
191 mCurrentDream.mService.wakeUp();
192 mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT);
193 return;
194 } catch (RemoteException ex) {
195 // oh well, we tried, finish immediately instead
196 }
Jeff Brownf6d46682014-07-17 22:44:20 -0700197 }
198 }
Jeff Brownf6d46682014-07-17 22:44:20 -0700199
Jeff Brown3edf5272014-08-14 19:25:14 -0700200 final DreamRecord oldDream = mCurrentDream;
201 mCurrentDream = null;
202 Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
203 + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze
204 + ", userId=" + oldDream.mUserId);
Chris Wren9bb290b2015-06-29 12:02:13 -0400205 MetricsLogger.hidden(mContext,
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500206 oldDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING);
Chris Wren9bb290b2015-06-29 12:02:13 -0400207 MetricsLogger.histogram(mContext,
208 oldDream.mCanDoze ? "dozing_minutes" : "dreaming_minutes" ,
209 (int) ((SystemClock.elapsedRealtime() - mDreamStartTime) / (1000L * 60L)));
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400210
Jeff Brown3edf5272014-08-14 19:25:14 -0700211 mHandler.removeCallbacks(mStopUnconnectedDreamRunnable);
212 mHandler.removeCallbacks(mStopStubbornDreamRunnable);
John Spurlock006f5672012-12-03 08:42:25 -0500213
Jeff Brown3edf5272014-08-14 19:25:14 -0700214 if (oldDream.mSentStartBroadcast) {
215 mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
216 }
Jeff Brown62c82e42012-09-26 01:30:41 -0700217
Jeff Brown3edf5272014-08-14 19:25:14 -0700218 if (oldDream.mService != null) {
219 // Tell the dream that it's being stopped so that
220 // it can shut down nicely before we yank its window token out from
221 // under it.
222 try {
223 oldDream.mService.detach();
224 } catch (RemoteException ex) {
225 // we don't care; this thing is on the way out
226 }
227
228 try {
229 oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
230 } catch (NoSuchElementException ex) {
231 // don't care
232 }
233 oldDream.mService = null;
234 }
235
236 if (oldDream.mBound) {
237 mContext.unbindService(oldDream);
238 }
Adrian Roos7445c0b2016-09-06 16:45:46 -0700239 oldDream.releaseWakeLockIfNeeded();
Jeff Brown3edf5272014-08-14 19:25:14 -0700240
Daniel Sandler2d784902012-10-03 23:04:50 -0400241 try {
Wale Ogunwaleac2561e2016-11-01 15:43:46 -0700242 mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY);
Daniel Sandler2d784902012-10-03 23:04:50 -0400243 } catch (RemoteException ex) {
Jeff Brown3edf5272014-08-14 19:25:14 -0700244 Slog.w(TAG, "Error removing window token for dream.", ex);
Daniel Sandler2d784902012-10-03 23:04:50 -0400245 }
246
Jeff Brown3edf5272014-08-14 19:25:14 -0700247 mHandler.post(new Runnable() {
248 @Override
249 public void run() {
250 mListener.onDreamStopped(oldDream.mToken);
251 }
252 });
253 } finally {
254 Trace.traceEnd(Trace.TRACE_TAG_POWER);
Jeff Brown62c82e42012-09-26 01:30:41 -0700255 }
Jeff Brown62c82e42012-09-26 01:30:41 -0700256 }
257
258 private void attach(IDreamService service) {
259 try {
260 service.asBinder().linkToDeath(mCurrentDream, 0);
Adrian Roos9ede1d22016-09-23 14:08:09 -0700261 service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze,
262 mCurrentDream.mDreamingStartedCallback);
Jeff Brown62c82e42012-09-26 01:30:41 -0700263 } catch (RemoteException ex) {
264 Slog.e(TAG, "The dream service died unexpectedly.", ex);
Jeff Brownf6d46682014-07-17 22:44:20 -0700265 stopDream(true /*immediate*/);
Jeff Brown62c82e42012-09-26 01:30:41 -0700266 return;
267 }
268
269 mCurrentDream.mService = service;
270
271 if (!mCurrentDream.mIsTest) {
Amith Yamasanicd757062012-10-19 18:23:52 -0700272 mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
Jeff Brown62c82e42012-09-26 01:30:41 -0700273 mCurrentDream.mSentStartBroadcast = true;
274 }
275 }
276
277 /**
278 * Callback interface to be implemented by the {@link DreamManagerService}.
279 */
280 public interface Listener {
281 void onDreamStopped(Binder token);
282 }
283
284 private final class DreamRecord implements DeathRecipient, ServiceConnection {
285 public final Binder mToken;
286 public final ComponentName mName;
287 public final boolean mIsTest;
Jeff Brown26875502014-01-30 21:47:47 -0800288 public final boolean mCanDoze;
Jeff Brown62c82e42012-09-26 01:30:41 -0700289 public final int mUserId;
290
Adrian Roos7445c0b2016-09-06 16:45:46 -0700291 public PowerManager.WakeLock mWakeLock;
Jeff Brown62c82e42012-09-26 01:30:41 -0700292 public boolean mBound;
John Spurlock006f5672012-12-03 08:42:25 -0500293 public boolean mConnected;
Jeff Brown62c82e42012-09-26 01:30:41 -0700294 public IDreamService mService;
295 public boolean mSentStartBroadcast;
296
Jeff Brownf6d46682014-07-17 22:44:20 -0700297 public boolean mWakingGently;
298
Jeff Brown62c82e42012-09-26 01:30:41 -0700299 public DreamRecord(Binder token, ComponentName name,
Adrian Roos7445c0b2016-09-06 16:45:46 -0700300 boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
Jeff Brown62c82e42012-09-26 01:30:41 -0700301 mToken = token;
302 mName = name;
303 mIsTest = isTest;
Jeff Brown26875502014-01-30 21:47:47 -0800304 mCanDoze = canDoze;
Jeff Brown62c82e42012-09-26 01:30:41 -0700305 mUserId = userId;
Adrian Roos7445c0b2016-09-06 16:45:46 -0700306 mWakeLock = wakeLock;
Adrian Roos9ede1d22016-09-23 14:08:09 -0700307 // Hold the lock while we're waiting for the service to connect and start dreaming.
308 // Released after the service has started dreaming, we stop dreaming, or it timed out.
Adrian Roos7445c0b2016-09-06 16:45:46 -0700309 mWakeLock.acquire();
Adrian Roos9ede1d22016-09-23 14:08:09 -0700310 mHandler.postDelayed(mReleaseWakeLockIfNeeded, 10000);
Jeff Brown62c82e42012-09-26 01:30:41 -0700311 }
312
313 // May be called on any thread.
314 @Override
315 public void binderDied() {
316 mHandler.post(new Runnable() {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400317 @Override
318 public void run() {
Jeff Brown62c82e42012-09-26 01:30:41 -0700319 mService = null;
320 if (mCurrentDream == DreamRecord.this) {
Jeff Brownf6d46682014-07-17 22:44:20 -0700321 stopDream(true /*immediate*/);
Jeff Brown62c82e42012-09-26 01:30:41 -0700322 }
323 }
324 });
325 }
326
327 // May be called on any thread.
328 @Override
329 public void onServiceConnected(ComponentName name, final IBinder service) {
330 mHandler.post(new Runnable() {
331 @Override
332 public void run() {
Adrian Roos9ede1d22016-09-23 14:08:09 -0700333 mConnected = true;
334 if (mCurrentDream == DreamRecord.this && mService == null) {
335 attach(IDreamService.Stub.asInterface(service));
336 // Wake lock will be released once dreaming starts.
337 } else {
Adrian Roos7445c0b2016-09-06 16:45:46 -0700338 releaseWakeLockIfNeeded();
Jeff Brown62c82e42012-09-26 01:30:41 -0700339 }
340 }
341 });
342 }
343
344 // May be called on any thread.
345 @Override
346 public void onServiceDisconnected(ComponentName name) {
347 mHandler.post(new Runnable() {
348 @Override
349 public void run() {
350 mService = null;
351 if (mCurrentDream == DreamRecord.this) {
Jeff Brownf6d46682014-07-17 22:44:20 -0700352 stopDream(true /*immediate*/);
Jeff Brown62c82e42012-09-26 01:30:41 -0700353 }
354 }
355 });
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400356 }
Adrian Roos9ede1d22016-09-23 14:08:09 -0700357
358 void releaseWakeLockIfNeeded() {
359 if (mWakeLock != null) {
360 mWakeLock.release();
361 mWakeLock = null;
362 mHandler.removeCallbacks(mReleaseWakeLockIfNeeded);
363 }
364 }
365
366 final Runnable mReleaseWakeLockIfNeeded = this::releaseWakeLockIfNeeded;
367
368 final IRemoteCallback mDreamingStartedCallback = new IRemoteCallback.Stub() {
369 // May be called on any thread.
370 @Override
371 public void sendResult(Bundle data) throws RemoteException {
372 mHandler.post(mReleaseWakeLockIfNeeded);
373 }
374 };
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400375 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400376}