Fix wake lock logic during Dream startup

Fixes a crash that would happen in all dreams
that did not have permission to acquire wake locks.
Instead moves the wake lock logic into the system
process. Also fixes a bug in DozeService where the
wake lock was not held until dozing was actually
properly initialized.

Fixes: 31612287
Bug: 31044352
Related-CL: I85955a2b7d6bad5171accbc336117a9660b1b198
Test: adb shell settings put secure screensaver_components com.android.dreams.basic/.Colors; adb shell service call dreams 1

Change-Id: Idb3f921ee71b6da6c2ab0c44c332ef91f93ddbc0
This commit is contained in:
Adrian Roos
2016-09-23 14:08:09 -07:00
parent 6f300c1729
commit 9ede1d2602
4 changed files with 130 additions and 131 deletions

View File

@@ -27,6 +27,7 @@ import android.graphics.PixelFormat;
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.IRemoteCallback;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
@@ -192,9 +193,6 @@ public class DreamService extends Service implements Window.Callback {
private boolean mDebug = false; private boolean mDebug = false;
private PowerManager.WakeLock mWakeLock;
private boolean mWakeLockAcquired;
public DreamService() { public DreamService() {
mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
} }
@@ -789,8 +787,6 @@ public class DreamService extends Service implements Window.Callback {
public void onCreate() { public void onCreate() {
if (mDebug) Slog.v(TAG, "onCreate()"); if (mDebug) Slog.v(TAG, "onCreate()");
super.onCreate(); super.onCreate();
mWakeLock = getSystemService(PowerManager.class)
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DreamService");
} }
/** /**
@@ -830,21 +826,9 @@ public class DreamService extends Service implements Window.Callback {
@Override @Override
public final IBinder onBind(Intent intent) { public final IBinder onBind(Intent intent) {
if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
// Need to stay awake until we dispatch onDreamingStarted. This is released either in
// attach() or onDestroy().
mWakeLock.acquire(5000);
mWakeLockAcquired = true;
return new DreamServiceWrapper(); return new DreamServiceWrapper();
} }
private void releaseWakeLockIfNeeded() {
if (mWakeLockAcquired) {
mWakeLock.release();
mWakeLockAcquired = false;
}
}
/** /**
* Stops the dream and detaches from the window. * Stops the dream and detaches from the window.
* <p> * <p>
@@ -921,8 +905,6 @@ public class DreamService extends Service implements Window.Callback {
detach(); detach();
super.onDestroy(); super.onDestroy();
releaseWakeLockIfNeeded(); // for acquire in onBind()
} }
// end public api // end public api
@@ -961,9 +943,9 @@ public class DreamService extends Service implements Window.Callback {
* Must run on mHandler. * Must run on mHandler.
* *
* @param windowToken A window token that will allow a window to be created in the correct layer. * @param windowToken A window token that will allow a window to be created in the correct layer.
* @param started A callback that will be invoked once onDreamingStarted has completed.
*/ */
private final void attach(IBinder windowToken, boolean canDoze) { private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) {
try {
if (mWindowToken != null) { if (mWindowToken != null) {
Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken);
return; return;
@@ -990,10 +972,8 @@ public class DreamService extends Service implements Window.Callback {
mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
mWindow.setFormat(PixelFormat.OPAQUE); mWindow.setFormat(PixelFormat.OPAQUE);
if (mDebug) { if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
windowToken, 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;
@@ -1033,19 +1013,25 @@ public class DreamService extends Service implements Window.Callback {
// which is posted to the handler by addView, so we post onDreamingStarted // which is posted to the handler by addView, so we post onDreamingStarted
// to the handler also. Need to watch out here in case detach occurs before // to the handler also. Need to watch out here in case detach occurs before
// this callback is invoked. // this callback is invoked.
mHandler.post(mWakeLock.wrap(() -> { mHandler.post(new Runnable() {
@Override
public void run() {
if (mWindow != null || mWindowless) { if (mWindow != null || mWindowless) {
if (mDebug) { if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()");
Slog.v(TAG, "Calling onDreamingStarted()");
}
mStarted = true; mStarted = true;
try {
onDreamingStarted(); onDreamingStarted();
}
}));
} finally { } finally {
releaseWakeLockIfNeeded(); // for acquire in onBind try {
started.sendResult(null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} }
} }
}
}
});
}
private boolean getWindowFlagValue(int flag, boolean defaultValue) { private boolean getWindowFlagValue(int flag, boolean defaultValue) {
return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0; return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0;
@@ -1116,11 +1102,12 @@ public class DreamService extends Service implements Window.Callback {
private final class DreamServiceWrapper extends IDreamService.Stub { private final class DreamServiceWrapper extends IDreamService.Stub {
@Override @Override
public void attach(final IBinder windowToken, final boolean canDoze) { public void attach(final IBinder windowToken, final boolean canDoze,
IRemoteCallback started) {
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
DreamService.this.attach(windowToken, canDoze); DreamService.this.attach(windowToken, canDoze, started);
} }
}); });
} }

View File

@@ -16,11 +16,13 @@
package android.service.dreams; package android.service.dreams;
import android.os.IRemoteCallback;
/** /**
* @hide * @hide
*/ */
oneway interface IDreamService { oneway interface IDreamService {
void attach(IBinder windowToken, boolean canDoze); void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started);
void detach(); void detach();
void wakeUp(); void wakeUp();
} }

View File

@@ -159,9 +159,7 @@ public class DozeService extends DreamService {
// Ask the host to get things ready to start dozing. // Ask the host to get things ready to start dozing.
// Once ready, we call startDozing() at which point the CPU may suspend // Once ready, we call startDozing() at which point the CPU may suspend
// and we will need to acquire a wakelock to do work. // and we will need to acquire a wakelock to do work.
mHost.startDozing(new Runnable() { mHost.startDozing(mWakeLock.wrap(() -> {
@Override
public void run() {
if (mDreaming) { if (mDreaming) {
startDozing(); startDozing();
@@ -169,8 +167,7 @@ public class DozeService extends DreamService {
// wakelock whenever we are doing work. Note that we never call // wakelock whenever we are doing work. Note that we never call
// stopDozing because can we just keep dozing until the bitter end. // stopDozing because can we just keep dozing until the bitter end.
} }
} }));
});
} }
@Override @Override

View File

@@ -24,8 +24,10 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.IBinder.DeathRecipient; import android.os.IBinder.DeathRecipient;
@@ -253,7 +255,8 @@ final class DreamController {
private void attach(IDreamService service) { private void attach(IDreamService service) {
try { try {
service.asBinder().linkToDeath(mCurrentDream, 0); service.asBinder().linkToDeath(mCurrentDream, 0);
service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze); service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze,
mCurrentDream.mDreamingStartedCallback);
} catch (RemoteException ex) { } catch (RemoteException ex) {
Slog.e(TAG, "The dream service died unexpectedly.", ex); Slog.e(TAG, "The dream service died unexpectedly.", ex);
stopDream(true /*immediate*/); stopDream(true /*immediate*/);
@@ -298,10 +301,10 @@ final class DreamController {
mCanDoze = canDoze; mCanDoze = canDoze;
mUserId = userId; mUserId = userId;
mWakeLock = wakeLock; mWakeLock = wakeLock;
// Hold the lock while we're waiting for the service to connect. Released either when // Hold the lock while we're waiting for the service to connect and start dreaming.
// DreamService connects (and is then responsible for keeping the device awake) or // Released after the service has started dreaming, we stop dreaming, or it timed out.
// dreaming stops.
mWakeLock.acquire(); mWakeLock.acquire();
mHandler.postDelayed(mReleaseWakeLockIfNeeded, 10000);
} }
// May be called on any thread. // May be called on any thread.
@@ -324,25 +327,17 @@ final class DreamController {
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
try {
mConnected = true; mConnected = true;
if (mCurrentDream == DreamRecord.this && mService == null) { if (mCurrentDream == DreamRecord.this && mService == null) {
attach(IDreamService.Stub.asInterface(service)); attach(IDreamService.Stub.asInterface(service));
} // Wake lock will be released once dreaming starts.
} finally { } else {
releaseWakeLockIfNeeded(); releaseWakeLockIfNeeded();
} }
} }
}); });
} }
private void releaseWakeLockIfNeeded() {
if (mWakeLock != null) {
mWakeLock.release();
mWakeLock = null;
}
}
// May be called on any thread. // May be called on any thread.
@Override @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
@@ -356,5 +351,23 @@ final class DreamController {
} }
}); });
} }
void releaseWakeLockIfNeeded() {
if (mWakeLock != null) {
mWakeLock.release();
mWakeLock = null;
mHandler.removeCallbacks(mReleaseWakeLockIfNeeded);
}
}
final Runnable mReleaseWakeLockIfNeeded = this::releaseWakeLockIfNeeded;
final IRemoteCallback mDreamingStartedCallback = new IRemoteCallback.Stub() {
// May be called on any thread.
@Override
public void sendResult(Bundle data) throws RemoteException {
mHandler.post(mReleaseWakeLockIfNeeded);
}
};
} }
} }