blob: bfb60bbe8f55ad3e09d8f74ebf603e3f0dac6072 [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;
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070028import android.service.dreams.DreamService;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040029import android.service.dreams.IDreamService;
30import android.util.Slog;
31import android.view.IWindowManager;
32import android.view.WindowManager;
33import android.view.WindowManagerGlobal;
34
John Spurlockf4f6b4c2012-08-25 12:08:03 -040035import java.io.PrintWriter;
36import java.util.NoSuchElementException;
37
38/**
39 * Internal controller for starting and stopping the current dream and managing related state.
40 *
Jeff Brown62c82e42012-09-26 01:30:41 -070041 * Assumes all operations are called from the dream handler thread.
John Spurlockf4f6b4c2012-08-25 12:08:03 -040042 */
43final class DreamController {
Jeff Brown62c82e42012-09-26 01:30:41 -070044 private static final String TAG = "DreamController";
John Spurlockf4f6b4c2012-08-25 12:08:03 -040045
46 private final Context mContext;
Jeff Brown62c82e42012-09-26 01:30:41 -070047 private final Handler mHandler;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040048 private final Listener mListener;
Jeff Brown62c82e42012-09-26 01:30:41 -070049 private final IWindowManager mIWindowManager;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040050
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070051 private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
Jeff Brown62c82e42012-09-26 01:30:41 -070052 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -070053 private final Intent mDreamingStoppedIntent = new Intent(Intent.ACTION_DREAMING_STOPPED)
Jeff Brown62c82e42012-09-26 01:30:41 -070054 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
John Spurlockf4f6b4c2012-08-25 12:08:03 -040055
John Spurlock591a9e82012-09-28 12:15:08 -040056 private final Intent mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
57
Jeff Brown62c82e42012-09-26 01:30:41 -070058 private DreamRecord mCurrentDream;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040059
Jeff Brown62c82e42012-09-26 01:30:41 -070060 public DreamController(Context context, Handler handler, Listener listener) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -040061 mContext = context;
Jeff Brown62c82e42012-09-26 01:30:41 -070062 mHandler = handler;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040063 mListener = listener;
64 mIWindowManager = WindowManagerGlobal.getWindowManagerService();
65 }
66
Jeff Brown62c82e42012-09-26 01:30:41 -070067 public void dump(PrintWriter pw) {
68 pw.println("Dreamland:");
69 if (mCurrentDream != null) {
70 pw.println(" mCurrentDream:");
71 pw.println(" mToken=" + mCurrentDream.mToken);
72 pw.println(" mName=" + mCurrentDream.mName);
73 pw.println(" mIsTest=" + mCurrentDream.mIsTest);
74 pw.println(" mUserId=" + mCurrentDream.mUserId);
75 pw.println(" mBound=" + mCurrentDream.mBound);
76 pw.println(" mService=" + mCurrentDream.mService);
77 pw.println(" mSentStartBroadcast=" + mCurrentDream.mSentStartBroadcast);
78 } else {
79 pw.println(" mCurrentDream: null");
80 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -040081 }
82
Jeff Brown62c82e42012-09-26 01:30:41 -070083 public void startDream(Binder token, ComponentName name, boolean isTest, int userId) {
84 stopDream();
85
John Spurlock591a9e82012-09-28 12:15:08 -040086 // Close the notification shade
87 mContext.sendBroadcast(mCloseNotificationShadeIntent);
88
Jeff Brown62c82e42012-09-26 01:30:41 -070089 Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", userId=" + userId);
90
91 mCurrentDream = new DreamRecord(token, name, isTest, userId);
92
93 try {
94 mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
95 } catch (RemoteException ex) {
96 Slog.e(TAG, "Unable to add window token for dream.", ex);
97 stopDream();
John Spurlockf4f6b4c2012-08-25 12:08:03 -040098 return;
99 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400100
Dianne Hackbornbe87e2f2012-09-28 16:31:34 -0700101 Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
Jeff Brown62c82e42012-09-26 01:30:41 -0700102 intent.setComponent(name);
103 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
104 try {
105 if (!mContext.bindService(intent, mCurrentDream,
106 Context.BIND_AUTO_CREATE, userId)) {
107 Slog.e(TAG, "Unable to bind dream service: " + intent);
108 stopDream();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400109 return;
110 }
Jeff Brown62c82e42012-09-26 01:30:41 -0700111 } catch (SecurityException ex) {
112 Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
113 stopDream();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400114 return;
115 }
116
Jeff Brown62c82e42012-09-26 01:30:41 -0700117 mCurrentDream.mBound = true;
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400118 }
119
Jeff Brown62c82e42012-09-26 01:30:41 -0700120 public void stopDream() {
121 if (mCurrentDream == null) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400122 return;
123 }
124
Jeff Brown62c82e42012-09-26 01:30:41 -0700125 final DreamRecord oldDream = mCurrentDream;
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400126 mCurrentDream = null;
Jeff Brown62c82e42012-09-26 01:30:41 -0700127 Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
128 + ", isTest=" + oldDream.mIsTest + ", userId=" + oldDream.mUserId);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400129
Jeff Brown62c82e42012-09-26 01:30:41 -0700130 if (oldDream.mSentStartBroadcast) {
131 mContext.sendBroadcast(mDreamingStoppedIntent);
132 }
133
134 if (oldDream.mService != null) {
Daniel Sandler2d784902012-10-03 23:04:50 -0400135 // Tell the dream that it's being stopped so that
136 // it can shut down nicely before we yank its window token out from
137 // under it.
138 try {
139 oldDream.mService.detach();
140 } catch (RemoteException ex) {
141 // we don't care; this thing is on the way out
142 }
143
Jeff Brown62c82e42012-09-26 01:30:41 -0700144 try {
145 oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
146 } catch (NoSuchElementException ex) {
147 // don't care
148 }
149 oldDream.mService = null;
150 }
151
152 if (oldDream.mBound) {
153 mContext.unbindService(oldDream);
154 }
155
156 try {
157 mIWindowManager.removeWindowToken(oldDream.mToken);
158 } catch (RemoteException ex) {
159 Slog.w(TAG, "Error removing window token for dream.", ex);
160 }
161
162 mHandler.post(new Runnable() {
163 @Override
164 public void run() {
165 mListener.onDreamStopped(oldDream.mToken);
166 }
167 });
168 }
169
170 private void attach(IDreamService service) {
171 try {
172 service.asBinder().linkToDeath(mCurrentDream, 0);
173 service.attach(mCurrentDream.mToken);
174 } catch (RemoteException ex) {
175 Slog.e(TAG, "The dream service died unexpectedly.", ex);
176 stopDream();
177 return;
178 }
179
180 mCurrentDream.mService = service;
181
182 if (!mCurrentDream.mIsTest) {
183 mContext.sendBroadcast(mDreamingStartedIntent);
184 mCurrentDream.mSentStartBroadcast = true;
185 }
186 }
187
188 /**
189 * Callback interface to be implemented by the {@link DreamManagerService}.
190 */
191 public interface Listener {
192 void onDreamStopped(Binder token);
193 }
194
195 private final class DreamRecord implements DeathRecipient, ServiceConnection {
196 public final Binder mToken;
197 public final ComponentName mName;
198 public final boolean mIsTest;
199 public final int mUserId;
200
201 public boolean mBound;
202 public IDreamService mService;
203 public boolean mSentStartBroadcast;
204
205 public DreamRecord(Binder token, ComponentName name,
206 boolean isTest, int userId) {
207 mToken = token;
208 mName = name;
209 mIsTest = isTest;
210 mUserId = userId;
211 }
212
213 // May be called on any thread.
214 @Override
215 public void binderDied() {
216 mHandler.post(new Runnable() {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400217 @Override
218 public void run() {
Jeff Brown62c82e42012-09-26 01:30:41 -0700219 mService = null;
220 if (mCurrentDream == DreamRecord.this) {
221 stopDream();
222 }
223 }
224 });
225 }
226
227 // May be called on any thread.
228 @Override
229 public void onServiceConnected(ComponentName name, final IBinder service) {
230 mHandler.post(new Runnable() {
231 @Override
232 public void run() {
233 if (mCurrentDream == DreamRecord.this && mService == null) {
234 attach(IDreamService.Stub.asInterface(service));
235 }
236 }
237 });
238 }
239
240 // May be called on any thread.
241 @Override
242 public void onServiceDisconnected(ComponentName name) {
243 mHandler.post(new Runnable() {
244 @Override
245 public void run() {
246 mService = null;
247 if (mCurrentDream == DreamRecord.this) {
248 stopDream();
249 }
250 }
251 });
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400252 }
253 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400254}