blob: 498e581b185442cb237d44d71255ae2c1dcaf1c2 [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
17package com.android.server;
18
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;
28import android.service.dreams.IDreamService;
29import android.util.Slog;
30import android.view.IWindowManager;
31import android.view.WindowManager;
32import android.view.WindowManagerGlobal;
33
34import com.android.internal.util.DumpUtils;
35
36import java.io.PrintWriter;
37import java.util.NoSuchElementException;
38
39/**
40 * Internal controller for starting and stopping the current dream and managing related state.
41 *
42 * Assumes all operations (except {@link #dump}) are called from a single thread.
43 */
44final class DreamController {
45 private static final boolean DEBUG = true;
46 private static final String TAG = DreamController.class.getSimpleName();
47
48 public interface Listener {
49 void onDreamStopped(boolean wasTest);
50 }
51
52 private final Context mContext;
53 private final IWindowManager mIWindowManager;
54 private final DeathRecipient mDeathRecipient;
55 private final ServiceConnection mServiceConnection;
56 private final Listener mListener;
57
58 private Handler mHandler;
59
60 private ComponentName mCurrentDreamComponent;
61 private IDreamService mCurrentDream;
62 private Binder mCurrentDreamToken;
63 private boolean mCurrentDreamIsTest;
64
65 public DreamController(Context context, DeathRecipient deathRecipient,
66 ServiceConnection serviceConnection, Listener listener) {
67 mContext = context;
68 mDeathRecipient = deathRecipient;
69 mServiceConnection = serviceConnection;
70 mListener = listener;
71 mIWindowManager = WindowManagerGlobal.getWindowManagerService();
72 }
73
74 public void setHandler(Handler handler) {
75 mHandler = handler;
76 }
77
78 public void dump(PrintWriter pw) {
79 if (mHandler== null || pw == null) {
80 return;
81 }
82 DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
83 @Override
84 public void dump(PrintWriter pw) {
85 pw.print(" component="); pw.println(mCurrentDreamComponent);
86 pw.print(" token="); pw.println(mCurrentDreamToken);
87 pw.print(" dream="); pw.println(mCurrentDream);
88 }
89 }, pw, 200);
90 }
91
92 public void start(ComponentName dream, boolean isTest) {
93 if (DEBUG) Slog.v(TAG, String.format("start(%s,%s)", dream, isTest));
94
95 if (mCurrentDreamComponent != null ) {
96 if (dream.equals(mCurrentDreamComponent) && isTest == mCurrentDreamIsTest) {
97 if (DEBUG) Slog.v(TAG, "Dream is already started: " + dream);
98 return;
99 }
100 // stop the current dream before starting a new one
101 stop();
102 }
103
104 mCurrentDreamComponent = dream;
105 mCurrentDreamIsTest = isTest;
106 mCurrentDreamToken = new Binder();
107
108 try {
109 if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken
110 + " for window type: " + WindowManager.LayoutParams.TYPE_DREAM);
111 mIWindowManager.addWindowToken(mCurrentDreamToken,
112 WindowManager.LayoutParams.TYPE_DREAM);
113 } catch (RemoteException e) {
114 Slog.w(TAG, "Unable to add window token.");
115 stop();
116 return;
117 }
118
119 Intent intent = new Intent(Intent.ACTION_MAIN)
120 .setComponent(mCurrentDreamComponent)
121 .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
122 .putExtra("android.dreams.TEST", mCurrentDreamIsTest);
123
124 if (!mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
125 Slog.w(TAG, "Unable to bind service");
126 stop();
127 return;
128 }
129 if (DEBUG) Slog.v(TAG, "Bound service");
130 }
131
132 public void attach(ComponentName name, IBinder dream) {
133 if (DEBUG) Slog.v(TAG, String.format("attach(%s,%s)", name, dream));
134 mCurrentDream = IDreamService.Stub.asInterface(dream);
135
136 boolean linked = linkDeathRecipient(dream);
137 if (!linked) {
138 stop();
139 return;
140 }
141
142 try {
143 if (DEBUG) Slog.v(TAG, "Attaching with token:" + mCurrentDreamToken);
144 mCurrentDream.attach(mCurrentDreamToken);
145 } catch (Throwable ex) {
146 Slog.w(TAG, "Unable to send window token to dream:" + ex);
147 stop();
148 }
149 }
150
151 public void stop() {
152 if (DEBUG) Slog.v(TAG, "stop()");
153
154 if (mCurrentDream != null) {
155 unlinkDeathRecipient(mCurrentDream.asBinder());
156
157 if (DEBUG) Slog.v(TAG, "Unbinding: " + mCurrentDreamComponent + " service: " + mCurrentDream);
158 mContext.unbindService(mServiceConnection);
159 }
160 if (mCurrentDreamToken != null) {
161 removeWindowToken(mCurrentDreamToken);
162 }
163
164 final boolean wasTest = mCurrentDreamIsTest;
165 mCurrentDream = null;
166 mCurrentDreamToken = null;
167 mCurrentDreamComponent = null;
168 mCurrentDreamIsTest = false;
169
170 if (mListener != null && mHandler != null) {
171 mHandler.post(new Runnable(){
172 @Override
173 public void run() {
174 mListener.onDreamStopped(wasTest);
175 }});
176 }
177 }
178
179 public void stopSelf(IBinder token) {
180 if (DEBUG) Slog.v(TAG, String.format("stopSelf(%s)", token));
181 if (token == null || token != mCurrentDreamToken) {
182 Slog.w(TAG, "Stop requested for non-current dream token: " + token);
183 } else {
184 stop();
185 }
186 }
187
188 private void removeWindowToken(IBinder token) {
189 if (DEBUG) Slog.v(TAG, "Removing window token: " + token);
190 try {
191 mIWindowManager.removeWindowToken(token);
192 } catch (Throwable e) {
193 Slog.w(TAG, "Error removing window token", e);
194 }
195 }
196
197 private boolean linkDeathRecipient(IBinder dream) {
198 if (DEBUG) Slog.v(TAG, "Linking death recipient");
199 try {
200 dream.linkToDeath(mDeathRecipient, 0);
201 return true;
202 } catch (RemoteException e) {
203 Slog.w(TAG, "Unable to link death recipient", e);
204 return false;
205 }
206 }
207
208 private void unlinkDeathRecipient(IBinder dream) {
209 if (DEBUG) Slog.v(TAG, "Unlinking death recipient");
210 try {
211 dream.unlinkToDeath(mDeathRecipient, 0);
212 } catch (NoSuchElementException e) {
213 // we tried
214 }
215 }
216
217}