diff options
author | 2023-01-23 19:00:28 +0000 | |
---|---|---|
committer | 2023-01-23 19:00:28 +0000 | |
commit | e83680e21e432b9dc9b8cf6e7ea570e5598bdda1 (patch) | |
tree | 3c1cafeee30252b0e8730a4f16766b9466064b5f | |
parent | 2fd7d4b33330c8c179df9f0382416a17fda80a8e (diff) | |
parent | 3f91d78c7a34d72cc3647dc070c8b0ecb77903f1 (diff) |
Merge "Add Universal Game Mode Hint mode switching to GameManagerService."
3 files changed, 185 insertions, 26 deletions
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index d195103534a5..893c8b5d2698 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -40,6 +40,7 @@ import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameManagerService; import android.app.IGameModeListener; +import android.app.IUidObserver; import android.app.StatsManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -165,38 +166,19 @@ public final class GameManagerService extends IGameManagerService.Stub { private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>(); @Nullable private final GameServiceController mGameServiceController; + private final Object mUidObserverLock = new Object(); + @VisibleForTesting + @Nullable + final UidObserver mUidObserver; + @GuardedBy("mUidObserverLock") + private final Set<Integer> mForegroundGameUids = new HashSet<>(); public GameManagerService(Context context) { this(context, createServiceThread().getLooper()); } GameManagerService(Context context, Looper looper) { - mContext = context; - mHandler = new SettingsHandler(looper); - mPackageManager = mContext.getPackageManager(); - mUserManager = mContext.getSystemService(UserManager.class); - mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); - mSystemDir = new File(Environment.getDataDirectory(), "system"); - mSystemDir.mkdirs(); - FileUtils.setPermissions(mSystemDir.toString(), - FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH, - -1, -1); - mGameModeInterventionListFile = new AtomicFile(new File(mSystemDir, - GAME_MODE_INTERVENTION_LIST_FILE_NAME)); - FileUtils.setPermissions(mGameModeInterventionListFile.getBaseFile().getAbsolutePath(), - FileUtils.S_IRUSR | FileUtils.S_IWUSR - | FileUtils.S_IRGRP | FileUtils.S_IWGRP, - -1, -1); - if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) { - mGameServiceController = new GameServiceController( - context, BackgroundThread.getExecutor(), - new GameServiceProviderSelectorImpl( - context.getResources(), - context.getPackageManager()), - new GameServiceProviderInstanceFactoryImpl(context)); - } else { - mGameServiceController = null; - } + this(context, looper, Environment.getDataDirectory()); } @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @@ -227,6 +209,14 @@ public final class GameManagerService extends IGameManagerService.Stub { } else { mGameServiceController = null; } + mUidObserver = new UidObserver(); + try { + ActivityManager.getService().registerUidObserver(mUidObserver, + ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, + ActivityManager.PROCESS_STATE_UNKNOWN, null); + } catch (RemoteException e) { + Slog.w(TAG, "Could not register UidObserver"); + } } @Override @@ -2152,4 +2142,66 @@ public final class GameManagerService extends IGameManagerService.Stub { * load dynamic library for frame rate overriding JNI calls */ private static native void nativeSetOverrideFrameRate(int uid, float frameRate); + + final class UidObserver extends IUidObserver.Stub { + @Override + public void onUidIdle(int uid, boolean disabled) {} + + @Override + public void onUidGone(int uid, boolean disabled) { + synchronized (mUidObserverLock) { + disableGameMode(uid); + } + } + + @Override + public void onUidActive(int uid) {} + + @Override + public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { + synchronized (mUidObserverLock) { + if (ActivityManager.isProcStateBackground(procState)) { + disableGameMode(uid); + return; + } + + final String[] packages = mContext.getPackageManager().getPackagesForUid(uid); + if (packages == null || packages.length == 0) { + return; + } + + final int userId = mContext.getUserId(); + if (!Arrays.stream(packages).anyMatch(p -> isPackageGame(p, userId))) { + return; + } + + if (mForegroundGameUids.isEmpty()) { + Slog.v(TAG, "Game power mode ON (process state was changed to foreground)"); + mPowerManagerInternal.setPowerMode(Mode.GAME, true); + } + mForegroundGameUids.add(uid); + } + } + + private void disableGameMode(int uid) { + synchronized (mUidObserverLock) { + if (!mForegroundGameUids.contains(uid)) { + return; + } + mForegroundGameUids.remove(uid); + if (!mForegroundGameUids.isEmpty()) { + return; + } + Slog.v(TAG, + "Game power mode OFF (process remomved or state changed to background)"); + mPowerManagerInternal.setPowerMode(Mode.GAME, false); + } + } + + @Override + public void onUidCachedChanged(int uid, boolean cached) {} + + @Override + public void onUidProcAdjChanged(int uid) {} + } } diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 33ac73560968..ea0481e03b97 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -43,6 +43,9 @@ <!-- needed by TrustManagerServiceTest to access LockSettings' secure storage --> <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> + <!-- needed by GameManagerServiceTest because GameManager creates a UidObserver --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> + <application android:testOnly="true" android:debuggable="true"> <uses-library android:name="android.test.runner" /> 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 c4c50424943f..32243f04f6e8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -45,6 +46,7 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.GameManager; import android.app.GameModeConfiguration; import android.app.GameModeInfo; @@ -2212,4 +2214,106 @@ public class GameManagerServiceTests { assertEquals(expectedInterventionListOutput, gameManagerService.getInterventionList(mPackageName, USER_ID_1)); } + + @Test + public void testGamePowerMode_gamePackage() throws Exception { + GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1); + String[] packages = {mPackageName}; + when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true); + } + + @Test + public void testGamePowerMode_twoGames() throws Exception { + GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1); + String[] packages1 = {mPackageName}; + when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1); + String someGamePkg = "some.game"; + String[] packages2 = {someGamePkg}; + int somePackageId = DEFAULT_PACKAGE_UID + 1; + when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2); + HashMap<Integer, Boolean> powerState = new HashMap<>(); + doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1))) + .when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean()); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + assertTrue(powerState.get(Mode.GAME)); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0); + gameManagerService.mUidObserver.onUidStateChanged( + somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + assertTrue(powerState.get(Mode.GAME)); + gameManagerService.mUidObserver.onUidStateChanged( + somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0); + assertFalse(powerState.get(Mode.GAME)); + } + + @Test + public void testGamePowerMode_twoGamesOverlap() throws Exception { + GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1); + String[] packages1 = {mPackageName}; + when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1); + String someGamePkg = "some.game"; + String[] packages2 = {someGamePkg}; + int somePackageId = DEFAULT_PACKAGE_UID + 1; + when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + gameManagerService.mUidObserver.onUidStateChanged( + somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0); + gameManagerService.mUidObserver.onUidStateChanged( + somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0); + verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true); + verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false); + } + + @Test + public void testGamePowerMode_released() throws Exception { + GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1); + String[] packages = {mPackageName}; + when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0); + verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false); + } + + @Test + public void testGamePowerMode_noPackage() throws Exception { + GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1); + String[] packages = {}; + when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true); + } + + @Test + public void testGamePowerMode_notAGamePackage() throws Exception { + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE); + GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1); + String[] packages = {"someapp"}; + when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true); + } + + @Test + public void testGamePowerMode_notAGamePackageNotReleased() throws Exception { + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE); + GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1); + String[] packages = {"someapp"}; + when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0); + gameManagerService.mUidObserver.onUidStateChanged( + DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0); + verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, false); + } } |