diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java index 8768a02ba95e0..f92194d3ffac5 100644 --- a/core/java/android/app/GameManager.java +++ b/core/java/android/app/GameManager.java @@ -187,7 +187,9 @@ public final class GameManager { * Sets the game mode for the given package. *

* The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. - * + *

+ * Setting the game mode on a non-game application or setting a game to + * {@link #GAME_MODE_UNSUPPORTED} will have no effect. * @hide */ @SystemApi diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl index f695ed32e8dda..aea097d069dce 100644 --- a/core/java/android/app/IGameManagerService.aidl +++ b/core/java/android/app/IGameManagerService.aidl @@ -19,20 +19,34 @@ package android.app; import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; +import android.app.IGameModeListener; /** * @hide */ interface IGameManagerService { int getGameMode(String packageName, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void setGameMode(String packageName, int gameMode, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") int[] getAvailableGameModes(String packageName); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") boolean isAngleEnabled(String packageName, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void notifyGraphicsEnvironmentSetup(String packageName, int userId); void setGameState(String packageName, in GameState gameState, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") GameModeInfo getGameModeInfo(String packageName, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_GAME_SERVICE)") void setGameServiceProvider(String packageName); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") float getResolutionScalingFactor(String packageName, int gameMode, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void updateCustomGameModeConfiguration(String packageName, in GameModeConfiguration gameModeConfig, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") + void addGameModeListener(IGameModeListener gameModeListener); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") + void removeGameModeListener(IGameModeListener gameModeListener); } diff --git a/core/java/android/app/IGameModeListener.aidl b/core/java/android/app/IGameModeListener.aidl new file mode 100644 index 0000000000000..77fcac07f7e74 --- /dev/null +++ b/core/java/android/app/IGameModeListener.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +/** @hide */ +interface IGameModeListener { + /** + * Called when the game mode of the user has changed. + */ + void onGameModeChanged(String packageName, int gameModeFrom, int gameModeTo, int userId); +} diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java index 207ab2a271124..fac3a0ecdec2a 100644 --- a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java +++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java @@ -50,15 +50,11 @@ public final class GameManagerTests { mPackageName = mContext.getPackageName(); // Reset the Game Mode for the test app, since it persists across invocations. - mGameManager.setGameMode(mPackageName, GameManager.GAME_MODE_UNSUPPORTED); + mGameManager.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD); } @Test public void testPublicApiGameModeGetterSetter() { - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, - mGameManager.getGameMode()); - - mGameManager.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD); assertEquals(GameManager.GAME_MODE_STANDARD, mGameManager.getGameMode()); @@ -73,10 +69,6 @@ public final class GameManagerTests { @Test public void testPrivilegedGameModeGetterSetter() { - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, - mGameManager.getGameMode(mPackageName)); - - mGameManager.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD); assertEquals(GameManager.GAME_MODE_STANDARD, mGameManager.getGameMode(mPackageName)); diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 14601da634d99..b92c1635d7c61 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -39,6 +39,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameManagerService; +import android.app.IGameModeListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -58,10 +59,12 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManagerInternal; import android.os.Process; +import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserManager; @@ -132,6 +135,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private final Context mContext; private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); + private final Object mGameModeListenerLock = new Object(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) final Handler mHandler; private final PackageManager mPackageManager; @@ -145,6 +149,9 @@ public final class GameManagerService extends IGameManagerService.Stub { private final ArrayMap mSettings = new ArrayMap<>(); @GuardedBy("mDeviceConfigLock") private final ArrayMap mConfigs = new ArrayMap<>(); + // listener to caller uid map + @GuardedBy("mGameModeListenerLock") + private final ArrayMap mGameModeListeners = new ArrayMap<>(); @Nullable private final GameServiceController mGameServiceController; @@ -598,6 +605,14 @@ public final class GameManagerService extends IGameManagerService.Stub { } } + // used to check if the override package config has any game mode config, if not, it's + // considered empty and safe to delete from settings + boolean hasActiveGameModeConfig() { + synchronized (mModeConfigLock) { + return !mModeConfigs.isEmpty(); + } + } + /** * GameModeConfiguration contains all the values for all the interventions associated with * a game mode. @@ -693,7 +708,8 @@ public final class GameManagerService extends IGameManagerService.Stub { public boolean isActive() { return (mGameMode == GameManager.GAME_MODE_STANDARD || mGameMode == GameManager.GAME_MODE_PERFORMANCE - || mGameMode == GameManager.GAME_MODE_BATTERY) + || mGameMode == GameManager.GAME_MODE_BATTERY + || mGameMode == GameManager.GAME_MODE_CUSTOM) && !willGamePerformOptimizations(mGameMode); } @@ -741,7 +757,8 @@ public final class GameManagerService extends IGameManagerService.Stub { } private int getAvailableGameModesBitfield() { - int field = 0; + int field = modeToBitmask(GameManager.GAME_MODE_CUSTOM) + | modeToBitmask(GameManager.GAME_MODE_STANDARD); synchronized (mModeConfigLock) { for (final int mode : mModeConfigs.keySet()) { field |= modeToBitmask(mode); @@ -753,13 +770,6 @@ public final class GameManagerService extends IGameManagerService.Stub { if (mPerfModeOptedIn) { field |= modeToBitmask(GameManager.GAME_MODE_PERFORMANCE); } - // The lowest bit is reserved for UNSUPPORTED, STANDARD is supported if we support any - // other mode. - if (field > 1) { - field |= modeToBitmask(GameManager.GAME_MODE_STANDARD); - } else { - field |= modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED); - } return field; } @@ -881,7 +891,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private final class LocalService extends GameManagerInternal { @Override public float getResolutionScalingFactor(String packageName, int userId) { - final int gameMode = getGameModeFromSettings(packageName, userId); + final int gameMode = getGameModeFromSettingsUnchecked(packageName, userId); return getResolutionScalingFactorInternal(packageName, gameMode, userId); } } @@ -960,7 +970,7 @@ public final class GameManagerService extends IGameManagerService.Stub { config = mConfigs.get(packageName); } if (config == null) { - return new int[]{}; + return new int[]{GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM}; } return config.getAvailableGameModes(); } @@ -986,12 +996,13 @@ public final class GameManagerService extends IGameManagerService.Stub { return getAvailableGameModesUnchecked(packageName); } - private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) { + private @GameMode int getGameModeFromSettingsUnchecked(String packageName, + @UserIdInt int userId) { synchronized (mLock) { if (!mSettings.containsKey(userId)) { Slog.d(TAG, "User ID '" + userId + "' does not have a Game Mode" - + " selected for package: '" + packageName + "'"); - return GameManager.GAME_MODE_UNSUPPORTED; + + " selected for package: '" + packageName + "'"); + return GameManager.GAME_MODE_STANDARD; } return mSettings.get(userId).getGameModeLocked(packageName); @@ -1024,12 +1035,12 @@ public final class GameManagerService extends IGameManagerService.Stub { // return a value if the package name is valid. Next, check if the caller has the necessary // permission and return a value. Do this check last, since it can throw an exception. if (isValidPackageName(packageName, userId)) { - return getGameModeFromSettings(packageName, userId); + return getGameModeFromSettingsUnchecked(packageName, userId); } // Since the package name doesn't match, check the caller has the necessary permission. checkPermission(Manifest.permission.MANAGE_GAME_MODE); - return getGameModeFromSettings(packageName, userId); + return getGameModeFromSettingsUnchecked(packageName, userId); } /** @@ -1054,7 +1065,7 @@ public final class GameManagerService extends IGameManagerService.Stub { return null; } - final @GameMode int activeGameMode = getGameModeFromSettings(packageName, userId); + final @GameMode int activeGameMode = getGameModeFromSettingsUnchecked(packageName, userId); final GamePackageConfiguration config = getConfig(packageName, userId); if (config != null) { final @GameMode int[] optedInGameModes = config.getOptedInGameModes(); @@ -1077,7 +1088,10 @@ public final class GameManagerService extends IGameManagerService.Stub { } return gameModeInfoBuilder.build(); } else { - return new GameModeInfo.Builder().setActiveGameMode(activeGameMode).build(); + return new GameModeInfo.Builder() + .setActiveGameMode(activeGameMode) + .setAvailableGameModes(getAvailableGameModesUnchecked(packageName)) + .build(); } } @@ -1091,11 +1105,11 @@ public final class GameManagerService extends IGameManagerService.Stub { throws SecurityException { checkPermission(Manifest.permission.MANAGE_GAME_MODE); - if (!isPackageGame(packageName, userId)) { - // Restrict to games only. + if (!isPackageGame(packageName, userId) || gameMode == GameManager.GAME_MODE_UNSUPPORTED) { + // Restrict to games and valid game modes only. return; } - + int fromGameMode; synchronized (mLock) { userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "setGameMode", @@ -1107,9 +1121,21 @@ public final class GameManagerService extends IGameManagerService.Stub { return; } GameManagerSettings userSettings = mSettings.get(userId); + fromGameMode = userSettings.getGameModeLocked(packageName); userSettings.setGameModeLocked(packageName, gameMode); } updateInterventions(packageName, gameMode, userId); + synchronized (mGameModeListenerLock) { + for (IGameModeListener listener : mGameModeListeners.keySet()) { + Binder.allowBlocking(listener.asBinder()); + try { + listener.onGameModeChanged(packageName, fromGameMode, gameMode, userId); + } catch (RemoteException ex) { + Slog.w(TAG, "Cannot notify game mode change for listener added by " + + mGameModeListeners.get(listener)); + } + } + } sendUserMessage(userId, WRITE_SETTINGS, "SET_GAME_MODE", WRITE_DELAY_MILLIS); sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE, "SET_GAME_MODE", 0 /*delayMillis*/); @@ -1334,6 +1360,57 @@ public final class GameManagerService extends IGameManagerService.Stub { + internalConfig.getScaling()); } + /** + * Adds a game mode listener. + * + * @throws SecurityException if caller doesn't have + * {@link android.Manifest.permission#MANAGE_GAME_MODE} + * permission. + */ + @Override + @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) + public void addGameModeListener(@NonNull IGameModeListener listener) { + checkPermission(Manifest.permission.MANAGE_GAME_MODE); + try { + final IBinder listenerBinder = listener.asBinder(); + listenerBinder.linkToDeath(new DeathRecipient() { + @Override public void binderDied() { + // TODO(b/258851194): add traces on binder death based listener removal + removeGameModeListenerUnchecked(listener); + listenerBinder.unlinkToDeath(this, 0 /*flags*/); + } + }, 0 /*flags*/); + synchronized (mGameModeListenerLock) { + mGameModeListeners.put(listener, Binder.getCallingUid()); + } + } catch (RemoteException ex) { + Slog.e(TAG, + "Failed to link death recipient for IGameModeListener from caller " + + Binder.getCallingUid() + ", abandoned its listener registration", ex); + } + } + + /** + * Removes a game mode listener. + * + * @throws SecurityException if caller doesn't have + * {@link android.Manifest.permission#MANAGE_GAME_MODE} + * permission. + */ + @Override + @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) + public void removeGameModeListener(@NonNull IGameModeListener listener) { + // TODO(b/258851194): add traces on manual listener removal + checkPermission(Manifest.permission.MANAGE_GAME_MODE); + removeGameModeListenerUnchecked(listener); + } + + private void removeGameModeListenerUnchecked(IGameModeListener listener) { + synchronized (mGameModeListenerLock) { + mGameModeListeners.remove(listener); + } + } + /** * Notified when boot is completed. */ @@ -1474,7 +1551,8 @@ public final class GameManagerService extends IGameManagerService.Stub { final GamePackageConfiguration packageConfig = getConfig(packageName, userId); if (gameMode == GameManager.GAME_MODE_STANDARD || gameMode == GameManager.GAME_MODE_UNSUPPORTED || packageConfig == null - || packageConfig.willGamePerformOptimizations(gameMode)) { + || packageConfig.willGamePerformOptimizations(gameMode) + || packageConfig.getGameModeConfiguration(gameMode) == null) { resetFps(packageName, userId); // resolution scaling does not need to be reset as it's now read dynamically on game // restart, see #getResolutionScalingFactor and CompatModePackages#getCompatScale. @@ -1562,13 +1640,9 @@ public final class GameManagerService extends IGameManagerService.Stub { if (!bitFieldContainsModeBitmask(modesBitfield, gameModeToReset)) { return; } - // if the game mode to reset is the only mode other than standard mode or there - // is device config, the entire package config override is removed. - if (Integer.bitCount(modesBitfield) <= 2 || deviceConfig == null) { + configOverride.removeModeConfig(gameModeToReset); + if (!configOverride.hasActiveGameModeConfig()) { settings.removeConfigOverride(packageName); - } else { - // otherwise we reset the mode by removing the game mode config override - configOverride.removeModeConfig(gameModeToReset); } } else { settings.removeConfigOverride(packageName); @@ -1596,20 +1670,12 @@ public final class GameManagerService extends IGameManagerService.Stub { // want to check if we support selectable game modes modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED); if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) { - if (bitFieldContainsModeBitmask(modesBitfield, - GameManager.GAME_MODE_STANDARD)) { - // If the current set mode isn't supported, - // but we support STANDARD, then set the mode to STANDARD. - newGameMode = GameManager.GAME_MODE_STANDARD; - } else { - // If we don't support any game modes, then set to UNSUPPORTED - newGameMode = GameManager.GAME_MODE_UNSUPPORTED; - } + // always default to STANDARD if there is no mode config + newGameMode = GameManager.GAME_MODE_STANDARD; } - } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) { - // If we have no config for the package, but the configured mode is not - // UNSUPPORTED, then set to UNSUPPORTED - newGameMode = GameManager.GAME_MODE_UNSUPPORTED; + } else { + // always default to STANDARD if there is no package config + newGameMode = GameManager.GAME_MODE_STANDARD; } return newGameMode; } diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java index 1e688371fa4d8..638bc4e7a0700 100644 --- a/services/core/java/com/android/server/app/GameManagerSettings.java +++ b/services/core/java/com/android/server/app/GameManagerSettings.java @@ -90,9 +90,14 @@ public class GameManagerSettings { */ int getGameModeLocked(String packageName) { if (mGameModes.containsKey(packageName)) { - return mGameModes.get(packageName); + final int gameMode = mGameModes.get(packageName); + if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) { + // force replace cached UNSUPPORTED mode with STANDARD starting in U + return GameManager.GAME_MODE_STANDARD; + } + return gameMode; } - return GameManager.GAME_MODE_UNSUPPORTED; + return GameManager.GAME_MODE_STANDARD; } /** @@ -255,7 +260,7 @@ public class GameManagerSettings { XmlUtils.skipCurrentTag(parser); return; } - int gameMode = GameManager.GAME_MODE_UNSUPPORTED; + int gameMode; try { gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE); } catch (XmlPullParserException e) { @@ -282,7 +287,7 @@ public class GameManagerSettings { + type); } } - if (config.getAvailableGameModes().length > 1) { + if (config.hasActiveGameModeConfig()) { mConfigOverrides.put(name, config); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 09d0683bdb4e5..dc77762795c7e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -33,6 +33,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,6 +44,7 @@ import android.app.GameManager; import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; +import android.app.IGameModeListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -57,7 +59,9 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.hardware.power.Mode; import android.os.Bundle; +import android.os.IBinder; import android.os.PowerManagerInternal; +import android.os.RemoteException; import android.os.UserManager; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; @@ -74,7 +78,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoSession; @@ -110,6 +116,9 @@ public class GameManagerServiceTests { private UserManager mMockUserManager; private BroadcastReceiver mShutDownActionReceiver; + @Captor + ArgumentCaptor mDeathRecipientCaptor; + // Stolen from ConnectivityServiceTest.MockContext class MockContext extends ContextWrapper { private static final String TAG = "MockContext"; @@ -444,7 +453,7 @@ public class GameManagerServiceTests { } /** - * By default game mode is not supported. + * By default game mode is set to STANDARD */ @Test public void testGameModeDefaultValue() { @@ -454,7 +463,7 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); mockModifyGameModeGranted(); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_1)); } @@ -470,7 +479,7 @@ public class GameManagerServiceTests { mockModifyGameModeGranted(); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_2); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_2)); } @@ -486,7 +495,7 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName); mockModifyGameModeGranted(); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_1)); // We need to make sure the mode is supported before setting it. mockDeviceConfigAll(); @@ -679,7 +688,8 @@ public class GameManagerServiceTests { public void testDeviceConfigDefault() { mockDeviceConfigDefault(); mockModifyGameModeGranted(); - checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1)); + checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -689,7 +699,8 @@ public class GameManagerServiceTests { public void testDeviceConfigNone() { mockDeviceConfigNone(); mockModifyGameModeGranted(); - checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1)); + checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -699,7 +710,8 @@ public class GameManagerServiceTests { public void testDeviceConfigInvalid() { mockDeviceConfigInvalid(); mockModifyGameModeGranted(); - checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1)); + checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -709,7 +721,8 @@ public class GameManagerServiceTests { public void testDeviceConfigMalformed() { mockDeviceConfigMalformed(); mockModifyGameModeGranted(); - checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1)); + checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -727,7 +740,7 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_PERFORMANCE, "120", "0.3"); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120); } @@ -747,7 +760,7 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_BATTERY, "60", "0.5"); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f); checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60); } @@ -769,7 +782,8 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_BATTERY, "60", "0.5"); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f); @@ -863,7 +877,7 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_PERFORMANCE); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90); } @@ -886,7 +900,7 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_BATTERY); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f); checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30); } @@ -910,7 +924,8 @@ public class GameManagerServiceTests { gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1, -1); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f); @@ -939,7 +954,8 @@ public class GameManagerServiceTests { GameManager.GAME_MODE_BATTERY); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f); checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120); checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f); @@ -956,7 +972,7 @@ public class GameManagerServiceTests { mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } @@ -969,7 +985,8 @@ public class GameManagerServiceTests { mockDeviceConfigNone(); mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); } /** @@ -981,7 +998,8 @@ public class GameManagerServiceTests { mockDeviceConfigNone(); mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), - GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); } /** @@ -995,7 +1013,7 @@ public class GameManagerServiceTests { mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -1009,7 +1027,7 @@ public class GameManagerServiceTests { mockModifyGameModeGranted(); checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1), GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); } /** @@ -1181,7 +1199,7 @@ public class GameManagerServiceTests { /** * Ensure that, if a game no longer supports any game modes, we set the game mode to - * UNSUPPORTED + * STANDARD */ @Test public void testUnsetInvalidGameMode() throws Exception { @@ -1192,7 +1210,7 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_1)); } @@ -1260,7 +1278,8 @@ public class GameManagerServiceTests { assertTrue(gameModeInfo.isFpsOverrideAllowed()); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService); assertEquals(new GameModeConfiguration.Builder() @@ -1286,7 +1305,8 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_BATTERY, gameModeInfo.getActiveGameMode()); checkReportedAvailableGameModes(gameManagerService, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService); assertNotNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)); @@ -1305,7 +1325,8 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameModeInfo.getActiveGameMode()); checkReportedAvailableGameModes(gameManagerService, - GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService); assertNotNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE)); @@ -1313,7 +1334,7 @@ public class GameManagerServiceTests { } @Test - public void testGetGameModeInfoWithUnsupportedGameMode() { + public void testGetGameModeInfoWithDefaultGameModes() { mockDeviceConfigNone(); mockModifyGameModeGranted(); GameManagerService gameManagerService = @@ -1321,9 +1342,11 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1); - assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameModeInfo.getActiveGameMode()); - checkReportedAvailableGameModes(gameManagerService); - + assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode()); + checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); + assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM)); + assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD)); assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)); assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE)); } @@ -1363,7 +1386,7 @@ public class GameManagerServiceTests { GameModeInfo gameModeInfo) { checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY); assertTrue(gameModeInfo.isFpsOverrideAllowed()); @@ -1381,7 +1404,7 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode()); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_BATTERY, - GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService, GameManager.GAME_MODE_BATTERY); assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)); @@ -1399,7 +1422,8 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode()); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_CUSTOM); checkReportedOptedInGameModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE); assertNotNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY)); @@ -1526,14 +1550,14 @@ public class GameManagerServiceTests { /* Expected fileOutput (order may vary) # user 1001: - com.android.app2 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.5,fps=60 + com.android.app2 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.5,fps=60 com.android.app1 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 - com.android.app0 0 2 angle=0,scaling=0.6,fps=120 3 angle=0,scaling=0.7,fps=30 + com.android.app0 1 2 angle=0,scaling=0.6,fps=120 3 angle=0,scaling=0.7,fps=30 # user 1002: - com.android.app2 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 + com.android.app2 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 com.android.app1 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 - com.android.app0 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 + com.android.app0 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30 The current game mode would only be set to non-zero if the current user have that game installed. */ @@ -1550,7 +1574,7 @@ public class GameManagerServiceTests { assertEquals(splitLine[6], "angle=0,scaling=0.5,fps=60"); splitLine = fileOutput.get(1).split("\\s+"); assertEquals(splitLine[0], "com.android.app1"); - assertEquals(splitLine[2], "0"); + assertEquals(splitLine[2], "1"); assertEquals(splitLine[3], "2"); assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90"); assertEquals(splitLine[5], "3"); @@ -1573,7 +1597,7 @@ public class GameManagerServiceTests { splitLine = fileOutput.get(0).split("\\s+"); assertEquals(splitLine[0], "com.android.app2"); - assertEquals(splitLine[2], "0"); + assertEquals(splitLine[2], "1"); assertEquals(splitLine[3], "2"); assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90"); assertEquals(splitLine[5], "3"); @@ -1587,7 +1611,7 @@ public class GameManagerServiceTests { assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30"); splitLine = fileOutput.get(2).split("\\s+"); assertEquals(splitLine[0], "com.android.app0"); - assertEquals(splitLine[2], "0"); + assertEquals(splitLine[2], "1"); assertEquals(splitLine[3], "2"); assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90"); assertEquals(splitLine[5], "3"); @@ -1608,7 +1632,7 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_2); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_STANDARD, - GameManager.GAME_MODE_BATTERY); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_CUSTOM); assertEquals(gameManagerService.getGameMode(mPackageName, USER_ID_1), GameManager.GAME_MODE_BATTERY); @@ -1617,15 +1641,15 @@ public class GameManagerServiceTests { assertEquals(gameManagerService.getGameMode(mPackageName, USER_ID_2), GameManager.GAME_MODE_STANDARD); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_STANDARD, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_PERFORMANCE); - gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_2); - gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_PERFORMANCE, + GameManager.GAME_MODE_CUSTOM); switchUser(gameManagerService, USER_ID_2, USER_ID_1); + assertEquals(gameManagerService.getGameMode(mPackageName, USER_ID_1), + GameManager.GAME_MODE_BATTERY); checkReportedAvailableGameModes(gameManagerService, GameManager.GAME_MODE_STANDARD, - GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_PERFORMANCE); - gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_2); - gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); + GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_PERFORMANCE, + GameManager.GAME_MODE_CUSTOM); } @Test @@ -1859,6 +1883,53 @@ public class GameManagerServiceTests { ArgumentMatchers.eq(0.0f)); } + @Test + public void testAddGameModeListener() throws RemoteException { + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + mockModifyGameModeGranted(); + + IGameModeListener mockListener = Mockito.mock(IGameModeListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + gameManagerService.addGameModeListener(mockListener); + verify(binder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); + + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); + verify(mockListener).onGameModeChanged(mPackageName, GameManager.GAME_MODE_STANDARD, + GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); + reset(mockListener); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1); + verify(mockListener).onGameModeChanged(mPackageName, GameManager.GAME_MODE_PERFORMANCE, + GameManager.GAME_MODE_BATTERY, USER_ID_1); + reset(mockListener); + + mDeathRecipientCaptor.getValue().binderDied(); + verify(binder).unlinkToDeath(eq(mDeathRecipientCaptor.getValue()), anyInt()); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_CUSTOM, USER_ID_1); + verify(mockListener, never()).onGameModeChanged(anyString(), anyInt(), anyInt(), anyInt()); + } + + @Test + public void testRemoveGameModeListener() throws RemoteException { + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + mockModifyGameModeGranted(); + + IGameModeListener mockListener = Mockito.mock(IGameModeListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + + gameManagerService.addGameModeListener(mockListener); + gameManagerService.removeGameModeListener(mockListener); + gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); + verify(mockListener, never()).onGameModeChanged(anyString(), anyInt(), anyInt(), anyInt()); + } + private static void deleteFolder(File folder) { File[] files = folder.listFiles(); if (files != null) { diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java index e8dd541b05313..582c78bce54c7 100644 --- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java @@ -192,7 +192,7 @@ public class GameManagerServiceSettingsTests { final Context context = InstrumentationRegistry.getContext(); GameManagerSettings settings = new GameManagerSettings(context.getFilesDir()); assertTrue(settings.readPersistentDataLocked()); - assertEquals(0, settings.getGameModeLocked(PACKAGE_NAME_1)); + assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_1)); assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2)); assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3));