Merge "Ensure GameSession is not destroyed when restarting game." into tm-dev

This commit is contained in:
TreeHugger Robot
2022-05-03 03:28:30 +00:00
committed by Android (Google) Code Review
5 changed files with 82 additions and 27 deletions

View File

@@ -35,6 +35,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ScreenshotHelper; import com.android.internal.util.ScreenshotHelper;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration; import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowManagerService;
@@ -62,6 +63,7 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide
activityTaskManager, activityTaskManager,
(WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE), (WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE),
LocalServices.getService(WindowManagerInternal.class), LocalServices.getService(WindowManagerInternal.class),
LocalServices.getService(ActivityTaskManagerInternal.class),
new GameServiceConnector(mContext, configuration), new GameServiceConnector(mContext, configuration),
new GameSessionServiceConnector(mContext, configuration), new GameSessionServiceConnector(mContext, configuration),
new ScreenshotHelper(mContext)); new ScreenshotHelper(mContext));

View File

@@ -29,7 +29,6 @@ import android.app.IProcessObserver;
import android.app.TaskStackListener; import android.app.TaskStackListener;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Insets; import android.graphics.Insets;
import android.graphics.Rect; import android.graphics.Rect;
@@ -60,6 +59,7 @@ import com.android.internal.infra.ServiceConnector;
import com.android.internal.infra.ServiceConnector.ServiceLifecycleCallbacks; import com.android.internal.infra.ServiceConnector.ServiceLifecycleCallbacks;
import com.android.internal.os.BackgroundThread; import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ScreenshotHelper; import com.android.internal.util.ScreenshotHelper;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener; import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowManagerService;
@@ -93,6 +93,9 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
public void onBinderDied() { public void onBinderDied() {
mBackgroundExecutor.execute(() -> { mBackgroundExecutor.execute(() -> {
synchronized (mLock) { synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "GameSessionService died. Destroying all sessions");
}
destroyAndClearAllGameSessionsLocked(); destroyAndClearAllGameSessionsLocked();
} }
}); });
@@ -223,6 +226,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
private final IActivityTaskManager mActivityTaskManager; private final IActivityTaskManager mActivityTaskManager;
private final WindowManagerService mWindowManagerService; private final WindowManagerService mWindowManagerService;
private final WindowManagerInternal mWindowManagerInternal; private final WindowManagerInternal mWindowManagerInternal;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
private final ScreenshotHelper mScreenshotHelper; private final ScreenshotHelper mScreenshotHelper;
private final ServiceConnector<IGameService> mGameServiceConnector; private final ServiceConnector<IGameService> mGameServiceConnector;
private final ServiceConnector<IGameSessionService> mGameSessionServiceConnector; private final ServiceConnector<IGameSessionService> mGameSessionServiceConnector;
@@ -249,6 +253,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
@NonNull IActivityTaskManager activityTaskManager, @NonNull IActivityTaskManager activityTaskManager,
@NonNull WindowManagerService windowManagerService, @NonNull WindowManagerService windowManagerService,
@NonNull WindowManagerInternal windowManagerInternal, @NonNull WindowManagerInternal windowManagerInternal,
@NonNull ActivityTaskManagerInternal activityTaskManagerInternal,
@NonNull ServiceConnector<IGameService> gameServiceConnector, @NonNull ServiceConnector<IGameService> gameServiceConnector,
@NonNull ServiceConnector<IGameSessionService> gameSessionServiceConnector, @NonNull ServiceConnector<IGameSessionService> gameSessionServiceConnector,
@NonNull ScreenshotHelper screenshotHelper) { @NonNull ScreenshotHelper screenshotHelper) {
@@ -261,6 +266,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
mActivityTaskManager = activityTaskManager; mActivityTaskManager = activityTaskManager;
mWindowManagerService = windowManagerService; mWindowManagerService = windowManagerService;
mWindowManagerInternal = windowManagerInternal; mWindowManagerInternal = windowManagerInternal;
mActivityTaskManagerInternal = activityTaskManagerInternal;
mGameServiceConnector = gameServiceConnector; mGameServiceConnector = gameServiceConnector;
mGameSessionServiceConnector = gameSessionServiceConnector; mGameSessionServiceConnector = gameSessionServiceConnector;
mScreenshotHelper = screenshotHelper; mScreenshotHelper = screenshotHelper;
@@ -443,6 +449,11 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
return; return;
} }
if (DEBUG) {
Slog.i(TAG, "onTaskRemoved() id: " + taskId);
}
removeAndDestroyGameSessionIfNecessaryLocked(taskId); removeAndDestroyGameSessionIfNecessaryLocked(taskId);
} }
} }
@@ -775,10 +786,20 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
if (gameSessionRecord.getGameSession() != null && packageName.equals( if (gameSessionRecord.getGameSession() != null && packageName.equals(
gameSessionRecord.getComponentName().getPackageName())) { gameSessionRecord.getComponentName().getPackageName())) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "endGameSessionsForPackageLocked(): No more processes for " Slog.i(TAG, "endGameSessionsForPackageLocked(): No more processes for "
+ packageName + ", ending game session with taskId: " + packageName + ", ending game session with taskId: "
+ gameSessionRecord.getTaskId()); + gameSessionRecord.getTaskId());
} }
RunningTaskInfo runningTaskInfo =
mGameTaskInfoProvider.getRunningTaskInfo(gameSessionRecord.getTaskId());
if (runningTaskInfo != null && (runningTaskInfo.isVisible)) {
if (DEBUG) {
Slog.i(TAG, "Found visible task. Ignoring end game session. taskId:"
+ gameSessionRecord.getTaskId());
}
continue;
}
mGameSessions.put(gameSessionRecord.getTaskId(), mGameSessions.put(gameSessionRecord.getTaskId(),
gameSessionRecord.withGameSessionEndedOnProcessDeath()); gameSessionRecord.withGameSessionEndedOnProcessDeath());
destroyGameSessionFromRecordLocked(gameSessionRecord); destroyGameSessionFromRecordLocked(gameSessionRecord);
@@ -867,24 +888,18 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
private void restartGame(int taskId) { private void restartGame(int taskId) {
String packageName; String packageName;
synchronized (mLock) { synchronized (mLock) {
boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId); GameSessionRecord gameSessionRecord = mGameSessions.get(taskId);
if (!isTaskAssociatedWithGameSession) { if (gameSessionRecord == null) {
return; return;
} }
packageName = gameSessionRecord.getComponentName().getPackageName();
packageName = mGameSessions.get(taskId).getComponentName().getPackageName();
} }
try { if (packageName == null) {
mActivityManager.forceStopPackage(packageName, UserHandle.USER_CURRENT); return;
} catch (RemoteException e) {
e.rethrowFromSystemServer();
} }
Intent launchIntent = mActivityTaskManagerInternal.restartTaskActivityProcessIfVisible(taskId, packageName);
mContext.getPackageManager().getLaunchIntentForPackage(packageName);
mContext.startActivity(launchIntent);
} }
} }

