blob: 6db495a5964a72563f5c8d380c15732b030d2074 [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) {
135 // TODO: It would be nice to tell the dream that it's being stopped so that
136 // it can shut down nicely before we yank its window token out from under it.
137 try {
138 oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
139 } catch (NoSuchElementException ex) {
140 // don't care
141 }
142 oldDream.mService = null;
143 }
144
145 if (oldDream.mBound) {
146 mContext.unbindService(oldDream);
147 }
148
149 try {
150 mIWindowManager.removeWindowToken(oldDream.mToken);
151 } catch (RemoteException ex) {
152 Slog.w(TAG, "Error removing window token for dream.", ex);
153 }
154
155 mHandler.post(new Runnable() {
156 @Override
157 public void run() {
158 mListener.onDreamStopped(oldDream.mToken);
159 }
160 });
161 }
162
163 private void attach(IDreamService service) {
164 try {
165 service.asBinder().linkToDeath(mCurrentDream, 0);
166 service.attach(mCurrentDream.mToken);
167 } catch (RemoteException ex) {
168 Slog.e(TAG, "The dream service died unexpectedly.", ex);
169 stopDream();
170 return;
171 }
172
173 mCurrentDream.mService = service;
174
175 if (!mCurrentDream.mIsTest) {
176 mContext.sendBroadcast(mDreamingStartedIntent);
177 mCurrentDream.mSentStartBroadcast = true;
178 }
179 }
180
181 /**
182 * Callback interface to be implemented by the {@link DreamManagerService}.
183 */
184 public interface Listener {
185 void onDreamStopped(Binder token);
186 }
187
188 private final class DreamRecord implements DeathRecipient, ServiceConnection {
189 public final Binder mToken;
190 public final ComponentName mName;
191 public final boolean mIsTest;
192 public final int mUserId;
193
194 public boolean mBound;
195 public IDreamService mService;
196 public boolean mSentStartBroadcast;
197
198 public DreamRecord(Binder token, ComponentName name,
199 boolean isTest, int userId) {
200 mToken = token;
201 mName = name;
202 mIsTest = isTest;
203 mUserId = userId;
204 }
205
206 // May be called on any thread.
207 @Override
208 public void binderDied() {
209 mHandler.post(new Runnable() {
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400210 @Override
211 public void run() {
Jeff Brown62c82e42012-09-26 01:30:41 -0700212 mService = null;
213 if (mCurrentDream == DreamRecord.this) {
214 stopDream();
215 }
216 }
217 });
218 }
219
220 // May be called on any thread.
221 @Override
222 public void onServiceConnected(ComponentName name, final IBinder service) {
223 mHandler.post(new Runnable() {
224 @Override
225 public void run() {
226 if (mCurrentDream == DreamRecord.this && mService == null) {
227 attach(IDreamService.Stub.asInterface(service));
228 }
229 }
230 });
231 }
232
233 // May be called on any thread.
234 @Override
235 public void onServiceDisconnected(ComponentName name) {
236 mHandler.post(new Runnable() {
237 @Override
238 public void run() {
239 mService = null;
240 if (mCurrentDream == DreamRecord.this) {
241 stopDream();
242 }
243 }
244 });
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400245 }
246 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400247}