blob: a63399656bab09f6e4fc32787fcbba3cb7768056 [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;
Chris Wrenf6e9228b2016-01-26 18:04:35 -050020import com.android.internal.logging.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;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.RemoteException;
30import android.os.IBinder.DeathRecipient;
Chris Wren9bb290b2015-06-29 12:02:13 -040031import android.os.SystemClock;
Jeff Brown3edf5272014-08-14 19:25:14 -070032import android.os.Trace;
Amith Yamasanicd757062012-10-19 18:23:52 -070033import android.os.UserHandle;
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070034import android.service.dreams.DreamService;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040035import android.service.dreams.IDreamService;
36import android.util.Slog;
37import android.view.IWindowManager;
38import android.view.WindowManager;
39import android.view.WindowManagerGlobal;
40
John Spurlockf4f6b4c2012-08-25 12:08:03 -040041import java.io.PrintWriter;
42import java.util.NoSuchElementException;
43
44/**
45 * Internal controller for starting and stopping the current dream and managing related state.
46 *
Jeff Brown62c82e42012-09-26 01:30:41 -070047 * Assumes all operations are called from the dream handler thread.
John Spurlockf4f6b4c2012-08-25 12:08:03 -040048 */
49final class DreamController {
Jeff Brown62c82e42012-09-26 01:30:41 -070050 private static final String TAG = "DreamController";
John Spurlockf4f6b4c2012-08-25 12:08:03 -040051
John Spurlock006f5672012-12-03 08:42:25 -050052 // How long we wait for a newly bound dream to create the service connection
53 private static final int DREAM_CONNECTION_TIMEOUT = 5 * 1000;
54
Jeff Brownf6d46682014-07-17 22:44:20 -070055 // Time to allow the dream to perform an exit transition when waking up.
56 private static final int DREAM_FINISH_TIMEOUT = 5 * 1000;
57
John Spurlockf4f6b4c2012-08-25 12:08:03 -040058 private final Context mContext;
Jeff Brown62c82e42012-09-26 01:30:41 -070059 private final Handler mHandler;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040060 private final Listener mListener;
Jeff Brown62c82e42012-09-26 01:30:41 -070061 private final IWindowManager mIWindowManager;
Chris Wren9bb290b2015-06-29 12:02:13 -040062 private long mDreamStartTime;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040063
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070064 private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
Jeff Brown62c82e42012-09-26 01:30:41 -070065 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070066 private final Intent mDreamingStoppedIntent = new Intent(Intent.ACTION_DREAMING_STOPPED)
Jeff Brown62c82e42012-09-26 01:30:41 -070067 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
John Spurlockf4f6b4c2012-08-25 12:08:03 -040068
Dianne Hackborn57dd7372015-07-27 18:11:14 -070069 private final Intent mCloseNotificationShadeIntent;
John Spurlock591a9e82012-09-28 12:15:08 -040070
Jeff Brown62c82e42012-09-26 01:30:41 -070071 private DreamRecord mCurrentDream;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040072
John Spurlock006f5672012-12-03 08:42:25 -050073 private final Runnable mStopUnconnectedDreamRunnable = new Runnable() {
74 @Override
75 public void run() {
76 if (mCurrentDream != null && mCurrentDream.mBound && !mCurrentDream.mConnected) {
77 Slog.w(TAG, "Bound dream did not connect in the time allotted");
Jeff Brownf6d46682014-07-17 22:44:20 -070078 stopDream(true /*immediate*/);
John Spurlock006f5672012-12-03 08:42:25 -050079 }
80 }
81 };
82
Jeff Brownf6d46682014-07-17 22:44:20 -070083 private final Runnable mStopStubbornDreamRunnable = new Runnable() {
84 @Override
85 public void run() {
86 Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted");
87 stopDream(true /*immediate*/);
88 }
89 };
90
Jeff Brown62c82e42012-09-26 01:30:41 -070091 public DreamController(Context context, Handler handler, Listener listener) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -040092 mContext = context;
Jeff Brown62c82e42012-09-26 01:30:41 -070093 mHandler = handler;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040094 mListener = listener;
95 mIWindowManager = WindowManagerGlobal.getWindowManagerService();
Dianne Hackborn57dd7372015-07-27 18:11:14 -070096 mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
97 mCloseNotificationShadeIntent.putExtra("reason", "dream");
John Spurlockf4f6b4c2012-08-25 12:08:03 -040098 }
99
Jeff Brown62c82e42012-09-26 01:30:41 -0700100 public void dump(PrintWriter pw) {
101 pw.println("Dreamland:");
102 if (mCurrentDream != null) {
103 pw.println(" mCurrentDream:");
104 pw.println(" mToken=" + mCurrentDream.mToken);
105 pw.println(" mName=" + mCurrentDream.mName);
106 pw.println(" mIsTest=" + mCurrentDream.mIsTest);
Jeff Brown26875502014-01-30 21:47:47 -0800107 pw.println(" mCanDoze=" + mCurrentDream.mCanDoze);
Jeff Brown62c82e42012-09-26 01:30:41 -0700108 pw.println(" mUserId=" + mCurrentDream.mUserId);
109 pw.println(" mBound=" + mCurrentDream.mBound);
110 pw.println(" mService=" + mCurrentDream.mService);
111 pw.println(" mSentStartBroadcast=" + mCurrentDream.mSentStartBroadcast);
Jeff Brownf6d46682014-07-17 22:44:20 -0700112 pw.println(" mWakingGently=" + mCurrentDream.mWakingGently);
Jeff Brown62c82e42012-09-26 01:30:41 -0700113 } else {
114 pw.println(" mCurrentDream: null");
115 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400116 }
117
Jeff Brown26875502014-01-30 21:47:47 -0800118 public void startDream(Binder token, ComponentName name,
119 boolean isTest, boolean canDoze, int userId) {
Jeff Brownf6d46682014-07-17 22:44:20 -0700120 stopDream(true /*immediate*/);
Jeff Brown62c82e42012-09-26 01:30:41 -0700121
Jeff Brown3edf5272014-08-14 19:25:14 -0700122 Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
Jeff Brown62c82e42012-09-26 01:30:41 -0700123 try {
Jeff Brown3edf5272014-08-14 19:25:14 -0700124 // Close the notification shade. Don't need to send to all, but better to be explicit.
125 mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400126
Jeff Brown3edf5272014-08-14 19:25:14 -0700127 Slog.i(TAG, "Starting dream: name=" + name
128 + ", isTest=" + isTest + ", canDoze=" + canDoze
129 + ", userId=" + userId);
130
131 mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId);
132
Chris Wren9bb290b2015-06-29 12:02:13 -0400133 mDreamStartTime = SystemClock.elapsedRealtime();
134 MetricsLogger.visible(mContext,
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500135 mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING);
Chris Wren9bb290b2015-06-29 12:02:13 -0400136
Jeff Brown3edf5272014-08-14 19:25:14 -0700137 try {
138 mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
139 } catch (RemoteException ex) {
140 Slog.e(TAG, "Unable to add window token for dream.", ex);
Jeff Brownf6d46682014-07-17 22:44:20 -0700141 stopDream(true /*immediate*/);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400142 return;
143 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400144
Jeff Brown3edf5272014-08-14 19:25:14 -0700145 Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
146 intent.setComponent(name);
147 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
148 try {
149 if (!mContext.bindServiceAsUser(intent, mCurrentDream,
Dianne Hackbornd69e4c12015-04-24 09:54:54 -0700150 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
151 new UserHandle(userId))) {
Jeff Brown3edf5272014-08-14 19:25:14 -0700152 Slog.e(TAG, "Unable to bind dream service: " + intent);
153 stopDream(true /*immediate*/);
154 return;
155 }
156 } catch (SecurityException ex) {
157 Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
158 stopDream(true /*immediate*/);
159 return;
160 }
161
162 mCurrentDream.mBound = true;
163 mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
164 } finally {
165 Trace.traceEnd(Trace.TRACE_TAG_POWER);
166 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400167 }
168
Jeff Brownf6d46682014-07-17 22:44:20 -0700169 public void stopDream(boolean immediate) {
Jeff Brown62c82e42012-09-26 01:30:41 -0700170 if (mCurrentDream == null) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400171 return;
172 }
173
Jeff Brown3edf5272014-08-14 19:25:14 -0700174 Trace.traceBegin(Trace.TRACE_TAG_POWER, "stopDream");
175 try {
176 if (!immediate) {
177 if (mCurrentDream.mWakingGently) {
178 return; // already waking gently
179 }
Jeff Brownf6d46682014-07-17 22:44:20 -0700180
Jeff Brown3edf5272014-08-14 19:25:14 -0700181 if (mCurrentDream.mService != null) {
182 // Give the dream a moment to wake up and finish itself gently.
183 mCurrentDream.mWakingGently = true;
184 try {
185 mCurrentDream.mService.wakeUp();
186 mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT);
187 return;
188 } catch (RemoteException ex) {
189 // oh well, we tried, finish immediately instead
190 }
Jeff Brownf6d46682014-07-17 22:44:20 -0700191 }
192 }
Jeff Brownf6d46682014-07-17 22:44:20 -0700193
Jeff Brown3edf5272014-08-14 19:25:14 -0700194 final DreamRecord oldDream = mCurrentDream;
195 mCurrentDream = null;
196 Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
197 + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze
198 + ", userId=" + oldDream.mUserId);
Chris Wren9bb290b2015-06-29 12:02:13 -0400199 MetricsLogger.hidden(mContext,
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500200 oldDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING);
Chris Wren9bb290b2015-06-29 12:02:13 -0400201 MetricsLogger.histogram(mContext,
202 oldDream.mCanDoze ? "dozing_minutes" : "dreaming_minutes" ,
203 (int) ((SystemClock.elapsedRealtime() - mDreamStartTime) / (1000L * 60L)));
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400204
Jeff Brown3edf5272014-08-14 19:25:14 -0700205 mHandler.removeCallbacks(mStopUnconnectedDreamRunnable);
206 mHandler.removeCallbacks(mStopStubbornDreamRunnable);
John Spurlock006f5672012-12-03 08:42:25 -0500207
Jeff Brown3edf5272014-08-14 19:25:14 -0700208 if (oldDream.mSentStartBroadcast) {
209 mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
210 }
Jeff Brown62c82e42012-09-26 01:30:41 -0700211
Jeff Brown3edf5272014-08-14 19:25:14 -0700212 if (oldDream.mService != null) {
213 // Tell the dream that it's being stopped so that
214 // it can shut down nicely before we yank its window token out from
215 // under it.
216 try {
217 oldDream.mService.detach();
218 } catch (RemoteException ex) {
219 // we don't care; this thing is on the way out
220 }
221
222 try {
223 oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
224 } catch (NoSuchElementException ex) {
225 // don't care
226 }
227 oldDream.mService = null;
228 }
229
230 if (oldDream.mBound) {
231 mContext.unbindService(oldDream);
232 }
233
Daniel Sandler2d784902012-10-03 23:04:50 -0400234 try {
Jeff Brown3edf5272014-08-14 19:25:14 -0700235 mIWindowManager.removeWindowToken(oldDream.mToken);
Daniel Sandler2d784902012-10-03 23:04:50 -0400236 } catch (RemoteException ex) {
Jeff Brown3edf5272014-08-14 19:25:14 -0700237 Slog.w(TAG, "Error removing window token for dream.", ex);
Daniel Sandler2d784902012-10-03 23:04:50 -0400238 }
239
Jeff Brown3edf5272014-08-14 19:25:14 -0700240 mHandler.post(new Runnable() {
241 @Override
242 public void run() {
243 mListener.onDreamStopped(oldDream.mToken);
244 }
245 });
246 } finally {
247 Trace.traceEnd(Trace.TRACE_TAG_POWER);
Jeff Brown62c82e42012-09-26 01:30:41 -0700248 }
Jeff Brown62c82e42012-09-26 01:30:41 -0700249 }
250
251 private void attach(IDreamService service) {
252 try {
253 service.asBinder().linkToDeath(mCurrentDream, 0);
Jeff Brown26875502014-01-30 21:47:47 -0800254 service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze);
Jeff Brown62c82e42012-09-26 01:30:41 -0700255 } catch (RemoteException ex) {
256 Slog.e(TAG, "The dream service died unexpectedly.", ex);
Jeff Brownf6d46682014-07-17 22:44:20 -0700257 stopDream(true /*immediate*/);
Jeff Brown62c82e42012-09-26 01:30:41 -0700258 return;
259 }
260
261 mCurrentDream.mService = service;
262
263 if (!mCurrentDream.mIsTest) {
Amith Yamasanicd757062012-10-19 18:23:52 -0700264 mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
Jeff Brown62c82e42012-09-26 01:30:41 -0700265 mCurrentDream.mSentStartBroadcast = true;
266 }
267 }
268
269 /**
270 * Callback interface to be implemented by the {@link DreamManagerService}.
271 */
272 public interface Listener {
273 void onDreamStopped(Binder token);
274 }
275
276 private final class DreamRecord implements DeathRecipient, ServiceConnection {
277 public final Binder mToken;
278 public final ComponentName mName;
279 public final boolean mIsTest;
Jeff Brown26875502014-01-30 21:47:47 -0800280 public final boolean mCanDoze;
Jeff Brown62c82e42012-09-26 01:30:41 -0700281 public final int mUserId;
282
283 public boolean mBound;
John Spurlock006f5672012-12-03 08:42:25 -0500284 public boolean mConnected;
Jeff Brown62c82e42012-09-26 01:30:41 -0700285 public IDreamService mService;
286 public boolean mSentStartBroadcast;
287
Jeff Brownf6d46682014-07-17 22:44:20 -0700288 public boolean mWakingGently;
289
Jeff Brown62c82e42012-09-26 01:30:41 -0700290 public DreamRecord(Binder token, ComponentName name,
Jeff Brown26875502014-01-30 21:47:47 -0800291 boolean isTest, boolean canDoze, int userId) {
Jeff Brown62c82e42012-09-26 01:30:41 -0700292 mToken = token;
293 mName = name;
294 mIsTest = isTest;
Jeff Brown26875502014-01-30 21:47:47 -0800295 mCanDoze = canDoze;
Jeff Brown62c82e42012-09-26 01:30:41 -0700296 mUserId = userId;
297 }
298
299 // May be called on any thread.
300 @Override
301 public void binderDied() {
302 mHandler.post(new Runnable() {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400303 @Override
304 public void run() {
Jeff Brown62c82e42012-09-26 01:30:41 -0700305 mService = null;
306 if (mCurrentDream == DreamRecord.this) {
Jeff Brownf6d46682014-07-17 22:44:20 -0700307 stopDream(true /*immediate*/);
Jeff Brown62c82e42012-09-26 01:30:41 -0700308 }
309 }
310 });
311 }
312
313 // May be called on any thread.
314 @Override
315 public void onServiceConnected(ComponentName name, final IBinder service) {
316 mHandler.post(new Runnable() {
317 @Override
318 public void run() {
John Spurlock006f5672012-12-03 08:42:25 -0500319 mConnected = true;
Jeff Brown62c82e42012-09-26 01:30:41 -0700320 if (mCurrentDream == DreamRecord.this && mService == null) {
321 attach(IDreamService.Stub.asInterface(service));
322 }
323 }
324 });
325 }
326
327 // May be called on any thread.
328 @Override
329 public void onServiceDisconnected(ComponentName name) {
330 mHandler.post(new Runnable() {
331 @Override
332 public void run() {
333 mService = null;
334 if (mCurrentDream == DreamRecord.this) {
Jeff Brownf6d46682014-07-17 22:44:20 -0700335 stopDream(true /*immediate*/);
Jeff Brown62c82e42012-09-26 01:30:41 -0700336 }
337 }
338 });
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400339 }
340 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400341}