diff options
author | John Spurlock <jspurlock@google.com> | 2012-09-20 05:45:53 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-09-20 05:45:54 -0700 |
commit | e0de5bfff2e74ee566ac2d053052de09aa25e54b (patch) | |
tree | 2fbae7d5235537036dd5ff47ffe5a42b4536d0b6 /core/java/android | |
parent | 8e356e089d4b9cf1cd0f31ceead666f8e75d4c28 (diff) | |
parent | f4f6b4c8b0fcf77d46567f13b409255948fe107b (diff) | |
download | frameworks_base-e0de5bfff2e74ee566ac2d053052de09aa25e54b.zip frameworks_base-e0de5bfff2e74ee566ac2d053052de09aa25e54b.tar.gz frameworks_base-e0de5bfff2e74ee566ac2d053052de09aa25e54b.tar.bz2 |
Merge "Fire "dreaming started" and "dreaming stopped" broadcasts." into jb-mr1-dev
Diffstat (limited to 'core/java/android')
-rw-r--r-- | core/java/android/service/dreams/Dream.java | 250 | ||||
-rw-r--r-- | core/java/android/service/dreams/DreamManagerService.java | 247 | ||||
-rw-r--r-- | core/java/android/service/dreams/IDreamManager.aidl | 2 |
3 files changed, 162 insertions, 337 deletions
diff --git a/core/java/android/service/dreams/Dream.java b/core/java/android/service/dreams/Dream.java index ba2ac67..4a23d39 100644 --- a/core/java/android/service/dreams/Dream.java +++ b/core/java/android/service/dreams/Dream.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.graphics.drawable.ColorDrawable; import android.os.Handler; import android.os.IBinder; -import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import android.view.ActionMode; @@ -40,12 +39,16 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.policy.PolicyManager; /** - * Extend this class to implement a custom screensaver. + * Extend this class to implement a custom Dream. + * + * <p>Dreams are interactive screensavers launched when a charging device is idle, or docked in a + * desk dock. Dreams provide another modality for apps to express themselves, tailored for + * an exhibition/lean-back experience.</p> */ public class Dream extends Service implements Window.Callback { private final static boolean DEBUG = true; - private final static String TAG = "Dream"; - + private final String TAG = Dream.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; + /** * The {@link Intent} that must be declared as handled by the service. * To be supported, the service must also require the @@ -60,28 +63,43 @@ public class Dream extends Service implements Window.Callback { public static final String METADATA_NAME_CONFIG_ACTIVITY = "android.service.dreams.config_activity"; - private Window mWindow; + /** + * Broadcast Action: Sent after the system starts dreaming. + * + * <p class="note">This is a protected intent that can only be sent by the system. + * It is only sent to registered receivers.</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DREAMING_STARTED = "android.intent.action.DREAMING_STARTED"; + /** + * Broadcast Action: Sent after the system stops dreaming. + * + * <p class="note">This is a protected intent that can only be sent by the system. + * It is only sent to registered receivers.</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DREAMING_STOPPED = "android.intent.action.DREAMING_STOPPED"; + + private final Handler mHandler = new Handler(); + private IBinder mWindowToken; + private Window mWindow; private WindowManager mWindowManager; private IDreamManager mSandman; - private boolean mInteractive; - - final Handler mHandler = new Handler(); - - boolean mFinished = false; + private boolean mFinished; // begin Window.Callback methods @Override public boolean dispatchKeyEvent(KeyEvent event) { // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on keyEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on keyEvent"); + safelyFinish(); return true; } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - if (DEBUG) Slog.v(TAG, "finishing on back key"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on back key"); + safelyFinish(); return true; } return mWindow.superDispatchKeyEvent(event); @@ -90,8 +108,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on keyShortcutEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on keyShortcutEvent"); + safelyFinish(); return true; } return mWindow.superDispatchKeyShortcutEvent(event); @@ -102,8 +120,8 @@ public class Dream extends Service implements Window.Callback { // TODO: create more flexible version of mInteractive that allows clicks // but finish()es on any other kind of activity if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on touchEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on touchEvent"); + safelyFinish(); return true; } return mWindow.superDispatchTouchEvent(event); @@ -112,8 +130,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchTrackballEvent(MotionEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on trackballEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on trackballEvent"); + safelyFinish(); return true; } return mWindow.superDispatchTrackballEvent(event); @@ -122,8 +140,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchGenericMotionEvent(MotionEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on genericMotionEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on genericMotionEvent"); + safelyFinish(); return true; } return mWindow.superDispatchGenericMotionEvent(event); @@ -212,30 +230,9 @@ public class Dream extends Service implements Window.Callback { public Window getWindow() { return mWindow; } - - /** - * Called when this Dream is constructed. Place your initialization here. - * - * Subclasses must call through to the superclass implementation. - */ - @Override - public void onCreate() { - super.onCreate(); - - if (DEBUG) Slog.v(TAG, "Dream created on thread " + Thread.currentThread().getId()); - - mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); - } - - /** - * Called when this Dream is started. - */ - public void onStart() { - // hook for subclasses - } /** - * Inflate a layout resource and set it to be the content view for this Dream. + * Inflates a layout resource and set it to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(int)}. * * @param layoutResID Resource ID to be inflated. @@ -248,7 +245,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Set a view to be the content view for this Dream. + * Sets a view to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)}, * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view. * @@ -262,7 +259,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Set a view to be the content view for this Dream. + * Sets a view to be the content view for this Dream. * Behaves similarly to * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}. * @@ -277,7 +274,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Add a view to the Dream's window, leaving other content views in place. + * Adds a view to the Dream's window, leaving other content views in place. * * @param view The desired content to display. * @param params Layout parameters for the view. @@ -285,21 +282,27 @@ public class Dream extends Service implements Window.Callback { public void addContentView(View view, ViewGroup.LayoutParams params) { getWindow().addContentView(view, params); } - + /** - * @param mInteractive the mInteractive to set + * Marks this dream as interactive to receive input events. + * + * <p>Non-interactive dreams (default) will dismiss on the first input event.</p> + * + * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p> + * + * @param interactive True if this dream will handle input events. */ - public void setInteractive(boolean mInteractive) { - this.mInteractive = mInteractive; + public void setInteractive(boolean interactive) { + mInteractive = interactive; } /** - * @return the mInteractive + * Returns whether or not this dream is interactive. */ public boolean isInteractive() { return mInteractive; } - + /** Convenience method for setting View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. */ protected void lightsOut() { // turn the lights down low @@ -319,14 +322,29 @@ public class Dream extends Service implements Window.Callback { public View findViewById(int id) { return getWindow().findViewById(id); } - + /** - * Called when this Dream is being removed from the screen and stopped. + * Called when this Dream is constructed. Place your initialization here. + * + * Subclasses must call through to the superclass implementation. */ @Override - public void onDestroy() { - super.onDestroy(); - mWindowManager.removeView(mWindow.getDecorView()); + public void onCreate() { + if (DEBUG) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId()); + super.onCreate(); + loadSandman(); + } + + /** + * Called when this Dream is started. + */ + public void onStart() { + // hook for subclasses + Slog.v(TAG, "called Dream.onStart()"); + } + + private void loadSandman() { + mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); } /** @@ -335,16 +353,21 @@ public class Dream extends Service implements Window.Callback { * @param windowToken Binder to attach to the window to allow access to the correct window type. * @hide */ - final /*package*/ void attach(IBinder windowToken) { - if (DEBUG) Slog.v(TAG, "Dream attached on thread " + Thread.currentThread().getId()); - + private final void attach(IBinder windowToken) { + if (DEBUG) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); + + if (mSandman == null) { + Slog.w(TAG, "No dream manager found, super.onCreate may not have been called"); + loadSandman(); + } + mWindowToken = windowToken; mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.requestFeature(Window.FEATURE_NO_TITLE); mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); - if (DEBUG) Slog.v(TAG, "attaching window token: " + windowToken - + " to window of type " + WindowManager.LayoutParams.TYPE_DREAM); + if (DEBUG) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", + windowToken, WindowManager.LayoutParams.TYPE_DREAM)); WindowManager.LayoutParams lp = mWindow.getAttributes(); lp.type = WindowManager.LayoutParams.TYPE_DREAM; @@ -355,58 +378,105 @@ public class Dream extends Service implements Window.Callback { | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON ); mWindow.setAttributes(lp); - - //WindowManagerImpl.getDefault().addView(mWindow.getDecorView(), lp); - - if (DEBUG) Slog.v(TAG, "created and attached window: " + mWindow); + + if (DEBUG) Slog.v(TAG, "Created and attached window: " + mWindow); mWindow.setWindowManager(null, windowToken, "dream", true); mWindowManager = mWindow.getWindowManager(); - - // now make it visible + + // now make it visible (on the ui thread) mHandler.post(new Runnable(){ @Override public void run() { - if (DEBUG) Slog.v(TAG, "Dream window added on thread " + Thread.currentThread().getId()); + if (DEBUG) Slog.v(TAG, "Window added on thread " + Thread.currentThread().getId()); - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + try { + getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + } catch (Throwable t) { + Slog.w("Crashed adding window view", t); + safelyFinish(); + return; + } // start it up - onStart(); + try { + onStart(); + } catch (Throwable t) { + Slog.w("Crashed in onStart()", t); + safelyFinish(); + } }}); } - + + private void safelyFinish() { + if (DEBUG) Slog.v(TAG, "safelyFinish()"); + try { + finish(); + } catch (Throwable t) { + Slog.w(TAG, "Crashed in safelyFinish()", t); + finishInternal(); + return; + } + + if (!mFinished) { + Slog.w(TAG, "Bad dream, did not call super.finish()"); + finishInternal(); + } + } + /** - * Stop the dream and wake up. - * - * After this method is called, the service will be stopped. + * Stops the dream, detaches from the window, and wakes up. + * + * Subclasses must call through to the superclass implementation. + * + * <p>After this method is called, the service will be stopped.</p> */ public void finish() { + if (DEBUG) Slog.v(TAG, "finish()"); + finishInternal(); + } + + private void finishInternal() { + if (DEBUG) Slog.v(TAG, "finishInternal() mFinished = " + mFinished); if (mFinished) return; try { - mSandman.awaken(); // assuming we were started by the DreamManager - stopSelf(); // if launched via any other means mFinished = true; - } catch (RemoteException ex) { - // sigh + + if (mSandman != null) { + mSandman.awakenSelf(mWindowToken); + } else { + Slog.w(TAG, "No dream manager found"); + } + stopSelf(); // if launched via any other means + + } catch (Throwable t) { + Slog.w(TAG, "Crashed in finishInternal()", t); } } - class IDreamServiceWrapper extends IDreamService.Stub { - public IDreamServiceWrapper() { - } + @Override + public void onDestroy() { + if (DEBUG) Slog.v(TAG, "onDestroy()"); + super.onDestroy(); - public void attach(IBinder windowToken) { - Dream.this.attach(windowToken); + if (DEBUG) Slog.v(TAG, "Removing window"); + try { + mWindowManager.removeView(mWindow.getDecorView()); + } catch (Throwable t) { + Slog.w(TAG, "Crashed removing window view", t); } } - /** - * Implement to return the implementation of the internal accessibility - * service interface. Subclasses should not override. - */ @Override public final IBinder onBind(Intent intent) { - return new IDreamServiceWrapper(); + if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent); + return new DreamServiceWrapper(); } + + private class DreamServiceWrapper extends IDreamService.Stub { + public void attach(IBinder windowToken) { + Dream.this.attach(windowToken); + } + } + } diff --git a/core/java/android/service/dreams/DreamManagerService.java b/core/java/android/service/dreams/DreamManagerService.java deleted file mode 100644 index 4aa1cbb..0000000 --- a/core/java/android/service/dreams/DreamManagerService.java +++ /dev/null @@ -1,247 +0,0 @@ -package android.service.dreams; - -import static android.provider.Settings.Secure.SCREENSAVER_COMPONENTS; -import static android.provider.Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT; -import java.io.FileDescriptor; -import java.io.PrintWriter; - -import android.app.ActivityManagerNative; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.os.Binder; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.Slog; -import android.view.IWindowManager; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; - -/** - * - * @hide - * - */ - -public class DreamManagerService - extends IDreamManager.Stub - implements ServiceConnection -{ - private static final boolean DEBUG = true; - private static final String TAG = "DreamManagerService"; - - final Object mLock = new Object[0]; - - private Context mContext; - private IWindowManager mIWindowManager; - - private ComponentName mCurrentDreamComponent; - private IDreamService mCurrentDream; - private Binder mCurrentDreamToken; - private int mCurrentUserId; - - public DreamManagerService(Context context) { - if (DEBUG) Slog.v(TAG, "DreamManagerService startup"); - mContext = context; - mIWindowManager = WindowManagerGlobal.getWindowManagerService(); - } - - private void checkPermission(String permission) { - if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) { - throw new SecurityException("Access denied to process: " + Binder.getCallingPid() - + ", must have permission " + permission); - } - } - - // IDreamManager method - @Override - public void dream() { - ComponentName[] dreams = getDreamComponentsForUser(mCurrentUserId); - ComponentName name = dreams != null && dreams.length > 0 ? dreams[0] : null; - if (name != null) { - synchronized (mLock) { - final long ident = Binder.clearCallingIdentity(); - try { - bindDreamComponentL(name, false); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - } - - // IDreamManager method - @Override - public void setDreamComponents(ComponentName[] componentNames) { - Settings.Secure.putStringForUser(mContext.getContentResolver(), - SCREENSAVER_COMPONENTS, - componentsToString(componentNames), - UserHandle.getCallingUserId()); - } - - private static String componentsToString(ComponentName[] componentNames) { - StringBuilder names = new StringBuilder(); - if (componentNames != null) { - for (ComponentName componentName : componentNames) { - if (names.length() > 0) - names.append(','); - names.append(componentName.flattenToString()); - } - } - return names.toString(); - } - - private static ComponentName[] componentsFromString(String names) { - String[] namesArray = names.split(","); - ComponentName[] componentNames = new ComponentName[namesArray.length]; - for (int i = 0; i < namesArray.length; i++) - componentNames[i] = ComponentName.unflattenFromString(namesArray[i]); - return componentNames; - } - - // IDreamManager method - @Override - public ComponentName[] getDreamComponents() { - return getDreamComponentsForUser(UserHandle.getCallingUserId()); - } - - private ComponentName[] getDreamComponentsForUser(int userId) { - String names = Settings.Secure.getStringForUser(mContext.getContentResolver(), - SCREENSAVER_COMPONENTS, - userId); - return names == null ? null : componentsFromString(names); - } - - // IDreamManager method - @Override - public ComponentName getDefaultDreamComponent() { - String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), - SCREENSAVER_DEFAULT_COMPONENT, - UserHandle.getCallingUserId()); - return name == null ? null : ComponentName.unflattenFromString(name); - } - - // IDreamManager method - @Override - public void testDream(ComponentName name) { - if (DEBUG) Slog.v(TAG, "startDream name=" + name - + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); -// checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); - synchronized (mLock) { - final long ident = Binder.clearCallingIdentity(); - try { - bindDreamComponentL(name, true); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - // IDreamManager method - @Override - public void awaken() { - if (DEBUG) Slog.v(TAG, "awaken()"); - synchronized (mLock) { - if (mCurrentDream != null) { - if (DEBUG) Slog.v(TAG, "disconnecting: " + mCurrentDreamComponent + " service: " + mCurrentDream); - mContext.unbindService(this); - mCurrentDream = null; - mCurrentDreamToken = null; - } - } - } - - // IDreamManager method - @Override - public boolean isDreaming() { - synchronized (mLock) { - return mCurrentDreamToken != null; - } - } - - public void bindDreamComponentL(ComponentName componentName, boolean test) { - if (DEBUG) Slog.v(TAG, "bindDreamComponent: componentName=" + componentName - + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); - - Intent intent = new Intent(Intent.ACTION_MAIN) - .setComponent(componentName) - .addFlags( - Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - ) - .putExtra("android.dreams.TEST", test); - - mCurrentDreamComponent = componentName; - 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. Proceed at your own risk."); - } - - if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) { - Slog.w(TAG, "unable to bind service: " + componentName); - } - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) Slog.v(TAG, "connected to dream: " + name + " binder=" + service + " thread=" + Thread.currentThread().getId()); - - mCurrentDream = IDreamService.Stub.asInterface(service); - try { - if (DEBUG) Slog.v(TAG, "attaching with token:" + mCurrentDreamToken); - mCurrentDream.attach(mCurrentDreamToken); - } catch (RemoteException ex) { - Slog.w(TAG, "Unable to send window token to dream:" + ex); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Slog.v(TAG, "disconnected: " + name + " service: " + mCurrentDream); - // Only happens in exceptional circumstances - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - pw.println("Dreamland:"); - pw.print(" component="); pw.println(mCurrentDreamComponent); - pw.print(" token="); pw.println(mCurrentDreamToken); - pw.print(" dream="); pw.println(mCurrentDream); - } - - public void systemReady() { - - // dream settings are kept per user, so keep track of current user - try { - mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id; - } catch (RemoteException e) { - Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); - } - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - if (DEBUG) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house"); - } - }}, filter); - - if (DEBUG) Slog.v(TAG, "ready to dream!"); - } - -} diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index b6fcdf0..bd1c524 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -19,6 +19,7 @@ package android.service.dreams; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.content.ComponentName; +import android.os.IBinder; /** @hide */ interface IDreamManager { @@ -29,4 +30,5 @@ interface IDreamManager { ComponentName getDefaultDreamComponent(); void testDream(in ComponentName componentName); boolean isDreaming(); + void awakenSelf(in IBinder token); }
\ No newline at end of file |