Loading core/java/android/service/dreams/DreamService.java +18 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.service.dreams; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.service.dreams.Flags.allowDreamAttachFailure; import static android.service.dreams.Flags.dreamHandlesBeingObscured; import static android.service.dreams.Flags.dreamHandlesConfirmKeys; import static android.service.dreams.Flags.startAndStopDozingInBackground; Loading Loading @@ -45,6 +46,7 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; Loading Loading @@ -250,6 +252,13 @@ public class DreamService extends Service implements Window.Callback { static final String EXTRA_DREAM_OVERLAY_COMPONENT = "android.service.dream.DreamService.dream_overlay_component"; /** * The name of the extra that indicates an error during attach. * @hide */ public static final String BUNDLE_KEY_ATTACH_ERROR = "android.service.dream.DreamService.attach_error"; private final IDreamManager mDreamManager; private final Handler mHandler; private IBinder mDreamToken; Loading Loading @@ -1571,6 +1580,15 @@ public class DreamService extends Service implements Window.Callback { if (mDreamToken != null) { Slog.e(mTag, "attach() called when dream with token=" + mDreamToken + " already attached"); if (allowDreamAttachFailure()) { try { final Bundle result = new Bundle(); result.putBoolean(BUNDLE_KEY_ATTACH_ERROR, true); started.sendResult(result); } catch (RemoteException e) { // The dream controller is dead, so there is nothing to do. } } return; } if (mFinished || mWaking) { Loading core/java/android/service/dreams/flags.aconfig +11 −1 Original line number Diff line number Diff line Loading @@ -130,3 +130,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "allow_dream_attach_failure" namespace: "systemui" description: "Fails starting dream when attaching to a service that is already attached" bug: "434674824" metadata { purpose: PURPOSE_BUGFIX } } services/core/java/com/android/server/dreams/DreamController.java +30 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.dreams; 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 static android.service.dreams.Flags.allowDreamAttachFailure; import android.app.ActivityTaskManager; import android.app.BroadcastOptions; Loading Loading @@ -410,6 +411,9 @@ final class DreamController { private void attach(IDreamService service) { try { service.asBinder().linkToDeath(mCurrentDream, 0); if (allowDreamAttachFailure()) { mCurrentDream.mService = service; } service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze, mCurrentDream.mIsPreviewMode, mCurrentDream.mDreamingStartedCallback); } catch (RemoteException ex) { Loading @@ -418,8 +422,13 @@ final class DreamController { return; } if (!allowDreamAttachFailure()) { mCurrentDream.mService = service; onDreamStarted(); } } private void onDreamStarted() { if (!mCurrentDream.mIsPreviewMode && !mSentStartBroadcast) { mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL, null /* receiverPermission */, mDreamingStartedStoppedOptions); Loading Loading @@ -475,6 +484,26 @@ final class DreamController { public void sendResult(Bundle data) { mHandler.post(mStopPreviousDreamsIfNeeded); mHandler.post(mReleaseWakeLockIfNeeded); if (!allowDreamAttachFailure()) { return; } mHandler.post(() -> { // If the dream has been stopped already, don't do anything. if (mCurrentDream != DreamRecord.this) { return; } if (data != null && data.getBoolean(DreamService.BUNDLE_KEY_ATTACH_ERROR)) { Slog.w(TAG, "Dream failed to start due to attach error"); stopDream(true, "dream failed to attach"); return; } onDreamStarted(); }); } }; Loading services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java +63 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.any; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; Loading @@ -37,6 +38,7 @@ import android.content.Context; import android.content.ServiceConnection; import android.content.res.Resources; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.IPowerManager; Loading @@ -44,12 +46,18 @@ import android.os.IRemoteCallback; import android.os.PowerManager; import android.os.RemoteException; import android.os.test.TestLooper; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.dreams.DreamService; import android.service.dreams.Flags; import android.service.dreams.IDreamService; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; Loading Loading @@ -85,6 +93,9 @@ public class DreamControllerTest { @Captor private ArgumentCaptor<IRemoteCallback> mRemoteCallbackCaptor; @Rule public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final TestLooper mLooper = new TestLooper(); private final Handler mHandler = new Handler(mLooper.getLooper()); Loading Loading @@ -133,8 +144,31 @@ public class DreamControllerTest { eq(false) /*preview*/, any()); } @EnableFlags(Flags.FLAG_ALLOW_DREAM_ATTACH_FAILURE) @Test public void startDream_flagEnabled_dreamListenerNotified() throws RemoteException { // Call dream controller to start dreaming. mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); // Mock service connected. final ServiceConnection serviceConnection = captureServiceConnection(); serviceConnection.onServiceConnected(mDreamName, mIBinder); mLooper.dispatchAll(); // Mock dream started callback. verify(mIDreamService).attach(eq(mToken), eq(false), eq(false), mRemoteCallbackCaptor.capture()); mRemoteCallbackCaptor.getValue().sendResult(null); mLooper.dispatchAll(); // Verify that dream listener is notified. verify(mListener).onDreamStarted(any()); } @DisableFlags(Flags.FLAG_ALLOW_DREAM_ATTACH_FAILURE) @Test public void startDream_dreamListenerNotified() { public void startDream_flagDisabled_dreamListenerNotified() { // Call dream controller to start dreaming. mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); Loading Loading @@ -312,6 +346,34 @@ public class DreamControllerTest { assertTrue(mDreamController.dreamIsFrontmost()); } @Test @EnableFlags(Flags.FLAG_ALLOW_DREAM_ATTACH_FAILURE) public void startDream_attachReturnsError_stopsDream() throws RemoteException { // Call dream controller to start dreaming. mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); // Mock service connected. final ServiceConnection serviceConnection = captureServiceConnection(); serviceConnection.onServiceConnected(mDreamName, mIBinder); mLooper.dispatchAll(); // Verify that dream service is called to attach. verify(mIDreamService).attach(eq(mToken), eq(false) /*doze*/, eq(false) /*preview*/, mRemoteCallbackCaptor.capture()); // Mock attach returning an error. final Bundle bundle = new Bundle(); bundle.putBoolean(DreamService.BUNDLE_KEY_ATTACH_ERROR, true); mRemoteCallbackCaptor.getValue().sendResult(bundle); mLooper.dispatchAll(); // Verify that the dream is stopped. verify(mIDreamService).detach(); verify(mListener).onDreamStopped(any()); verify(mListener, never()).onDreamStarted(any()); } private ServiceConnection captureServiceConnection() { verify(mContext).bindServiceAsUser(any(), mServiceConnectionACaptor.capture(), anyInt(), any()); Loading Loading
core/java/android/service/dreams/DreamService.java +18 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.service.dreams; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.service.dreams.Flags.allowDreamAttachFailure; import static android.service.dreams.Flags.dreamHandlesBeingObscured; import static android.service.dreams.Flags.dreamHandlesConfirmKeys; import static android.service.dreams.Flags.startAndStopDozingInBackground; Loading Loading @@ -45,6 +46,7 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; Loading Loading @@ -250,6 +252,13 @@ public class DreamService extends Service implements Window.Callback { static final String EXTRA_DREAM_OVERLAY_COMPONENT = "android.service.dream.DreamService.dream_overlay_component"; /** * The name of the extra that indicates an error during attach. * @hide */ public static final String BUNDLE_KEY_ATTACH_ERROR = "android.service.dream.DreamService.attach_error"; private final IDreamManager mDreamManager; private final Handler mHandler; private IBinder mDreamToken; Loading Loading @@ -1571,6 +1580,15 @@ public class DreamService extends Service implements Window.Callback { if (mDreamToken != null) { Slog.e(mTag, "attach() called when dream with token=" + mDreamToken + " already attached"); if (allowDreamAttachFailure()) { try { final Bundle result = new Bundle(); result.putBoolean(BUNDLE_KEY_ATTACH_ERROR, true); started.sendResult(result); } catch (RemoteException e) { // The dream controller is dead, so there is nothing to do. } } return; } if (mFinished || mWaking) { Loading
core/java/android/service/dreams/flags.aconfig +11 −1 Original line number Diff line number Diff line Loading @@ -130,3 +130,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "allow_dream_attach_failure" namespace: "systemui" description: "Fails starting dream when attaching to a service that is already attached" bug: "434674824" metadata { purpose: PURPOSE_BUGFIX } }
services/core/java/com/android/server/dreams/DreamController.java +30 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.dreams; 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 static android.service.dreams.Flags.allowDreamAttachFailure; import android.app.ActivityTaskManager; import android.app.BroadcastOptions; Loading Loading @@ -410,6 +411,9 @@ final class DreamController { private void attach(IDreamService service) { try { service.asBinder().linkToDeath(mCurrentDream, 0); if (allowDreamAttachFailure()) { mCurrentDream.mService = service; } service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze, mCurrentDream.mIsPreviewMode, mCurrentDream.mDreamingStartedCallback); } catch (RemoteException ex) { Loading @@ -418,8 +422,13 @@ final class DreamController { return; } if (!allowDreamAttachFailure()) { mCurrentDream.mService = service; onDreamStarted(); } } private void onDreamStarted() { if (!mCurrentDream.mIsPreviewMode && !mSentStartBroadcast) { mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL, null /* receiverPermission */, mDreamingStartedStoppedOptions); Loading Loading @@ -475,6 +484,26 @@ final class DreamController { public void sendResult(Bundle data) { mHandler.post(mStopPreviousDreamsIfNeeded); mHandler.post(mReleaseWakeLockIfNeeded); if (!allowDreamAttachFailure()) { return; } mHandler.post(() -> { // If the dream has been stopped already, don't do anything. if (mCurrentDream != DreamRecord.this) { return; } if (data != null && data.getBoolean(DreamService.BUNDLE_KEY_ATTACH_ERROR)) { Slog.w(TAG, "Dream failed to start due to attach error"); stopDream(true, "dream failed to attach"); return; } onDreamStarted(); }); } }; Loading
services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java +63 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.any; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; Loading @@ -37,6 +38,7 @@ import android.content.Context; import android.content.ServiceConnection; import android.content.res.Resources; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.IPowerManager; Loading @@ -44,12 +46,18 @@ import android.os.IRemoteCallback; import android.os.PowerManager; import android.os.RemoteException; import android.os.test.TestLooper; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.dreams.DreamService; import android.service.dreams.Flags; import android.service.dreams.IDreamService; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; Loading Loading @@ -85,6 +93,9 @@ public class DreamControllerTest { @Captor private ArgumentCaptor<IRemoteCallback> mRemoteCallbackCaptor; @Rule public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final TestLooper mLooper = new TestLooper(); private final Handler mHandler = new Handler(mLooper.getLooper()); Loading Loading @@ -133,8 +144,31 @@ public class DreamControllerTest { eq(false) /*preview*/, any()); } @EnableFlags(Flags.FLAG_ALLOW_DREAM_ATTACH_FAILURE) @Test public void startDream_flagEnabled_dreamListenerNotified() throws RemoteException { // Call dream controller to start dreaming. mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); // Mock service connected. final ServiceConnection serviceConnection = captureServiceConnection(); serviceConnection.onServiceConnected(mDreamName, mIBinder); mLooper.dispatchAll(); // Mock dream started callback. verify(mIDreamService).attach(eq(mToken), eq(false), eq(false), mRemoteCallbackCaptor.capture()); mRemoteCallbackCaptor.getValue().sendResult(null); mLooper.dispatchAll(); // Verify that dream listener is notified. verify(mListener).onDreamStarted(any()); } @DisableFlags(Flags.FLAG_ALLOW_DREAM_ATTACH_FAILURE) @Test public void startDream_dreamListenerNotified() { public void startDream_flagDisabled_dreamListenerNotified() { // Call dream controller to start dreaming. mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); Loading Loading @@ -312,6 +346,34 @@ public class DreamControllerTest { assertTrue(mDreamController.dreamIsFrontmost()); } @Test @EnableFlags(Flags.FLAG_ALLOW_DREAM_ATTACH_FAILURE) public void startDream_attachReturnsError_stopsDream() throws RemoteException { // Call dream controller to start dreaming. mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); // Mock service connected. final ServiceConnection serviceConnection = captureServiceConnection(); serviceConnection.onServiceConnected(mDreamName, mIBinder); mLooper.dispatchAll(); // Verify that dream service is called to attach. verify(mIDreamService).attach(eq(mToken), eq(false) /*doze*/, eq(false) /*preview*/, mRemoteCallbackCaptor.capture()); // Mock attach returning an error. final Bundle bundle = new Bundle(); bundle.putBoolean(DreamService.BUNDLE_KEY_ATTACH_ERROR, true); mRemoteCallbackCaptor.getValue().sendResult(bundle); mLooper.dispatchAll(); // Verify that the dream is stopped. verify(mIDreamService).detach(); verify(mListener).onDreamStopped(any()); verify(mListener, never()).onDreamStarted(any()); } private ServiceConnection captureServiceConnection() { verify(mContext).bindServiceAsUser(any(), mServiceConnectionACaptor.capture(), anyInt(), any()); Loading