blob: 45ae2c5a7c5ee96b513d824f788666857dbf7f9d [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
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.os.Binder;
24import android.os.Handler;
25import android.os.IBinder;
26import android.os.RemoteException;
27import android.os.IBinder.DeathRecipient;
Amith Yamasanicd757062012-10-19 18:23:52 -070028import android.os.UserHandle;
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070029import android.service.dreams.DreamService;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040030import android.service.dreams.IDreamService;
31import android.util.Slog;
32import android.view.IWindowManager;
33import android.view.WindowManager;
34import android.view.WindowManagerGlobal;
35
John Spurlockf4f6b4c2012-08-25 12:08:03 -040036import java.io.PrintWriter;
37import java.util.NoSuchElementException;
38
39/**
40 * Internal controller for starting and stopping the current dream and managing related state.
41 *
Jeff Brown62c82e42012-09-26 01:30:41 -070042 * Assumes all operations are called from the dream handler thread.
John Spurlockf4f6b4c2012-08-25 12:08:03 -040043 */
44final class DreamController {
Jeff Brown62c82e42012-09-26 01:30:41 -070045 private static final String TAG = "DreamController";
John Spurlockf4f6b4c2012-08-25 12:08:03 -040046
John Spurlock006f5672012-12-03 08:42:25 -050047 // How long we wait for a newly bound dream to create the service connection
48 private static final int DREAM_CONNECTION_TIMEOUT = 5 * 1000;
49
John Spurlockf4f6b4c2012-08-25 12:08:03 -040050 private final Context mContext;
Jeff Brown62c82e42012-09-26 01:30:41 -070051 private final Handler mHandler;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040052 private final Listener mListener;
Jeff Brown62c82e42012-09-26 01:30:41 -070053 private final IWindowManager mIWindowManager;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040054
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070055 private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
Jeff Brown62c82e42012-09-26 01:30:41 -070056 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070057 private final Intent mDreamingStoppedIntent = new Intent(Intent.ACTION_DREAMING_STOPPED)
Jeff Brown62c82e42012-09-26 01:30:41 -070058 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
John Spurlockf4f6b4c2012-08-25 12:08:03 -040059
John Spurlock591a9e82012-09-28 12:15:08 -040060 private final Intent mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
61
Jeff Brown62c82e42012-09-26 01:30:41 -070062 private DreamRecord mCurrentDream;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040063
John Spurlock006f5672012-12-03 08:42:25 -050064 private final Runnable mStopUnconnectedDreamRunnable = new Runnable() {
65 @Override
66 public void run() {
67 if (mCurrentDream != null && mCurrentDream.mBound && !mCurrentDream.mConnected) {
68 Slog.w(TAG, "Bound dream did not connect in the time allotted");
69 stopDream();
70 }
71 }
72 };
73
Jeff Brown62c82e42012-09-26 01:30:41 -070074 public DreamController(Context context, Handler handler, Listener listener) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -040075 mContext = context;
Jeff Brown62c82e42012-09-26 01:30:41 -070076 mHandler = handler;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040077 mListener = listener;
78 mIWindowManager = WindowManagerGlobal.getWindowManagerService();
79 }
80
Jeff Brown62c82e42012-09-26 01:30:41 -070081 public void dump(PrintWriter pw) {
82 pw.println("Dreamland:");
83 if (mCurrentDream != null) {
84 pw.println(" mCurrentDream:");
85 pw.println(" mToken=" + mCurrentDream.mToken);
86 pw.println(" mName=" + mCurrentDream.mName);
87 pw.println(" mIsTest=" + mCurrentDream.mIsTest);
88 pw.println(" mUserId=" + mCurrentDream.mUserId);
89 pw.println(" mBound=" + mCurrentDream.mBound);
90 pw.println(" mService=" + mCurrentDream.mService);
91 pw.println(" mSentStartBroadcast=" + mCurrentDream.mSentStartBroadcast);
92 } else {
93 pw.println(" mCurrentDream: null");
94 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -040095 }
96
Jeff Brown62c82e42012-09-26 01:30:41 -070097 public void startDream(Binder token, ComponentName name, boolean isTest, int userId) {
98 stopDream();
99
Amith Yamasanicd757062012-10-19 18:23:52 -0700100 // Close the notification shade. Don't need to send to all, but better to be explicit.
101 mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
John Spurlock591a9e82012-09-28 12:15:08 -0400102
Jeff Brown62c82e42012-09-26 01:30:41 -0700103 Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", userId=" + userId);
104
105 mCurrentDream = new DreamRecord(token, name, isTest, userId);
106
107 try {
108 mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
109 } catch (RemoteException ex) {
110 Slog.e(TAG, "Unable to add window token for dream.", ex);
111 stopDream();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400112 return;
113 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400114
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -0700115 Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
Jeff Brown62c82e42012-09-26 01:30:41 -0700116 intent.setComponent(name);
117 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
118 try {
119 if (!mContext.bindService(intent, mCurrentDream,
120 Context.BIND_AUTO_CREATE, userId)) {
121 Slog.e(TAG, "Unable to bind dream service: " + intent);
122 stopDream();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400123 return;
124 }
Jeff Brown62c82e42012-09-26 01:30:41 -0700125 } catch (SecurityException ex) {
126 Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
127 stopDream();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400128 return;
129 }
130
Jeff Brown62c82e42012-09-26 01:30:41 -0700131 mCurrentDream.mBound = true;
John Spurlock006f5672012-12-03 08:42:25 -0500132 mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400133 }
134
Jeff Brown62c82e42012-09-26 01:30:41 -0700135 public void stopDream() {
136 if (mCurrentDream == null) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400137 return;
138 }
139
Jeff Brown62c82e42012-09-26 01:30:41 -0700140 final DreamRecord oldDream = mCurrentDream;
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400141 mCurrentDream = null;
Jeff Brown62c82e42012-09-26 01:30:41 -0700142 Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
143 + ", isTest=" + oldDream.mIsTest + ", userId=" + oldDream.mUserId);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400144
John Spurlock006f5672012-12-03 08:42:25 -0500145 mHandler.removeCallbacks(mStopUnconnectedDreamRunnable);
146
Jeff Brown62c82e42012-09-26 01:30:41 -0700147 if (oldDream.mSentStartBroadcast) {
Amith Yamasanicd757062012-10-19 18:23:52 -0700148 mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
Jeff Brown62c82e42012-09-26 01:30:41 -0700149 }
150
151 if (oldDream.mService != null) {
Daniel Sandler2d784902012-10-03 23:04:50 -0400152 // Tell the dream that it's being stopped so that
153 // it can shut down nicely before we yank its window token out from
154 // under it.
155 try {
156 oldDream.mService.detach();
157 } catch (RemoteException ex) {
158 // we don't care; this thing is on the way out
159 }
160
Jeff Brown62c82e42012-09-26 01:30:41 -0700161 try {
162 oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
163 } catch (NoSuchElementException ex) {
164 // don't care
165 }
166 oldDream.mService = null;
167 }
168
169 if (oldDream.mBound) {
170 mContext.unbindService(oldDream);
171 }
172
173 try {
174 mIWindowManager.removeWindowToken(oldDream.mToken);
175 } catch (RemoteException ex) {
176 Slog.w(TAG, "Error removing window token for dream.", ex);
177 }
178
179 mHandler.post(new Runnable() {
180 @Override
181 public void run() {
182 mListener.onDreamStopped(oldDream.mToken);
183 }
184 });
185 }
186
187 private void attach(IDreamService service) {
188 try {
189 service.asBinder().linkToDeath(mCurrentDream, 0);
190 service.attach(mCurrentDream.mToken);
191 } catch (RemoteException ex) {
192 Slog.e(TAG, "The dream service died unexpectedly.", ex);
193 stopDream();
194 return;
195 }
196
197 mCurrentDream.mService = service;
198
199 if (!mCurrentDream.mIsTest) {
Amith Yamasanicd757062012-10-19 18:23:52 -0700200 mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
Jeff Brown62c82e42012-09-26 01:30:41 -0700201 mCurrentDream.mSentStartBroadcast = true;
202 }
203 }
204
205 /**
206 * Callback interface to be implemented by the {@link DreamManagerService}.
207 */
208 public interface Listener {
209 void onDreamStopped(Binder token);
210 }
211
212 private final class DreamRecord implements DeathRecipient, ServiceConnection {
213 public final Binder mToken;
214 public final ComponentName mName;
215 public final boolean mIsTest;
216 public final int mUserId;
217
218 public boolean mBound;
John Spurlock006f5672012-12-03 08:42:25 -0500219 public boolean mConnected;
Jeff Brown62c82e42012-09-26 01:30:41 -0700220 public IDreamService mService;
221 public boolean mSentStartBroadcast;
222
223 public DreamRecord(Binder token, ComponentName name,
224 boolean isTest, int userId) {
225 mToken = token;
226 mName = name;
227 mIsTest = isTest;
228 mUserId = userId;
229 }
230
231 // May be called on any thread.
232 @Override
233 public void binderDied() {
234 mHandler.post(new Runnable() {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400235 @Override
236 public void run() {
Jeff Brown62c82e42012-09-26 01:30:41 -0700237 mService = null;
238 if (mCurrentDream == DreamRecord.this) {
239 stopDream();
240 }
241 }
242 });
243 }
244
245 // May be called on any thread.
246 @Override
247 public void onServiceConnected(ComponentName name, final IBinder service) {
248 mHandler.post(new Runnable() {
249 @Override
250 public void run() {
John Spurlock006f5672012-12-03 08:42:25 -0500251 mConnected = true;
Jeff Brown62c82e42012-09-26 01:30:41 -0700252 if (mCurrentDream == DreamRecord.this && mService == null) {
253 attach(IDreamService.Stub.asInterface(service));
254 }
255 }
256 });
257 }
258
259 // May be called on any thread.
260 @Override
261 public void onServiceDisconnected(ComponentName name) {
262 mHandler.post(new Runnable() {
263 @Override
264 public void run() {
265 mService = null;
266 if (mCurrentDream == DreamRecord.this) {
267 stopDream();
268 }
269 }
270 });
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400271 }
272 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400273}