View File

@@ -693,4 +693,15 @@ public abstract class ActivityTaskManagerInternal {
* @return a task ID if a valid task ID is found. Otherwise, return INVALID_TASK_ID * @return a task ID if a valid task ID is found. Otherwise, return INVALID_TASK_ID
*/ */
public abstract int getTaskToShowPermissionDialogOn(String pkgName, int uid); public abstract int getTaskToShowPermissionDialogOn(String pkgName, int uid);
/**
* Attempts to restart the process associated with the top most Activity associated with the
* given {@code packageName} in the task associated with the given {@code taskId}.
*
* This will request the process of the activity to restart with its saved state (via
* {@link android.app.Activity#onSaveInstanceState(Bundle)}) if possible. If the activity is in
* background the process will be killed keeping its record.
*/
public abstract void restartTaskActivityProcessIfVisible(
int taskId, @NonNull String packageName);
} }

View File

@@ -6695,5 +6695,31 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
.getTaskToShowPermissionDialogOn(pkgName, uid); .getTaskToShowPermissionDialogOn(pkgName, uid);
} }
} }
@Override
public void restartTaskActivityProcessIfVisible(int taskId, String packageName) {
synchronized (ActivityTaskManagerService.this.mGlobalLock) {
final Task task =
ActivityTaskManagerService.this.mRootWindowContainer
.anyTaskForId(taskId, MATCH_ATTACHED_TASK_ONLY);
if (task == null) {
Slog.w(TAG, "Failed to restart Activity. No task found for id: " + taskId);
return;
}
final ActivityRecord activity = task.getActivity(activityRecord -> {
return packageName.equals(activityRecord.packageName)
&& !activityRecord.finishing;
});
if (activity == null) {
Slog.w(TAG, "Failed to restart Activity. No Activity found for package name: "
+ packageName + " in task: " + taskId);
return;
}
activity.restartProcessIfVisible();
}
}
} }
} }

View File

