PageRenderTime 42ms CodeModel.GetById 10ms app.highlight 25ms RepoModel.GetById 2ms app.codeStats 0ms

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