DreamController.java revision 591a9e8d6ef2cab3ab3a701bd6279b6c12e6e4c6
1/* 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.dreams; 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.Dream; 29import android.service.dreams.IDreamService; 30import android.util.Slog; 31import android.view.IWindowManager; 32import android.view.WindowManager; 33import android.view.WindowManagerGlobal; 34 35import java.io.PrintWriter; 36import java.util.NoSuchElementException; 37 38/** 39 * Internal controller for starting and stopping the current dream and managing related state. 40 * 41 * Assumes all operations are called from the dream handler thread. 42 */ 43final class DreamController { 44 private static final String TAG = "DreamController"; 45 46 private final Context mContext; 47 private final Handler mHandler; 48 private final Listener mListener; 49 private final IWindowManager mIWindowManager; 50 51 private final Intent mDreamingStartedIntent = new Intent(Dream.ACTION_DREAMING_STARTED) 52 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 53 private final Intent mDreamingStoppedIntent = new Intent(Dream.ACTION_DREAMING_STOPPED) 54 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 55 56 private final Intent mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 57 58 private DreamRecord mCurrentDream; 59 60 public DreamController(Context context, Handler handler, Listener listener) { 61 mContext = context; 62 mHandler = handler; 63 mListener = listener; 64 mIWindowManager = WindowManagerGlobal.getWindowManagerService(); 65 } 66 67 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 } 81 } 82 83 public void startDream(Binder token, ComponentName name, boolean isTest, int userId) { 84 stopDream(); 85 86 // Close the notification shade 87 mContext.sendBroadcast(mCloseNotificationShadeIntent); 88 89 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(); 98 return; 99 } 100 101 Intent intent = new Intent(Intent.ACTION_MAIN); 102 intent.addCategory(Dream.CATEGORY_DREAM); 103 intent.setComponent(name); 104 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 105 try { 106 if (!mContext.bindService(intent, mCurrentDream, 107 Context.BIND_AUTO_CREATE, userId)) { 108 Slog.e(TAG, "Unable to bind dream service: " + intent); 109 stopDream(); 110 return; 111 } 112 } catch (SecurityException ex) { 113 Slog.e(TAG, "Unable to bind dream service: " + intent, ex); 114 stopDream(); 115 return; 116 } 117 118 mCurrentDream.mBound = true; 119 } 120 121 public void stopDream() { 122 if (mCurrentDream == null) { 123 return; 124 } 125 126 final DreamRecord oldDream = mCurrentDream; 127 mCurrentDream = null; 128 Slog.i(TAG, "Stopping dream: name=" + oldDream.mName 129 + ", isTest=" + oldDream.mIsTest + ", userId=" + oldDream.mUserId); 130 131 if (oldDream.mSentStartBroadcast) { 132 mContext.sendBroadcast(mDreamingStoppedIntent); 133 } 134 135 if (oldDream.mService != null) { 136 // TODO: It would be nice to tell the dream that it's being stopped so that 137 // it can shut down nicely before we yank its window token out from under it. 138 try { 139 oldDream.mService.asBinder().unlinkToDeath(oldDream, 0); 140 } catch (NoSuchElementException ex) { 141 // don't care 142 } 143 oldDream.mService = null; 144 } 145 146 if (oldDream.mBound) { 147 mContext.unbindService(oldDream); 148 } 149 150 try { 151 mIWindowManager.removeWindowToken(oldDream.mToken); 152 } catch (RemoteException ex) { 153 Slog.w(TAG, "Error removing window token for dream.", ex); 154 } 155 156 mHandler.post(new Runnable() { 157 @Override 158 public void run() { 159 mListener.onDreamStopped(oldDream.mToken); 160 } 161 }); 162 } 163 164 private void attach(IDreamService service) { 165 try { 166 service.asBinder().linkToDeath(mCurrentDream, 0); 167 service.attach(mCurrentDream.mToken); 168 } catch (RemoteException ex) { 169 Slog.e(TAG, "The dream service died unexpectedly.", ex); 170 stopDream(); 171 return; 172 } 173 174 mCurrentDream.mService = service; 175 176 if (!mCurrentDream.mIsTest) { 177 mContext.sendBroadcast(mDreamingStartedIntent); 178 mCurrentDream.mSentStartBroadcast = true; 179 } 180 } 181 182 /** 183 * Callback interface to be implemented by the {@link DreamManagerService}. 184 */ 185 public interface Listener { 186 void onDreamStopped(Binder token); 187 } 188 189 private final class DreamRecord implements DeathRecipient, ServiceConnection { 190 public final Binder mToken; 191 public final ComponentName mName; 192 public final boolean mIsTest; 193 public final int mUserId; 194 195 public boolean mBound; 196 public IDreamService mService; 197 public boolean mSentStartBroadcast; 198 199 public DreamRecord(Binder token, ComponentName name, 200 boolean isTest, int userId) { 201 mToken = token; 202 mName = name; 203 mIsTest = isTest; 204 mUserId = userId; 205 } 206 207 // May be called on any thread. 208 @Override 209 public void binderDied() { 210 mHandler.post(new Runnable() { 211 @Override 212 public void run() { 213 mService = null; 214 if (mCurrentDream == DreamRecord.this) { 215 stopDream(); 216 } 217 } 218 }); 219 } 220 221 // May be called on any thread. 222 @Override 223 public void onServiceConnected(ComponentName name, final IBinder service) { 224 mHandler.post(new Runnable() { 225 @Override 226 public void run() { 227 if (mCurrentDream == DreamRecord.this && mService == null) { 228 attach(IDreamService.Stub.asInterface(service)); 229 } 230 } 231 }); 232 } 233 234 // May be called on any thread. 235 @Override 236 public void onServiceDisconnected(ComponentName name) { 237 mHandler.post(new Runnable() { 238 @Override 239 public void run() { 240 mService = null; 241 if (mCurrentDream == DreamRecord.this) { 242 stopDream(); 243 } 244 } 245 }); 246 } 247 } 248}