@@ -32,7 +32,9 @@ import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import android.Manifest; import android.Manifest;
import android.annotation.Nullable; import android.annotation.Nullable;
@@ -80,6 +82,7 @@ import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.FunctionalUtils.ThrowingConsumer; import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.android.internal.util.ScreenshotHelper; import com.android.internal.util.ScreenshotHelper;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener; import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowManagerService;
@@ -125,6 +128,7 @@ public final class GameServiceProviderInstanceImplTest {
private static final Bitmap TEST_BITMAP; private static final Bitmap TEST_BITMAP;
static { static {
Picture picture = new Picture(); Picture picture = new Picture();
Canvas canvas = picture.beginRecording(200, 100); Canvas canvas = picture.beginRecording(200, 100);
@@ -146,6 +150,8 @@ public final class GameServiceProviderInstanceImplTest {
@Mock @Mock
private WindowManagerInternal mMockWindowManagerInternal; private WindowManagerInternal mMockWindowManagerInternal;
@Mock @Mock
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
@Mock
private IActivityManager mMockActivityManager; private IActivityManager mMockActivityManager;
@Mock @Mock
private ScreenshotHelper mMockScreenshotHelper; private ScreenshotHelper mMockScreenshotHelper;
@@ -227,6 +233,7 @@ public final class GameServiceProviderInstanceImplTest {
mMockActivityTaskManager, mMockActivityTaskManager,
mMockWindowManagerService, mMockWindowManagerService,
mMockWindowManagerInternal, mMockWindowManagerInternal,
mActivityTaskManagerInternal,
mFakeGameServiceConnector, mFakeGameServiceConnector,
mFakeGameSessionServiceConnector, mFakeGameSessionServiceConnector,
mMockScreenshotHelper); mMockScreenshotHelper);
@@ -1134,8 +1141,9 @@ public final class GameServiceProviderInstanceImplTest {
mFakeGameSessionService.getCapturedCreateInvocations().get(0) mFakeGameSessionService.getCapturedCreateInvocations().get(0)
.mGameSessionController.restartGame(10); .mGameSessionController.restartGame(10);
verify(mMockActivityManager).forceStopPackage(GAME_A_PACKAGE, UserHandle.USER_CURRENT); verify(mActivityTaskManagerInternal).restartTaskActivityProcessIfVisible(
assertThat(mMockContext.getLastStartedIntent()).isEqualTo(launchIntent); 10,
GAME_A_PACKAGE);
} }
@Test @Test
@@ -1158,7 +1166,8 @@ public final class GameServiceProviderInstanceImplTest {
verify(mMockActivityManager).registerProcessObserver(any()); verify(mMockActivityManager).registerProcessObserver(any());
verifyNoMoreInteractions(mMockActivityManager); verifyNoMoreInteractions(mMockActivityManager);
assertThat(mMockContext.getLastStartedIntent()).isNull(); verify(mActivityTaskManagerInternal, never())
.restartTaskActivityProcessIfVisible(anyInt(), anyString());
} }
@Test @Test
@@ -1172,6 +1181,8 @@ public final class GameServiceProviderInstanceImplTest {
mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY); mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY);
assertThrows(SecurityException.class, assertThrows(SecurityException.class,
() -> gameSessionController.restartGame(10)); () -> gameSessionController.restartGame(10));
verify(mActivityTaskManagerInternal, never())
.restartTaskActivityProcessIfVisible(anyInt(), anyString());
} }
private void startTask(int taskId, ComponentName componentName) { private void startTask(int taskId, ComponentName componentName) {
@@ -1398,7 +1409,6 @@ public final class GameServiceProviderInstanceImplTest {
} }
private final class MockContext extends ContextWrapper { private final class MockContext extends ContextWrapper {
private Intent mLastStartedIntent;
// Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
private final HashMap<String, Integer> mMockedPermissions = new HashMap<>(); private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
@@ -1424,11 +1434,6 @@ public final class GameServiceProviderInstanceImplTest {
return mMockPackageManager; return mMockPackageManager;
} }
@Override
public void startActivity(Intent intent) {
mLastStartedIntent = intent;
}
@Override @Override
public void enforceCallingPermission(String permission, @Nullable String message) { public void enforceCallingPermission(String permission, @Nullable String message) {
final Integer granted = mMockedPermissions.get(permission); final Integer granted = mMockedPermissions.get(permission);
@@ -1441,9 +1446,5 @@ public final class GameServiceProviderInstanceImplTest {
throw new SecurityException("[Test] permission denied: " + permission); throw new SecurityException("[Test] permission denied: " + permission);
} }
} }
Intent getLastStartedIntent() {
return mLastStartedIntent;
}
} }
} }