Merge "Send user activity when dream quits unexpectedly" into udc-dev
This commit is contained in:
@@ -2560,6 +2560,17 @@
|
|||||||
assistant activities (ACTIVITY_TYPE_ASSISTANT) -->
|
assistant activities (ACTIVITY_TYPE_ASSISTANT) -->
|
||||||
<bool name="config_dismissDreamOnActivityStart">false</bool>
|
<bool name="config_dismissDreamOnActivityStart">false</bool>
|
||||||
|
|
||||||
|
<!-- Whether to send a user activity event to PowerManager when a dream quits unexpectedly so
|
||||||
|
that the screen won't immediately shut off.
|
||||||
|
|
||||||
|
When a dream stops unexpectedly, such as due to an app update, if the device has been
|
||||||
|
inactive less than the user's screen timeout, the device goes to keyguard and times out
|
||||||
|
back to dreaming after a few seconds. If the device has been inactive longer, the screen
|
||||||
|
will immediately turn off. With this flag on, the device will go back to keyguard in all
|
||||||
|
scenarios rather than turning off, which gives the device a chance to start dreaming
|
||||||
|
again. -->
|
||||||
|
<bool name="config_resetScreenTimeoutOnUnexpectedDreamExit">false</bool>
|
||||||
|
|
||||||
<!-- The prefixes of dream component names that are loggable.
|
<!-- The prefixes of dream component names that are loggable.
|
||||||
Matched against ComponentName#flattenToString() for dream components.
|
Matched against ComponentName#flattenToString() for dream components.
|
||||||
If empty, logs "other" for all. -->
|
If empty, logs "other" for all. -->
|
||||||
|
|||||||
@@ -2207,6 +2207,7 @@
|
|||||||
<java-symbol type="array" name="config_supportedDreamComplications" />
|
<java-symbol type="array" name="config_supportedDreamComplications" />
|
||||||
<java-symbol type="array" name="config_disabledDreamComponents" />
|
<java-symbol type="array" name="config_disabledDreamComponents" />
|
||||||
<java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
|
<java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
|
||||||
|
<java-symbol type="bool" name="config_resetScreenTimeoutOnUnexpectedDreamExit" />
|
||||||
<java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" />
|
<java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" />
|
||||||
<java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" />
|
<java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" />
|
||||||
<java-symbol type="integer" name="config_minDreamOverlayDurationMs" />
|
<java-symbol type="integer" name="config_minDreamOverlayDurationMs" />
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package com.android.server.dreams;
|
|||||||
|
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
|
||||||
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
|
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
|
||||||
|
import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
|
||||||
|
import static android.os.PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS;
|
||||||
|
|
||||||
import android.app.ActivityTaskManager;
|
import android.app.ActivityTaskManager;
|
||||||
import android.app.BroadcastOptions;
|
import android.app.BroadcastOptions;
|
||||||
@@ -72,6 +74,7 @@ final class DreamController {
|
|||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
private final Listener mListener;
|
private final Listener mListener;
|
||||||
private final ActivityTaskManager mActivityTaskManager;
|
private final ActivityTaskManager mActivityTaskManager;
|
||||||
|
private final PowerManager mPowerManager;
|
||||||
|
|
||||||
private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
|
private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
|
||||||
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | FLAG_RECEIVER_FOREGROUND);
|
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | FLAG_RECEIVER_FOREGROUND);
|
||||||
@@ -84,6 +87,15 @@ final class DreamController {
|
|||||||
private final Intent mCloseNotificationShadeIntent;
|
private final Intent mCloseNotificationShadeIntent;
|
||||||
private final Bundle mCloseNotificationShadeOptions;
|
private final Bundle mCloseNotificationShadeOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this flag is on, we report user activity to {@link PowerManager} so that the screen
|
||||||
|
* doesn't shut off immediately when a dream quits unexpectedly. The device will instead go to
|
||||||
|
* keyguard and time out back to dreaming shortly.
|
||||||
|
*
|
||||||
|
* This allows the dream a second chance to relaunch in case of an app update or other crash.
|
||||||
|
*/
|
||||||
|
private final boolean mResetScreenTimeoutOnUnexpectedDreamExit;
|
||||||
|
|
||||||
private DreamRecord mCurrentDream;
|
private DreamRecord mCurrentDream;
|
||||||
|
|
||||||
// Whether a dreaming started intent has been broadcast.
|
// Whether a dreaming started intent has been broadcast.
|
||||||
@@ -101,6 +113,7 @@ final class DreamController {
|
|||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
|
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
|
||||||
|
mPowerManager = mContext.getSystemService(PowerManager.class);
|
||||||
mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
||||||
mCloseNotificationShadeIntent.putExtra(EXTRA_REASON_KEY, EXTRA_REASON_VALUE);
|
mCloseNotificationShadeIntent.putExtra(EXTRA_REASON_KEY, EXTRA_REASON_VALUE);
|
||||||
mCloseNotificationShadeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
mCloseNotificationShadeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||||
@@ -110,6 +123,8 @@ final class DreamController {
|
|||||||
EXTRA_REASON_VALUE)
|
EXTRA_REASON_VALUE)
|
||||||
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
|
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
|
||||||
.toBundle();
|
.toBundle();
|
||||||
|
mResetScreenTimeoutOnUnexpectedDreamExit = context.getResources().getBoolean(
|
||||||
|
com.android.internal.R.bool.config_resetScreenTimeoutOnUnexpectedDreamExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,6 +228,17 @@ final class DreamController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a user activity signal to PowerManager to stop the screen from turning off immediately
|
||||||
|
* if there hasn't been any user interaction in a while.
|
||||||
|
*/
|
||||||
|
private void resetScreenTimeout() {
|
||||||
|
Slog.i(TAG, "Resetting screen timeout");
|
||||||
|
long time = SystemClock.uptimeMillis();
|
||||||
|
mPowerManager.userActivity(time, USER_ACTIVITY_EVENT_OTHER,
|
||||||
|
USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops dreaming.
|
* Stops dreaming.
|
||||||
*
|
*
|
||||||
@@ -420,6 +446,9 @@ final class DreamController {
|
|||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
mService = null;
|
mService = null;
|
||||||
if (mCurrentDream == DreamRecord.this) {
|
if (mCurrentDream == DreamRecord.this) {
|
||||||
|
if (mResetScreenTimeoutOnUnexpectedDreamExit) {
|
||||||
|
resetScreenTimeout();
|
||||||
|
}
|
||||||
stopDream(true /*immediate*/, "binder died");
|
stopDream(true /*immediate*/, "binder died");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -445,6 +474,9 @@ final class DreamController {
|
|||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
mService = null;
|
mService = null;
|
||||||
if (mCurrentDream == DreamRecord.this) {
|
if (mCurrentDream == DreamRecord.this) {
|
||||||
|
if (mResetScreenTimeoutOnUnexpectedDreamExit) {
|
||||||
|
resetScreenTimeout();
|
||||||
|
}
|
||||||
stopDream(true /*immediate*/, "service disconnected");
|
stopDream(true /*immediate*/, "service disconnected");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,11 @@
|
|||||||
|
|
||||||
package com.android.server.dreams;
|
package com.android.server.dreams;
|
||||||
|
|
||||||
|
import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
|
||||||
|
import static android.os.PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
@@ -32,7 +36,9 @@ import android.content.ServiceConnection;
|
|||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.os.IPowerManager;
|
||||||
import android.os.IRemoteCallback;
|
import android.os.IRemoteCallback;
|
||||||
|
import android.os.PowerManager;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.test.TestLooper;
|
import android.os.test.TestLooper;
|
||||||
import android.service.dreams.IDreamService;
|
import android.service.dreams.IDreamService;
|
||||||
@@ -58,6 +64,8 @@ public class DreamControllerTest {
|
|||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ActivityTaskManager mActivityTaskManager;
|
private ActivityTaskManager mActivityTaskManager;
|
||||||
|
@Mock
|
||||||
|
private IPowerManager mPowerManager;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private IBinder mIBinder;
|
private IBinder mIBinder;
|
||||||
@@ -67,6 +75,8 @@ public class DreamControllerTest {
|
|||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<ServiceConnection> mServiceConnectionACaptor;
|
private ArgumentCaptor<ServiceConnection> mServiceConnectionACaptor;
|
||||||
@Captor
|
@Captor
|
||||||
|
private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor;
|
||||||
|
@Captor
|
||||||
private ArgumentCaptor<IRemoteCallback> mRemoteCallbackCaptor;
|
private ArgumentCaptor<IRemoteCallback> mRemoteCallbackCaptor;
|
||||||
|
|
||||||
private final TestLooper mLooper = new TestLooper();
|
private final TestLooper mLooper = new TestLooper();
|
||||||
@@ -90,6 +100,12 @@ public class DreamControllerTest {
|
|||||||
when(mContext.getSystemServiceName(ActivityTaskManager.class))
|
when(mContext.getSystemServiceName(ActivityTaskManager.class))
|
||||||
.thenReturn(Context.ACTIVITY_TASK_SERVICE);
|
.thenReturn(Context.ACTIVITY_TASK_SERVICE);
|
||||||
|
|
||||||
|
final PowerManager powerManager = new PowerManager(mContext, mPowerManager, null, null);
|
||||||
|
when(mContext.getSystemService(Context.POWER_SERVICE))
|
||||||
|
.thenReturn(powerManager);
|
||||||
|
when(mContext.getSystemServiceName(PowerManager.class))
|
||||||
|
.thenReturn(Context.POWER_SERVICE);
|
||||||
|
|
||||||
mToken = new Binder();
|
mToken = new Binder();
|
||||||
mDreamName = ComponentName.unflattenFromString("dream");
|
mDreamName = ComponentName.unflattenFromString("dream");
|
||||||
mOverlayName = ComponentName.unflattenFromString("dream_overlay");
|
mOverlayName = ComponentName.unflattenFromString("dream_overlay");
|
||||||
@@ -209,9 +225,51 @@ public class DreamControllerTest {
|
|||||||
verify(mIDreamService).detach();
|
verify(mIDreamService).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serviceDisconnect_resetsScreenTimeout() throws RemoteException {
|
||||||
|
// Start dream.
|
||||||
|
mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
|
||||||
|
0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
|
||||||
|
ServiceConnection serviceConnection = captureServiceConnection();
|
||||||
|
serviceConnection.onServiceConnected(mDreamName, mIBinder);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
|
||||||
|
// Dream disconnects unexpectedly.
|
||||||
|
serviceConnection.onServiceDisconnected(mDreamName);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
|
||||||
|
// Power manager receives user activity signal.
|
||||||
|
verify(mPowerManager).userActivity(/*displayId=*/ anyInt(), /*time=*/ anyLong(),
|
||||||
|
eq(USER_ACTIVITY_EVENT_OTHER),
|
||||||
|
eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void binderDied_resetsScreenTimeout() throws RemoteException {
|
||||||
|
// Start dream.
|
||||||
|
mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
|
||||||
|
0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
|
||||||
|
captureServiceConnection().onServiceConnected(mDreamName, mIBinder);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
|
||||||
|
// Dream binder dies.
|
||||||
|
captureDeathRecipient().binderDied();
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
|
||||||
|
// Power manager receives user activity signal.
|
||||||
|
verify(mPowerManager).userActivity(/*displayId=*/ anyInt(), /*time=*/ anyLong(),
|
||||||
|
eq(USER_ACTIVITY_EVENT_OTHER),
|
||||||
|
eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
|
||||||
|
}
|
||||||
|
|
||||||
private ServiceConnection captureServiceConnection() {
|
private ServiceConnection captureServiceConnection() {
|
||||||
verify(mContext).bindServiceAsUser(any(), mServiceConnectionACaptor.capture(), anyInt(),
|
verify(mContext).bindServiceAsUser(any(), mServiceConnectionACaptor.capture(), anyInt(),
|
||||||
any());
|
any());
|
||||||
return mServiceConnectionACaptor.getValue();
|
return mServiceConnectionACaptor.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IBinder.DeathRecipient captureDeathRecipient() throws RemoteException {
|
||||||
|
verify(mIBinder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt());
|
||||||
|
return mDeathRecipientCaptor.getValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user