summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mariia Sandrikova <mariiasand@google.com> 2022-12-23 21:59:37 +0000
committer Andrii Kulian <akulian@google.com> 2022-12-25 21:05:32 +0000
commitf2a91a3dfd9dca7fe685bee288110cdc950cb3d3 (patch)
tree3c7438e8a4317b8ed1d0579bf3f7427e67436153
parentccf864a882306296adc6bee674665260f6c7f6d2 (diff)
Enable forced focus for resumed split screen activities
This is needed because some game engines wait until they get focus before drawing the content of the app so they are blacked out when they are resumed and do not have focus yet. Bug: 203398248 Test: atest FrameworksCoreTests:ViewRootImplTest#whenDispatchFakeFocus_noFocusAfterwards Change-Id: I5675b32fbb9f60a7373fadbce379d571168436e7
-rw-r--r--core/java/android/app/ActivityThread.java15
-rw-r--r--core/java/android/app/ClientTransactionHandler.java3
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java22
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java3
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutorHelper.java3
-rw-r--r--core/java/android/view/View.java16
-rw-r--r--core/java/android/view/ViewRootImpl.java37
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java6
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java6
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java2
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java14
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java15
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java3
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java14
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java3
20 files changed, 149 insertions, 26 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8e747b2471b4..afd8a52d43ce 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4227,7 +4227,8 @@ public final class ActivityThread extends ClientTransactionHandler
private void scheduleResume(ActivityClientRecord r) {
final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
- transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(/* isForward */ false));
+ transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(/* isForward */ false,
+ /* shouldSendCompatFakeFocus */ false));
executeTransaction(transaction);
}
@@ -4895,7 +4896,7 @@ public final class ActivityThread extends ClientTransactionHandler
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
- boolean isForward, String reason) {
+ boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
@@ -5002,6 +5003,16 @@ public final class ActivityThread extends ClientTransactionHandler
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
+
+ if (shouldSendCompatFakeFocus) {
+ // Attaching to a window is asynchronous with the activity being resumed,
+ // so it's possible we will need to send a fake focus event after attaching
+ if (impl != null) {
+ impl.dispatchCompatFakeFocus();
+ } else {
+ r.window.getDecorView().fakeFocusAfterAttachingToWindow();
+ }
+ }
}
r.nextIdle = mNewActivities;
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 3ba578371ea7..419ffac230f7 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -108,7 +108,8 @@ public abstract class ClientTransactionHandler {
* @param reason Reason for performing this operation.
*/
public abstract void handleResumeActivity(@NonNull ActivityClientRecord r,
- boolean finalStateRequest, boolean isForward, String reason);
+ boolean finalStateRequest, boolean isForward, boolean shouldSendCompatFakeFocus,
+ String reason);
/**
* Notify the activity about top resumed state change.
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index e6fdc006615a..222f8ca61e09 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -39,6 +39,9 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
private int mProcState;
private boolean mUpdateProcState;
private boolean mIsForward;
+ // Whether we should send compat fake focus when the activity is resumed. This is needed
+ // because some game engines wait to get focus before drawing the content of the app.
+ private boolean mShouldSendCompatFakeFocus;
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
@@ -52,7 +55,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
client.handleResumeActivity(r, true /* finalStateRequest */, mIsForward,
- "RESUME_ACTIVITY");
+ mShouldSendCompatFakeFocus, "RESUME_ACTIVITY");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -74,7 +77,8 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
private ResumeActivityItem() {}
/** Obtain an instance initialized with provided params. */
- public static ResumeActivityItem obtain(int procState, boolean isForward) {
+ public static ResumeActivityItem obtain(int procState, boolean isForward,
+ boolean shouldSendCompatFakeFocus) {
ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class);
if (instance == null) {
instance = new ResumeActivityItem();
@@ -82,12 +86,13 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
instance.mProcState = procState;
instance.mUpdateProcState = true;
instance.mIsForward = isForward;
+ instance.mShouldSendCompatFakeFocus = shouldSendCompatFakeFocus;
return instance;
}
/** Obtain an instance initialized with provided params. */
- public static ResumeActivityItem obtain(boolean isForward) {
+ public static ResumeActivityItem obtain(boolean isForward, boolean shouldSendCompatFakeFocus) {
ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class);
if (instance == null) {
instance = new ResumeActivityItem();
@@ -95,6 +100,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
instance.mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
instance.mUpdateProcState = false;
instance.mIsForward = isForward;
+ instance.mShouldSendCompatFakeFocus = shouldSendCompatFakeFocus;
return instance;
}
@@ -105,6 +111,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
mUpdateProcState = false;
mIsForward = false;
+ mShouldSendCompatFakeFocus = false;
ObjectPool.recycle(this);
}
@@ -117,6 +124,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
dest.writeInt(mProcState);
dest.writeBoolean(mUpdateProcState);
dest.writeBoolean(mIsForward);
+ dest.writeBoolean(mShouldSendCompatFakeFocus);
}
/** Read from Parcel. */
@@ -124,6 +132,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
mProcState = in.readInt();
mUpdateProcState = in.readBoolean();
mIsForward = in.readBoolean();
+ mShouldSendCompatFakeFocus = in.readBoolean();
}
public static final @NonNull Creator<ResumeActivityItem> CREATOR =
@@ -147,7 +156,8 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
}
final ResumeActivityItem other = (ResumeActivityItem) o;
return mProcState == other.mProcState && mUpdateProcState == other.mUpdateProcState
- && mIsForward == other.mIsForward;
+ && mIsForward == other.mIsForward
+ && mShouldSendCompatFakeFocus == other.mShouldSendCompatFakeFocus;
}
@Override
@@ -156,12 +166,14 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
result = 31 * result + mProcState;
result = 31 * result + (mUpdateProcState ? 1 : 0);
result = 31 * result + (mIsForward ? 1 : 0);
+ result = 31 * result + (mShouldSendCompatFakeFocus ? 1 : 0);
return result;
}
@Override
public String toString() {
return "ResumeActivityItem{procState=" + mProcState
- + ",updateProcState=" + mUpdateProcState + ",isForward=" + mIsForward + "}";
+ + ",updateProcState=" + mUpdateProcState + ",isForward=" + mIsForward
+ + ",shouldSendCompatFakeFocus=" + mShouldSendCompatFakeFocus + "}";
}
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 1ff0b796fb1e..c8f7d100a398 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -226,7 +226,8 @@ public class TransactionExecutor {
break;
case ON_RESUME:
mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
- r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
+ r.isForward, false /* shouldSendCompatFakeFocus */,
+ "LIFECYCLER_RESUME_ACTIVITY");
break;
case ON_PAUSE:
mTransactionHandler.handlePauseActivity(r, false /* finished */,
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index cb6aa09cc6db..5311b09e609d 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -202,7 +202,8 @@ public class TransactionExecutorHelper {
lifecycleItem = StopActivityItem.obtain(0 /* configChanges */);
break;
default:
- lifecycleItem = ResumeActivityItem.obtain(false /* isForward */);
+ lifecycleItem = ResumeActivityItem.obtain(false /* isForward */,
+ false /* shouldSendCompatFakeFocus */);
break;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d709840d1b10..01984571a8f4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -21602,6 +21602,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return Math.max(vis1, vis2);
}
+ private boolean mShouldFakeFocus = false;
+
+ /**
+ * Fake send a focus event after attaching to window.
+ * See {@link android.view.ViewRootImpl#dispatchCompatFakeFocus()} for details.
+ * @hide
+ */
+ public void fakeFocusAfterAttachingToWindow() {
+ mShouldFakeFocus = true;
+ }
+
/**
* @param info the {@link android.view.View.AttachInfo} to associated with
* this view
@@ -21670,6 +21681,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
notifyEnterOrExitForAutoFillIfNeeded(true);
notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
+
+ if (mShouldFakeFocus) {
+ getViewRootImpl().dispatchCompatFakeFocus();
+ mShouldFakeFocus = false;
+ }
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 52232f8c7e50..65a16ad762c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -261,6 +261,7 @@ public final class ViewRootImpl implements ViewParent,
private static final boolean DEBUG_CONTENT_CAPTURE = false || LOCAL_LOGV;
private static final boolean DEBUG_SCROLL_CAPTURE = false || LOCAL_LOGV;
private static final boolean DEBUG_BLAST = false || LOCAL_LOGV;
+ private static final int LOGTAG_INPUT_FOCUS = 62001;
/**
* Set to false if we do not want to use the multi threaded renderer even though
@@ -3840,8 +3841,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (mAdded) {
- dispatchFocusEvent(hasWindowFocus);
-
+ dispatchFocusEvent(hasWindowFocus, false /* fakeFocus */);
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
mImeFocusController.onPostWindowFocus(
@@ -3878,7 +3878,33 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private void dispatchFocusEvent(boolean hasWindowFocus) {
+ /**
+ * Send a fake focus event for unfocused apps in split screen as some game engines wait to
+ * get focus before drawing the content of the app. This will be used so that apps do not get
+ * blacked out when they are resumed and do not have focus yet.
+ *
+ * {@hide}
+ */
+ // TODO(b/263094829): Investigate dispatching this for onPause as well
+ public void dispatchCompatFakeFocus() {
+ boolean aboutToHaveFocus = false;
+ synchronized (this) {
+ aboutToHaveFocus = mWindowFocusChanged && mUpcomingWindowFocus;
+ }
+ final boolean alreadyHaveFocus = mAttachInfo.mHasWindowFocus;
+ if (aboutToHaveFocus || alreadyHaveFocus) {
+ // Do not need to toggle focus if app doesn't need it, or has focus.
+ return;
+ }
+ EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
+ "Giving fake focus to " + mBasePackageName, "reason=unity bug workaround");
+ dispatchFocusEvent(true /* hasWindowFocus */, true /* fakeFocus */);
+ EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
+ "Removing fake focus from " + mBasePackageName, "reason=timeout callback");
+ dispatchFocusEvent(false /* hasWindowFocus */, true /* fakeFocus */);
+ }
+
+ private void dispatchFocusEvent(boolean hasWindowFocus, boolean fakeFocus) {
profileRendering(hasWindowFocus);
if (hasWindowFocus && mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
mFullRedrawNeeded = true;
@@ -3904,7 +3930,10 @@ public final class ViewRootImpl implements ViewParent,
}
mAttachInfo.mHasWindowFocus = hasWindowFocus;
- mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes);
+
+ if (!fakeFocus) {
+ mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes);
+ }
if (mView != null) {
mAttachInfo.mKeyDispatchState.reset();
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 92b4ba1d619c..184f86968e51 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5370,6 +5370,11 @@
TODO(b/255532890) Enable when ignoreOrientationRequest is set -->
<bool name="config_letterboxIsEnabledForTranslucentActivities">false</bool>
+ <!-- Whether sending compat fake focus for split screen resumed activities is enabled.
+ Needed because some game engines wait to get focus before drawing the content of
+ the app which isn't guaranteed by default in multi-window modes. -->
+ <bool name="config_isCompatFakeFocusEnabled">false</bool>
+
<!-- Whether camera compat treatment is enabled for issues caused by orientation mismatch
between camera buffers and an app window. This includes force rotation of fixed
orientation activities connected to the camera in fullscreen and showing a tooltip in
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f54335a6fba8..af29b233ba83 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4433,6 +4433,7 @@
<java-symbol type="bool" name="config_letterboxIsEducationEnabled" />
<java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" />
<java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" />
+ <java-symbol type="bool" name="config_isCompatFakeFocusEnabled" />
<java-symbol type="bool" name="config_isWindowManagerCameraCompatTreatmentEnabled" />
<java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 9db88051542b..abbbb2f516b2 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -775,7 +775,8 @@ public class ActivityThreadTest {
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
null, 0, new MergedConfiguration(), false /* preserveWindow */);
final ResumeActivityItem resumeStateRequest =
- ResumeActivityItem.obtain(true /* isForward */);
+ ResumeActivityItem.obtain(true /* isForward */,
+ false /* shouldSendCompatFakeFocus*/);
final ClientTransaction transaction = newTransaction(activity);
transaction.addCallback(callbackItem);
@@ -786,7 +787,8 @@ public class ActivityThreadTest {
private static ClientTransaction newResumeTransaction(Activity activity) {
final ResumeActivityItem resumeStateRequest =
- ResumeActivityItem.obtain(true /* isForward */);
+ ResumeActivityItem.obtain(true /* isForward */,
+ false /* shouldSendCompatFakeFocus */);
final ClientTransaction transaction = newTransaction(activity);
transaction.setLifecycleStateRequest(resumeStateRequest);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 7f2b51d9209c..ca6735bc6c32 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -237,15 +237,15 @@ public class ObjectPoolTests {
@Test
public void testRecycleResumeActivityItem() {
- ResumeActivityItem emptyItem = ResumeActivityItem.obtain(false);
- ResumeActivityItem item = ResumeActivityItem.obtain(3, true);
+ ResumeActivityItem emptyItem = ResumeActivityItem.obtain(false, false);
+ ResumeActivityItem item = ResumeActivityItem.obtain(3, true, false);
assertNotSame(item, emptyItem);
assertFalse(item.equals(emptyItem));
item.recycle();
assertEquals(item, emptyItem);
- ResumeActivityItem item2 = ResumeActivityItem.obtain(2, true);
+ ResumeActivityItem item2 = ResumeActivityItem.obtain(2, true, false);
assertSame(item, item2);
assertFalse(item2.equals(emptyItem));
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index f4bf1cd879f4..48a824915015 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -222,7 +222,7 @@ public class TransactionParcelTests {
public void testResume() {
// Write to parcel
ResumeActivityItem item = ResumeActivityItem.obtain(27 /* procState */,
- true /* isForward */);
+ true /* isForward */, false /* shouldSendCompatFakeFocus */);
writeAndPrepareForReading(item);
// Read from parcel and assert
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 1d0644897fc2..2afbb476bc16 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -297,6 +297,20 @@ public class ViewRootImplTest {
}
}
+ @Test
+ public void whenDispatchFakeFocus_focusDoesNotPersist() throws Exception {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ view.clearFocus();
+
+ assertThat(view.hasWindowFocus()).isFalse();
+
+ mViewRootImpl = view.getViewRootImpl();
+
+ mViewRootImpl.dispatchCompatFakeFocus();
+ assertThat(view.hasWindowFocus()).isFalse();
+ }
+
/**
* When window doesn't have focus, keys should be dropped.
*/
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 3e75c7db0276..539eb6253f4d 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -302,7 +302,7 @@ public class ActivityThreadClientTest {
private void resumeActivity(ActivityClientRecord r) {
mThread.handleResumeActivity(r, true /* finalStateRequest */,
- true /* isForward */, "test");
+ true /* isForward */, false /* shouldSendCompatFakeFocus */, "test");
}
private void pauseActivity(ActivityClientRecord r) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index bf4e25c3b14c..c1a68de430a3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -9359,7 +9359,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
- lifecycleItem = ResumeActivityItem.obtain(isTransitionForward());
+ lifecycleItem = ResumeActivityItem.obtain(isTransitionForward(),
+ shouldSendCompatFakeFocus());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
@@ -10120,6 +10121,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ /**
+ * Whether we should send fake focus when the activity is resumed. This is done because some
+ * game engines wait to get focus before drawing the content of the app.
+ */
+ // TODO(b/263593361): Explore enabling compat fake focus for freeform.
+ // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when
+ // covered with bubbles.
+ boolean shouldSendCompatFakeFocus() {
+ return mWmService.mLetterboxConfiguration.isCompatFakeFocusEnabled() && inMultiWindowMode()
+ && !inPinnedWindowingMode() && !inFreeformWindowingMode();
+ }
+
static class Builder {
private final ActivityTaskManagerService mAtmService;
private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3f885f3ab23f..473a6e5ac0c5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -916,7 +916,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
- lifecycleItem = ResumeActivityItem.obtain(isTransitionForward);
+ lifecycleItem = ResumeActivityItem.obtain(isTransitionForward,
+ r.shouldSendCompatFakeFocus());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 18c5c3b82b19..7266d2194779 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -214,7 +214,8 @@ final class DisplayRotationCompatPolicy {
activity.app.getThread(), activity.token);
transaction.addCallback(
RefreshCallbackItem.obtain(cycleThroughStop ? ON_STOP : ON_PAUSE));
- transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(/* isForward */ false));
+ transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(
+ /* isForward */ false, /* shouldSendCompatFakeFocus */ false));
activity.mAtmService.getLifecycleManager().scheduleTransaction(transaction);
mHandler.postDelayed(
() -> onActivityRefreshed(activity),
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index a7bf595fa673..03c558924430 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -191,6 +191,11 @@ final class LetterboxConfiguration {
// Allows to enable letterboxing strategy for translucent activities ignoring flags.
private boolean mTranslucentLetterboxingOverrideEnabled;
+ // Whether sending compat fake focus is enabled for unfocused apps in splitscreen. Some game
+ // engines wait to get focus before drawing the content of the app so this needs to be used
+ // otherwise the apps get blacked out when they are resumed and do not have focus yet.
+ private boolean mIsCompatFakeFocusEnabled;
+
// Whether camera compatibility treatment is enabled.
// See DisplayRotationCompatPolicy for context.
private final boolean mIsCameraCompatTreatmentEnabled;
@@ -259,6 +264,8 @@ final class LetterboxConfiguration {
R.bool.config_isWindowManagerCameraCompatTreatmentEnabled);
mLetterboxConfigurationPersister = letterboxConfigurationPersister;
mLetterboxConfigurationPersister.start();
+ mIsCompatFakeFocusEnabled = mContext.getResources()
+ .getBoolean(R.bool.config_isCompatFakeFocusEnabled);
}
/**
@@ -970,6 +977,13 @@ final class LetterboxConfiguration {
"enable_translucent_activity_letterbox", false);
}
+ // TODO(b/262866240): Add listener to check for device config property
+ /** Whether fake sending focus is enabled for unfocused apps in splitscreen */
+ boolean isCompatFakeFocusEnabled() {
+ return mIsCompatFakeFocusEnabled && DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER, "enable_compat_fake_focus", true);
+ }
+
/** Whether camera compatibility treatment is enabled. */
boolean isCameraCompatTreatmentEnabled(boolean checkDeviceConfig) {
return mIsCameraCompatTreatmentEnabled
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index ae3b2f2bf8e6..b8878618b21c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1444,7 +1444,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
next.abortAndClearOptionsAnimation();
transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.getReportedProcState(),
- dc.isNextTransitionForward()));
+ dc.isNextTransitionForward(), next.shouldSendCompatFakeFocus()));
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index d1234e3de81a..8bb79e3f7ddc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -398,7 +398,8 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase {
final ClientTransaction transaction = ClientTransaction.obtain(
mActivity.app.getThread(), mActivity.token);
transaction.addCallback(RefreshCallbackItem.obtain(cycleThroughStop ? ON_STOP : ON_PAUSE));
- transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(/* isForward */ false));
+ transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(
+ /* isForward */ false, /* shouldSendCompatFakeFocus */ false));
verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
.scheduleTransaction(eq(transaction));