diff options
Diffstat (limited to 'services/java/com/android/server/DreamController.java')
| -rw-r--r-- | services/java/com/android/server/DreamController.java | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/services/java/com/android/server/DreamController.java b/services/java/com/android/server/DreamController.java new file mode 100644 index 0000000..498e581 --- /dev/null +++ b/services/java/com/android/server/DreamController.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.IBinder.DeathRecipient; +import android.service.dreams.IDreamService; +import android.util.Slog; +import android.view.IWindowManager; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import com.android.internal.util.DumpUtils; + +import java.io.PrintWriter; +import java.util.NoSuchElementException; + +/** + * Internal controller for starting and stopping the current dream and managing related state. + * + * Assumes all operations (except {@link #dump}) are called from a single thread. + */ +final class DreamController { + private static final boolean DEBUG = true; + private static final String TAG = DreamController.class.getSimpleName(); + + public interface Listener { + void onDreamStopped(boolean wasTest); + } + + private final Context mContext; + private final IWindowManager mIWindowManager; + private final DeathRecipient mDeathRecipient; + private final ServiceConnection mServiceConnection; + private final Listener mListener; + + private Handler mHandler; + + private ComponentName mCurrentDreamComponent; + private IDreamService mCurrentDream; + private Binder mCurrentDreamToken; + private boolean mCurrentDreamIsTest; + + public DreamController(Context context, DeathRecipient deathRecipient, + ServiceConnection serviceConnection, Listener listener) { + mContext = context; + mDeathRecipient = deathRecipient; + mServiceConnection = serviceConnection; + mListener = listener; + mIWindowManager = WindowManagerGlobal.getWindowManagerService(); + } + + public void setHandler(Handler handler) { + mHandler = handler; + } + + public void dump(PrintWriter pw) { + if (mHandler== null || pw == null) { + return; + } + DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() { + @Override + public void dump(PrintWriter pw) { + pw.print(" component="); pw.println(mCurrentDreamComponent); + pw.print(" token="); pw.println(mCurrentDreamToken); + pw.print(" dream="); pw.println(mCurrentDream); + } + }, pw, 200); + } + + public void start(ComponentName dream, boolean isTest) { + if (DEBUG) Slog.v(TAG, String.format("start(%s,%s)", dream, isTest)); + + if (mCurrentDreamComponent != null ) { + if (dream.equals(mCurrentDreamComponent) && isTest == mCurrentDreamIsTest) { + if (DEBUG) Slog.v(TAG, "Dream is already started: " + dream); + return; + } + // stop the current dream before starting a new one + stop(); + } + + mCurrentDreamComponent = dream; + mCurrentDreamIsTest = isTest; + mCurrentDreamToken = new Binder(); + + try { + if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken + + " for window type: " + WindowManager.LayoutParams.TYPE_DREAM); + mIWindowManager.addWindowToken(mCurrentDreamToken, + WindowManager.LayoutParams.TYPE_DREAM); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to add window token."); + stop(); + return; + } + + Intent intent = new Intent(Intent.ACTION_MAIN) + .setComponent(mCurrentDreamComponent) + .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + .putExtra("android.dreams.TEST", mCurrentDreamIsTest); + + if (!mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)) { + Slog.w(TAG, "Unable to bind service"); + stop(); + return; + } + if (DEBUG) Slog.v(TAG, "Bound service"); + } + + public void attach(ComponentName name, IBinder dream) { + if (DEBUG) Slog.v(TAG, String.format("attach(%s,%s)", name, dream)); + mCurrentDream = IDreamService.Stub.asInterface(dream); + + boolean linked = linkDeathRecipient(dream); + if (!linked) { + stop(); + return; + } + + try { + if (DEBUG) Slog.v(TAG, "Attaching with token:" + mCurrentDreamToken); + mCurrentDream.attach(mCurrentDreamToken); + } catch (Throwable ex) { + Slog.w(TAG, "Unable to send window token to dream:" + ex); + stop(); + } + } + + public void stop() { + if (DEBUG) Slog.v(TAG, "stop()"); + + if (mCurrentDream != null) { + unlinkDeathRecipient(mCurrentDream.asBinder()); + + if (DEBUG) Slog.v(TAG, "Unbinding: " + mCurrentDreamComponent + " service: " + mCurrentDream); + mContext.unbindService(mServiceConnection); + } + if (mCurrentDreamToken != null) { + removeWindowToken(mCurrentDreamToken); + } + + final boolean wasTest = mCurrentDreamIsTest; + mCurrentDream = null; + mCurrentDreamToken = null; + mCurrentDreamComponent = null; + mCurrentDreamIsTest = false; + + if (mListener != null && mHandler != null) { + mHandler.post(new Runnable(){ + @Override + public void run() { + mListener.onDreamStopped(wasTest); + }}); + } + } + + public void stopSelf(IBinder token) { + if (DEBUG) Slog.v(TAG, String.format("stopSelf(%s)", token)); + if (token == null || token != mCurrentDreamToken) { + Slog.w(TAG, "Stop requested for non-current dream token: " + token); + } else { + stop(); + } + } + + private void removeWindowToken(IBinder token) { + if (DEBUG) Slog.v(TAG, "Removing window token: " + token); + try { + mIWindowManager.removeWindowToken(token); + } catch (Throwable e) { + Slog.w(TAG, "Error removing window token", e); + } + } + + private boolean linkDeathRecipient(IBinder dream) { + if (DEBUG) Slog.v(TAG, "Linking death recipient"); + try { + dream.linkToDeath(mDeathRecipient, 0); + return true; + } catch (RemoteException e) { + Slog.w(TAG, "Unable to link death recipient", e); + return false; + } + } + + private void unlinkDeathRecipient(IBinder dream) { + if (DEBUG) Slog.v(TAG, "Unlinking death recipient"); + try { + dream.unlinkToDeath(mDeathRecipient, 0); + } catch (NoSuchElementException e) { + // we tried + } + } + +}
\ No newline at end of file |
