am e0de5bff: Merge "Fire "dreaming started" and "dreaming stopped" broadcasts." into jb-mr1-dev
* commit 'e0de5bfff2e74ee566ac2d053052de09aa25e54b': Fire "dreaming started" and "dreaming stopped" broadcasts.
This commit is contained in:
@@ -20289,6 +20289,8 @@ package android.service.dreams {
|
|||||||
method public void setContentView(android.view.View);
|
method public void setContentView(android.view.View);
|
||||||
method public void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
|
method public void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
|
||||||
method public void setInteractive(boolean);
|
method public void setInteractive(boolean);
|
||||||
|
field public static final java.lang.String ACTION_DREAMING_STARTED = "android.intent.action.DREAMING_STARTED";
|
||||||
|
field public static final java.lang.String ACTION_DREAMING_STOPPED = "android.intent.action.DREAMING_STOPPED";
|
||||||
field public static final java.lang.String METADATA_NAME_CONFIG_ACTIVITY = "android.service.dreams.config_activity";
|
field public static final java.lang.String METADATA_NAME_CONFIG_ACTIVITY = "android.service.dreams.config_activity";
|
||||||
field public static final java.lang.String SERVICE_INTERFACE = "android.service.dreams.Dream";
|
field public static final java.lang.String SERVICE_INTERFACE = "android.service.dreams.Dream";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import android.content.Intent;
|
|||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
@@ -40,12 +39,16 @@ import android.view.accessibility.AccessibilityEvent;
|
|||||||
import com.android.internal.policy.PolicyManager;
|
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 {
|
public class Dream extends Service implements Window.Callback {
|
||||||
private final static boolean DEBUG = true;
|
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.
|
* The {@link Intent} that must be declared as handled by the service.
|
||||||
* To be supported, the service must also require the
|
* 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 =
|
public static final String METADATA_NAME_CONFIG_ACTIVITY =
|
||||||
"android.service.dreams.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 WindowManager mWindowManager;
|
||||||
private IDreamManager mSandman;
|
private IDreamManager mSandman;
|
||||||
|
|
||||||
private boolean mInteractive;
|
private boolean mInteractive;
|
||||||
|
private boolean mFinished;
|
||||||
final Handler mHandler = new Handler();
|
|
||||||
|
|
||||||
boolean mFinished = false;
|
|
||||||
|
|
||||||
// begin Window.Callback methods
|
// begin Window.Callback methods
|
||||||
@Override
|
@Override
|
||||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
// TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK
|
// TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK
|
||||||
if (!mInteractive) {
|
if (!mInteractive) {
|
||||||
if (DEBUG) Slog.v(TAG, "finishing on keyEvent");
|
if (DEBUG) Slog.v(TAG, "Finishing on keyEvent");
|
||||||
finish();
|
safelyFinish();
|
||||||
return true;
|
return true;
|
||||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
} else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||||
if (DEBUG) Slog.v(TAG, "finishing on back key");
|
if (DEBUG) Slog.v(TAG, "Finishing on back key");
|
||||||
finish();
|
safelyFinish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return mWindow.superDispatchKeyEvent(event);
|
return mWindow.superDispatchKeyEvent(event);
|
||||||
@@ -90,8 +108,8 @@ public class Dream extends Service implements Window.Callback {
|
|||||||
@Override
|
@Override
|
||||||
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
|
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
|
||||||
if (!mInteractive) {
|
if (!mInteractive) {
|
||||||
if (DEBUG) Slog.v(TAG, "finishing on keyShortcutEvent");
|
if (DEBUG) Slog.v(TAG, "Finishing on keyShortcutEvent");
|
||||||
finish();
|
safelyFinish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return mWindow.superDispatchKeyShortcutEvent(event);
|
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
|
// TODO: create more flexible version of mInteractive that allows clicks
|
||||||
// but finish()es on any other kind of activity
|
// but finish()es on any other kind of activity
|
||||||
if (!mInteractive) {
|
if (!mInteractive) {
|
||||||
if (DEBUG) Slog.v(TAG, "finishing on touchEvent");
|
if (DEBUG) Slog.v(TAG, "Finishing on touchEvent");
|
||||||
finish();
|
safelyFinish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return mWindow.superDispatchTouchEvent(event);
|
return mWindow.superDispatchTouchEvent(event);
|
||||||
@@ -112,8 +130,8 @@ public class Dream extends Service implements Window.Callback {
|
|||||||
@Override
|
@Override
|
||||||
public boolean dispatchTrackballEvent(MotionEvent event) {
|
public boolean dispatchTrackballEvent(MotionEvent event) {
|
||||||
if (!mInteractive) {
|
if (!mInteractive) {
|
||||||
if (DEBUG) Slog.v(TAG, "finishing on trackballEvent");
|
if (DEBUG) Slog.v(TAG, "Finishing on trackballEvent");
|
||||||
finish();
|
safelyFinish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return mWindow.superDispatchTrackballEvent(event);
|
return mWindow.superDispatchTrackballEvent(event);
|
||||||
@@ -122,8 +140,8 @@ public class Dream extends Service implements Window.Callback {
|
|||||||
@Override
|
@Override
|
||||||
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||||
if (!mInteractive) {
|
if (!mInteractive) {
|
||||||
if (DEBUG) Slog.v(TAG, "finishing on genericMotionEvent");
|
if (DEBUG) Slog.v(TAG, "Finishing on genericMotionEvent");
|
||||||
finish();
|
safelyFinish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return mWindow.superDispatchGenericMotionEvent(event);
|
return mWindow.superDispatchGenericMotionEvent(event);
|
||||||
@@ -212,30 +230,9 @@ public class Dream extends Service implements Window.Callback {
|
|||||||
public Window getWindow() {
|
public Window getWindow() {
|
||||||
return mWindow;
|
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)}.
|
* Behaves similarly to {@link android.app.Activity#setContentView(int)}.
|
||||||
*
|
*
|
||||||
* @param layoutResID Resource ID to be inflated.
|
* @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)},
|
* 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.
|
* 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
|
* Behaves similarly to
|
||||||
* {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}.
|
* {@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 view The desired content to display.
|
||||||
* @param params Layout parameters for the view.
|
* @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) {
|
public void addContentView(View view, ViewGroup.LayoutParams params) {
|
||||||
getWindow().addContentView(view, 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) {
|
public void setInteractive(boolean interactive) {
|
||||||
this.mInteractive = mInteractive;
|
mInteractive = interactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the mInteractive
|
* Returns whether or not this dream is interactive.
|
||||||
*/
|
*/
|
||||||
public boolean isInteractive() {
|
public boolean isInteractive() {
|
||||||
return mInteractive;
|
return mInteractive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convenience method for setting View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. */
|
/** Convenience method for setting View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. */
|
||||||
protected void lightsOut() {
|
protected void lightsOut() {
|
||||||
// turn the lights down low
|
// turn the lights down low
|
||||||
@@ -319,14 +322,29 @@ public class Dream extends Service implements Window.Callback {
|
|||||||
public View findViewById(int id) {
|
public View findViewById(int id) {
|
||||||
return getWindow().findViewById(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
|
@Override
|
||||||
public void onDestroy() {
|
public void onCreate() {
|
||||||
super.onDestroy();
|
if (DEBUG) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId());
|
||||||
mWindowManager.removeView(mWindow.getDecorView());
|
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.
|
* @param windowToken Binder to attach to the window to allow access to the correct window type.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
final /*package*/ void attach(IBinder windowToken) {
|
private final void attach(IBinder windowToken) {
|
||||||
if (DEBUG) Slog.v(TAG, "Dream attached on thread " + Thread.currentThread().getId());
|
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 = PolicyManager.makeNewWindow(this);
|
||||||
mWindow.setCallback(this);
|
mWindow.setCallback(this);
|
||||||
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
|
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
|
||||||
mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
|
mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
|
||||||
|
|
||||||
if (DEBUG) Slog.v(TAG, "attaching window token: " + windowToken
|
if (DEBUG) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
|
||||||
+ " to window of type " + WindowManager.LayoutParams.TYPE_DREAM);
|
windowToken, WindowManager.LayoutParams.TYPE_DREAM));
|
||||||
|
|
||||||
WindowManager.LayoutParams lp = mWindow.getAttributes();
|
WindowManager.LayoutParams lp = mWindow.getAttributes();
|
||||||
lp.type = WindowManager.LayoutParams.TYPE_DREAM;
|
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
|
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
|
||||||
);
|
);
|
||||||
mWindow.setAttributes(lp);
|
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);
|
mWindow.setWindowManager(null, windowToken, "dream", true);
|
||||||
mWindowManager = mWindow.getWindowManager();
|
mWindowManager = mWindow.getWindowManager();
|
||||||
|
|
||||||
// now make it visible
|
// now make it visible (on the ui thread)
|
||||||
mHandler.post(new Runnable(){
|
mHandler.post(new Runnable(){
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
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
|
// start it up
|
||||||
onStart();
|
try {
|
||||||
|
onStart();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Slog.w("Crashed in onStart()", t);
|
||||||
|
safelyFinish();
|
||||||
|
}
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void safelyFinish() {
|
||||||
* Stop the dream and wake up.
|
if (DEBUG) Slog.v(TAG, "safelyFinish()");
|
||||||
*
|
|
||||||
* After this method is called, the service will be stopped.
|
|
||||||
*/
|
|
||||||
public void finish() {
|
|
||||||
if (mFinished) return;
|
|
||||||
try {
|
try {
|
||||||
mSandman.awaken(); // assuming we were started by the DreamManager
|
finish();
|
||||||
stopSelf(); // if launched via any other means
|
} catch (Throwable t) {
|
||||||
mFinished = true;
|
Slog.w(TAG, "Crashed in safelyFinish()", t);
|
||||||
} catch (RemoteException ex) {
|
finishInternal();
|
||||||
// sigh
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mFinished) {
|
||||||
|
Slog.w(TAG, "Bad dream, did not call super.finish()");
|
||||||
|
finishInternal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IDreamServiceWrapper extends IDreamService.Stub {
|
/**
|
||||||
public IDreamServiceWrapper() {
|
* 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 {
|
||||||
|
mFinished = true;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
if (DEBUG) Slog.v(TAG, "onDestroy()");
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
if (DEBUG) Slog.v(TAG, "Removing window");
|
||||||
|
try {
|
||||||
|
mWindowManager.removeView(mWindow.getDecorView());
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Slog.w(TAG, "Crashed removing window view", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final IBinder onBind(Intent intent) {
|
||||||
|
if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
|
||||||
|
return new DreamServiceWrapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DreamServiceWrapper extends IDreamService.Stub {
|
||||||
public void attach(IBinder windowToken) {
|
public void attach(IBinder windowToken) {
|
||||||
Dream.this.attach(windowToken);
|
Dream.this.attach(windowToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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!");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,7 @@ package android.service.dreams;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
interface IDreamManager {
|
interface IDreamManager {
|
||||||
@@ -29,4 +30,5 @@ interface IDreamManager {
|
|||||||
ComponentName getDefaultDreamComponent();
|
ComponentName getDefaultDreamComponent();
|
||||||
void testDream(in ComponentName componentName);
|
void testDream(in ComponentName componentName);
|
||||||
boolean isDreaming();
|
boolean isDreaming();
|
||||||
|
void awakenSelf(in IBinder token);
|
||||||
}
|
}
|
||||||
@@ -140,6 +140,9 @@
|
|||||||
|
|
||||||
<protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
|
<protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
|
||||||
|
|
||||||
|
<protected-broadcast android:name="android.intent.action.DREAMING_STARTED" />
|
||||||
|
<protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
|
||||||
|
|
||||||
<!-- ====================================== -->
|
<!-- ====================================== -->
|
||||||
<!-- Permissions for things that cost money -->
|
<!-- Permissions for things that cost money -->
|
||||||
<!-- ====================================== -->
|
<!-- ====================================== -->
|
||||||
@@ -1929,6 +1932,18 @@
|
|||||||
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
|
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
|
||||||
android:protectionLevel="signature|system" />
|
android:protectionLevel="signature|system" />
|
||||||
|
|
||||||
|
<!-- Allows applications to read dream settings and dream state.
|
||||||
|
@hide -->
|
||||||
|
<permission android:name="android.permission.READ_DREAM_STATE"
|
||||||
|
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
|
||||||
|
android:protectionLevel="signature" />
|
||||||
|
|
||||||
|
<!-- Allows applications to write dream settings, and start or stop dreaming.
|
||||||
|
@hide -->
|
||||||
|
<permission android:name="android.permission.WRITE_DREAM_STATE"
|
||||||
|
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
|
||||||
|
android:protectionLevel="signature" />
|
||||||
|
|
||||||
<!-- Allow an application to read and write the cache partition.
|
<!-- Allow an application to read and write the cache partition.
|
||||||
@hide -->
|
@hide -->
|
||||||
<permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
|
<permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
|
||||||
|
|||||||
@@ -56,6 +56,10 @@
|
|||||||
<uses-permission android:name="android.permission.SET_ORIENTATION" />
|
<uses-permission android:name="android.permission.SET_ORIENTATION" />
|
||||||
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
||||||
|
|
||||||
|
<!-- DreamManager -->
|
||||||
|
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="com.android.systemui.SystemUIApplication"
|
android:name="com.android.systemui.SystemUIApplication"
|
||||||
android:persistent="true"
|
android:persistent="true"
|
||||||
|
|||||||
217
services/java/com/android/server/DreamController.java
Normal file
217
services/java/com/android/server/DreamController.java
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
387
services/java/com/android/server/DreamManagerService.java
Normal file
387
services/java/com/android/server/DreamManagerService.java
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
/*
|
||||||
|
* 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 static android.provider.Settings.Secure.SCREENSAVER_COMPONENTS;
|
||||||
|
import static android.provider.Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT;
|
||||||
|
|
||||||
|
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.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.service.dreams.Dream;
|
||||||
|
import android.service.dreams.IDreamManager;
|
||||||
|
import android.util.Slog;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service api for managing dreams.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final class DreamManagerService
|
||||||
|
extends IDreamManager.Stub
|
||||||
|
implements ServiceConnection {
|
||||||
|
private static final boolean DEBUG = true;
|
||||||
|
private static final String TAG = DreamManagerService.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final Intent mDreamingStartedIntent = new Intent(Dream.ACTION_DREAMING_STARTED)
|
||||||
|
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
|
||||||
|
private static final Intent mDreamingStoppedIntent = new Intent(Dream.ACTION_DREAMING_STOPPED)
|
||||||
|
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
|
||||||
|
|
||||||
|
private final Object mLock = new Object();
|
||||||
|
private final DreamController mController;
|
||||||
|
private final DreamControllerHandler mHandler;
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
private final CurrentUserManager mCurrentUserManager = new CurrentUserManager();
|
||||||
|
|
||||||
|
private final DeathRecipient mAwakenOnBinderDeath = new DeathRecipient() {
|
||||||
|
@Override
|
||||||
|
public void binderDied() {
|
||||||
|
if (DEBUG) Slog.v(TAG, "binderDied()");
|
||||||
|
awaken();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final DreamController.Listener mControllerListener = new DreamController.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onDreamStopped(boolean wasTest) {
|
||||||
|
synchronized(mLock) {
|
||||||
|
setDreamingLocked(false, wasTest);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
private boolean mIsDreaming;
|
||||||
|
|
||||||
|
public DreamManagerService(Context context) {
|
||||||
|
if (DEBUG) Slog.v(TAG, "DreamManagerService startup");
|
||||||
|
mContext = context;
|
||||||
|
mController = new DreamController(context, mAwakenOnBinderDeath, this, mControllerListener);
|
||||||
|
mHandler = new DreamControllerHandler(mController);
|
||||||
|
mController.setHandler(mHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void systemReady() {
|
||||||
|
mCurrentUserManager.init(mContext);
|
||||||
|
|
||||||
|
if (DEBUG) Slog.v(TAG, "Ready to dream!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
|
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
|
||||||
|
|
||||||
|
pw.println("Dreamland:");
|
||||||
|
mController.dump(pw);
|
||||||
|
mCurrentUserManager.dump(pw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// begin IDreamManager api
|
||||||
|
@Override
|
||||||
|
public ComponentName[] getDreamComponents() {
|
||||||
|
checkPermission(android.Manifest.permission.READ_DREAM_STATE);
|
||||||
|
int userId = UserHandle.getCallingUserId();
|
||||||
|
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
return getDreamComponentsForUser(userId);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDreamComponents(ComponentName[] componentNames) {
|
||||||
|
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
|
||||||
|
int userId = UserHandle.getCallingUserId();
|
||||||
|
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
Settings.Secure.putStringForUser(mContext.getContentResolver(),
|
||||||
|
SCREENSAVER_COMPONENTS,
|
||||||
|
componentsToString(componentNames),
|
||||||
|
userId);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComponentName getDefaultDreamComponent() {
|
||||||
|
checkPermission(android.Manifest.permission.READ_DREAM_STATE);
|
||||||
|
int userId = UserHandle.getCallingUserId();
|
||||||
|
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
|
||||||
|
SCREENSAVER_DEFAULT_COMPONENT,
|
||||||
|
userId);
|
||||||
|
return name == null ? null : ComponentName.unflattenFromString(name);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDreaming() {
|
||||||
|
checkPermission(android.Manifest.permission.READ_DREAM_STATE);
|
||||||
|
|
||||||
|
return mIsDreaming;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dream() {
|
||||||
|
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
|
||||||
|
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
if (DEBUG) Slog.v(TAG, "Dream now");
|
||||||
|
ComponentName[] dreams = getDreamComponentsForUser(mCurrentUserManager.getCurrentUserId());
|
||||||
|
ComponentName firstDream = dreams != null && dreams.length > 0 ? dreams[0] : null;
|
||||||
|
if (firstDream != null) {
|
||||||
|
mHandler.requestStart(firstDream, false /*isTest*/);
|
||||||
|
synchronized (mLock) {
|
||||||
|
setDreamingLocked(true, false /*isTest*/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testDream(ComponentName dream) {
|
||||||
|
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
|
||||||
|
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
if (DEBUG) Slog.v(TAG, "Test dream name=" + dream);
|
||||||
|
if (dream != null) {
|
||||||
|
mHandler.requestStart(dream, true /*isTest*/);
|
||||||
|
synchronized (mLock) {
|
||||||
|
setDreamingLocked(true, true /*isTest*/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void awaken() {
|
||||||
|
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
|
||||||
|
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
if (DEBUG) Slog.v(TAG, "Wake up");
|
||||||
|
mHandler.requestStop();
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void awakenSelf(IBinder token) {
|
||||||
|
// requires no permission, called by Dream from an arbitrary process
|
||||||
|
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
if (DEBUG) Slog.v(TAG, "Wake up from dream: " + token);
|
||||||
|
if (token != null) {
|
||||||
|
mHandler.requestStopSelf(token);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end IDreamManager api
|
||||||
|
|
||||||
|
// begin ServiceConnection
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder dream) {
|
||||||
|
if (DEBUG) Slog.v(TAG, "Service connected: " + name + " binder=" +
|
||||||
|
dream + " thread=" + Thread.currentThread().getId());
|
||||||
|
mHandler.requestAttach(name, dream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
if (DEBUG) Slog.v(TAG, "Service disconnected: " + name);
|
||||||
|
// Only happens in exceptional circumstances, awaken just to be safe
|
||||||
|
awaken();
|
||||||
|
}
|
||||||
|
// end ServiceConnection
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDreamingLocked(boolean isDreaming, boolean isTest) {
|
||||||
|
boolean wasDreaming = mIsDreaming;
|
||||||
|
if (!isTest) {
|
||||||
|
if (!wasDreaming && isDreaming) {
|
||||||
|
if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STARTED");
|
||||||
|
mContext.sendBroadcast(mDreamingStartedIntent);
|
||||||
|
} else if (wasDreaming && !isDreaming) {
|
||||||
|
if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STOPPED");
|
||||||
|
mContext.sendBroadcast(mDreamingStoppedIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mIsDreaming = isDreaming;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComponentName[] getDreamComponentsForUser(int userId) {
|
||||||
|
String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
|
||||||
|
SCREENSAVER_COMPONENTS,
|
||||||
|
userId);
|
||||||
|
return names == null ? null : componentsFromString(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of the current user, since dream() uses the current user's configuration.
|
||||||
|
*/
|
||||||
|
private static class CurrentUserManager {
|
||||||
|
private final Object mLock = new Object();
|
||||||
|
private int mCurrentUserId;
|
||||||
|
|
||||||
|
public void init(Context context) {
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(Intent.ACTION_USER_SWITCHED);
|
||||||
|
context.registerReceiver(new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
|
||||||
|
synchronized(mLock) {
|
||||||
|
mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
|
||||||
|
if (DEBUG) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}, filter);
|
||||||
|
try {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dump(PrintWriter pw) {
|
||||||
|
pw.print(" user="); pw.println(getCurrentUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentUserId() {
|
||||||
|
synchronized(mLock) {
|
||||||
|
return mCurrentUserId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for asynchronous operations performed by the dream manager.
|
||||||
|
*
|
||||||
|
* Ensures operations to {@link DreamController} are single-threaded.
|
||||||
|
*/
|
||||||
|
private static final class DreamControllerHandler extends Handler {
|
||||||
|
private final DreamController mController;
|
||||||
|
private final Runnable mStopRunnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mController.stop();
|
||||||
|
}};
|
||||||
|
|
||||||
|
public DreamControllerHandler(DreamController controller) {
|
||||||
|
super(true /*async*/);
|
||||||
|
mController = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestStart(final ComponentName name, final boolean isTest) {
|
||||||
|
post(new Runnable(){
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mController.start(name, isTest);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestAttach(final ComponentName name, final IBinder dream) {
|
||||||
|
post(new Runnable(){
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mController.attach(name, dream);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestStopSelf(final IBinder token) {
|
||||||
|
post(new Runnable(){
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mController.stopSelf(token);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestStop() {
|
||||||
|
post(mStopRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,7 +38,6 @@ import android.os.StrictMode;
|
|||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.server.search.SearchManagerService;
|
import android.server.search.SearchManagerService;
|
||||||
import android.service.dreams.DreamManagerService;
|
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import android.os.ServiceManager;
|
|||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.WorkSource;
|
import android.os.WorkSource;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.service.dreams.Dream;
|
||||||
import android.service.dreams.IDreamManager;
|
import android.service.dreams.IDreamManager;
|
||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -98,6 +99,8 @@ public final class PowerManagerService extends IPowerManager.Stub
|
|||||||
private static final int DIRTY_STAY_ON = 1 << 7;
|
private static final int DIRTY_STAY_ON = 1 << 7;
|
||||||
// Dirty bit: battery state changed
|
// Dirty bit: battery state changed
|
||||||
private static final int DIRTY_BATTERY_STATE = 1 << 8;
|
private static final int DIRTY_BATTERY_STATE = 1 << 8;
|
||||||
|
// Dirty bit: dream ended
|
||||||
|
private static final int DIRTY_DREAM_ENDED = 1 << 9;
|
||||||
|
|
||||||
// Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
|
// Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
|
||||||
// The screen should be off or in the process of being turned off by the display controller.
|
// The screen should be off or in the process of being turned off by the display controller.
|
||||||
@@ -364,6 +367,10 @@ public final class PowerManagerService extends IPowerManager.Stub
|
|||||||
filter.addAction(Intent.ACTION_DOCK_EVENT);
|
filter.addAction(Intent.ACTION_DOCK_EVENT);
|
||||||
mContext.registerReceiver(new DockReceiver(), filter);
|
mContext.registerReceiver(new DockReceiver(), filter);
|
||||||
|
|
||||||
|
filter = new IntentFilter();
|
||||||
|
filter.addAction(Dream.ACTION_DREAMING_STOPPED);
|
||||||
|
mContext.registerReceiver(new DreamReceiver(), filter);
|
||||||
|
|
||||||
// Register for settings changes.
|
// Register for settings changes.
|
||||||
final ContentResolver resolver = mContext.getContentResolver();
|
final ContentResolver resolver = mContext.getContentResolver();
|
||||||
resolver.registerContentObserver(Settings.Secure.getUriFor(
|
resolver.registerContentObserver(Settings.Secure.getUriFor(
|
||||||
@@ -1146,8 +1153,12 @@ public final class PowerManagerService extends IPowerManager.Stub
|
|||||||
* Determines whether to post a message to the sandman to update the dream state.
|
* Determines whether to post a message to the sandman to update the dream state.
|
||||||
*/
|
*/
|
||||||
private void updateDreamLocked(int dirty) {
|
private void updateDreamLocked(int dirty) {
|
||||||
if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_SETTINGS
|
if ((dirty & (DIRTY_WAKEFULNESS
|
||||||
| DIRTY_IS_POWERED | DIRTY_STAY_ON | DIRTY_BATTERY_STATE)) != 0) {
|
| DIRTY_SETTINGS
|
||||||
|
| DIRTY_IS_POWERED
|
||||||
|
| DIRTY_STAY_ON
|
||||||
|
| DIRTY_BATTERY_STATE
|
||||||
|
| DIRTY_DREAM_ENDED)) != 0) {
|
||||||
scheduleSandmanLocked();
|
scheduleSandmanLocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1230,15 +1241,15 @@ public final class PowerManagerService extends IPowerManager.Stub
|
|||||||
handleDreamFinishedLocked();
|
handleDreamFinishedLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow the sandman to detect when the dream has ended.
|
// In addition to listening for the intent, poll the sandman periodically to detect
|
||||||
// FIXME: The DreamManagerService should tell us explicitly.
|
// when the dream has ended (as a watchdog only, ensuring our state is always correct).
|
||||||
if (mWakefulness == WAKEFULNESS_DREAMING
|
if (mWakefulness == WAKEFULNESS_DREAMING
|
||||||
|| mWakefulness == WAKEFULNESS_NAPPING) {
|
|| mWakefulness == WAKEFULNESS_NAPPING) {
|
||||||
if (!mSandmanScheduled) {
|
if (!mSandmanScheduled) {
|
||||||
mSandmanScheduled = true;
|
mSandmanScheduled = true;
|
||||||
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
|
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
|
||||||
msg.setAsynchronous(true);
|
msg.setAsynchronous(true);
|
||||||
mHandler.sendMessageDelayed(msg, 1000);
|
mHandler.sendMessageDelayed(msg, 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1472,6 +1483,11 @@ public final class PowerManagerService extends IPowerManager.Stub
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleDreamEndedLocked() {
|
||||||
|
mDirty |= DIRTY_DREAM_ENDED;
|
||||||
|
updatePowerStateLocked();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reboot the device immediately, passing 'reason' (may be null)
|
* Reboot the device immediately, passing 'reason' (may be null)
|
||||||
* to the underlying __reboot system call. Should not return.
|
* to the underlying __reboot system call. Should not return.
|
||||||
@@ -1937,6 +1953,15 @@ public final class PowerManagerService extends IPowerManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class DreamReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
handleDreamEndedLocked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class SettingsObserver extends ContentObserver {
|
private final class SettingsObserver extends ContentObserver {
|
||||||
public SettingsObserver(Handler handler) {
|
public SettingsObserver(Handler handler) {
|
||||||
super(handler);
|
super(handler);
|
||||||
|
|||||||
Reference in New Issue
Block a user