/services/java/com/android/server/dreams/DreamController.java

https://github.com/aizuzi/platform_frameworks_base · Java · 273 lines · 202 code · 38 blank · 33 comment · 23 complexity · b673fbdadcac0c864d654d20f66b1e24 MD5 · raw file

  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. package com.android.server.dreams;
  17. import android.content.ComponentName;
  18. import android.content.Context;
  19. import android.content.Intent;
  20. import android.content.ServiceConnection;
  21. import android.os.Binder;
  22. import android.os.Handler;
  23. import android.os.IBinder;
  24. import android.os.RemoteException;
  25. import android.os.IBinder.DeathRecipient;
  26. import android.os.UserHandle;
  27. import android.service.dreams.DreamService;
  28. import android.service.dreams.IDreamService;
  29. import android.util.Slog;
  30. import android.view.IWindowManager;
  31. import android.view.WindowManager;
  32. import android.view.WindowManagerGlobal;
  33. import java.io.PrintWriter;
  34. import java.util.NoSuchElementException;
  35. /**
  36. * Internal controller for starting and stopping the current dream and managing related state.
  37. *
  38. * Assumes all operations are called from the dream handler thread.
  39. */
  40. final class DreamController {
  41. private static final String TAG = "DreamController";
  42. // How long we wait for a newly bound dream to create the service connection
  43. private static final int DREAM_CONNECTION_TIMEOUT = 5 * 1000;
  44. private final Context mContext;
  45. private final Handler mHandler;
  46. private final Listener mListener;
  47. private final IWindowManager mIWindowManager;
  48. private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
  49. .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
  50. private final Intent mDreamingStoppedIntent = new Intent(Intent.ACTION_DREAMING_STOPPED)
  51. .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
  52. private final Intent mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
  53. private DreamRecord mCurrentDream;
  54. private final Runnable mStopUnconnectedDreamRunnable = new Runnable() {
  55. @Override
  56. public void run() {
  57. if (mCurrentDream != null && mCurrentDream.mBound && !mCurrentDream.mConnected) {
  58. Slog.w(TAG, "Bound dream did not connect in the time allotted");
  59. stopDream();
  60. }
  61. }
  62. };
  63. public DreamController(Context context, Handler handler, Listener listener) {
  64. mContext = context;
  65. mHandler = handler;
  66. mListener = listener;
  67. mIWindowManager = WindowManagerGlobal.getWindowManagerService();
  68. }
  69. public void dump(PrintWriter pw) {
  70. pw.println("Dreamland:");
  71. if (mCurrentDream != null) {
  72. pw.println(" mCurrentDream:");
  73. pw.println(" mToken=" + mCurrentDream.mToken);
  74. pw.println(" mName=" + mCurrentDream.mName);
  75. pw.println(" mIsTest=" + mCurrentDream.mIsTest);
  76. pw.println(" mUserId=" + mCurrentDream.mUserId);
  77. pw.println(" mBound=" + mCurrentDream.mBound);
  78. pw.println(" mService=" + mCurrentDream.mService);
  79. pw.println(" mSentStartBroadcast=" + mCurrentDream.mSentStartBroadcast);
  80. } else {
  81. pw.println(" mCurrentDream: null");
  82. }
  83. }
  84. public void startDream(Binder token, ComponentName name, boolean isTest, int userId) {
  85. stopDream();
  86. // Close the notification shade. Don't need to send to all, but better to be explicit.
  87. mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
  88. Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", userId=" + userId);
  89. mCurrentDream = new DreamRecord(token, name, isTest, userId);
  90. try {
  91. mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
  92. } catch (RemoteException ex) {
  93. Slog.e(TAG, "Unable to add window token for dream.", ex);
  94. stopDream();
  95. return;
  96. }
  97. Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
  98. intent.setComponent(name);
  99. intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
  100. try {
  101. if (!mContext.bindServiceAsUser(intent, mCurrentDream,
  102. Context.BIND_AUTO_CREATE, new UserHandle(userId))) {
  103. Slog.e(TAG, "Unable to bind dream service: " + intent);
  104. stopDream();
  105. return;
  106. }
  107. } catch (SecurityException ex) {
  108. Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
  109. stopDream();
  110. return;
  111. }
  112. mCurrentDream.mBound = true;
  113. mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
  114. }
  115. public void stopDream() {
  116. if (mCurrentDream == null) {
  117. return;
  118. }
  119. final DreamRecord oldDream = mCurrentDream;
  120. mCurrentDream = null;
  121. Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
  122. + ", isTest=" + oldDream.mIsTest + ", userId=" + oldDream.mUserId);
  123. mHandler.removeCallbacks(mStopUnconnectedDreamRunnable);
  124. if (oldDream.mSentStartBroadcast) {
  125. mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
  126. }
  127. if (oldDream.mService != null) {
  128. // Tell the dream that it's being stopped so that
  129. // it can shut down nicely before we yank its window token out from
  130. // under it.
  131. try {
  132. oldDream.mService.detach();
  133. } catch (RemoteException ex) {
  134. // we don't care; this thing is on the way out
  135. }
  136. try {
  137. oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
  138. } catch (NoSuchElementException ex) {
  139. // don't care
  140. }
  141. oldDream.mService = null;
  142. }
  143. if (oldDream.mBound) {
  144. mContext.unbindService(oldDream);
  145. }
  146. try {
  147. mIWindowManager.removeWindowToken(oldDream.mToken);
  148. } catch (RemoteException ex) {
  149. Slog.w(TAG, "Error removing window token for dream.", ex);
  150. }
  151. mHandler.post(new Runnable() {
  152. @Override
  153. public void run() {
  154. mListener.onDreamStopped(oldDream.mToken);
  155. }
  156. });
  157. }
  158. private void attach(IDreamService service) {
  159. try {
  160. service.asBinder().linkToDeath(mCurrentDream, 0);
  161. service.attach(mCurrentDream.mToken);
  162. } catch (RemoteException ex) {
  163. Slog.e(TAG, "The dream service died unexpectedly.", ex);
  164. stopDream();
  165. return;
  166. }
  167. mCurrentDream.mService = service;
  168. if (!mCurrentDream.mIsTest) {
  169. mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
  170. mCurrentDream.mSentStartBroadcast = true;
  171. }
  172. }
  173. /**
  174. * Callback interface to be implemented by the {@link DreamManagerService}.
  175. */
  176. public interface Listener {
  177. void onDreamStopped(Binder token);
  178. }
  179. private final class DreamRecord implements DeathRecipient, ServiceConnection {
  180. public final Binder mToken;
  181. public final ComponentName mName;
  182. public final boolean mIsTest;
  183. public final int mUserId;
  184. public boolean mBound;
  185. public boolean mConnected;
  186. public IDreamService mService;
  187. public boolean mSentStartBroadcast;
  188. public DreamRecord(Binder token, ComponentName name,
  189. boolean isTest, int userId) {
  190. mToken = token;
  191. mName = name;
  192. mIsTest = isTest;
  193. mUserId = userId;
  194. }
  195. // May be called on any thread.
  196. @Override
  197. public void binderDied() {
  198. mHandler.post(new Runnable() {
  199. @Override
  200. public void run() {
  201. mService = null;
  202. if (mCurrentDream == DreamRecord.this) {
  203. stopDream();
  204. }
  205. }
  206. });
  207. }
  208. // May be called on any thread.
  209. @Override
  210. public void onServiceConnected(ComponentName name, final IBinder service) {
  211. mHandler.post(new Runnable() {
  212. @Override
  213. public void run() {
  214. mConnected = true;
  215. if (mCurrentDream == DreamRecord.this && mService == null) {
  216. attach(IDreamService.Stub.asInterface(service));
  217. }
  218. }
  219. });
  220. }
  221. // May be called on any thread.
  222. @Override
  223. public void onServiceDisconnected(ComponentName name) {
  224. mHandler.post(new Runnable() {
  225. @Override
  226. public void run() {
  227. mService = null;
  228. if (mCurrentDream == DreamRecord.this) {
  229. stopDream();
  230. }
  231. }
  232. });
  233. }
  234. }
  235. }