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.server.LocalServices;
import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerService;
@@ -62,6 +63,7 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide
activityTaskManager,
(WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE),
LocalServices.getService(WindowManagerInternal.class),
LocalServices.getService(ActivityTaskManagerInternal.class),
new GameServiceConnector(mContext, configuration),
new GameSessionServiceConnector(mContext, configuration),
new ScreenshotHelper(mContext));

View File

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

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