diff options
5 files changed, 82 insertions, 27 deletions
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java index 90b1b634f2ee..50e529c66e65 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java @@ -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)); diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index a200067b8f3f..4fd739ca5e3e 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -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); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 938e0edf05bb..6152676b9be7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -690,4 +690,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); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 81d8e4446c99..9d4dee4154e6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -6686,5 +6686,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(); + } + } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java index 5b551b1c183c..a8b340c6a391 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java @@ -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<>(); @@ -1425,11 +1435,6 @@ public final class GameServiceProviderInstanceImplTest { } @Override - public void startActivity(Intent intent) { - mLastStartedIntent = intent; - } - - @Override public void enforceCallingPermission(String permission, @Nullable String message) { final Integer granted = mMockedPermissions.get(permission); if (granted == null) { @@ -1441,9 +1446,5 @@ public final class GameServiceProviderInstanceImplTest { throw new SecurityException("[Test] permission denied: " + permission); } } - - Intent getLastStartedIntent() { - return mLastStartedIntent; - } } } |