Add Universal Game Mode Hint mode switching to GameManagerService.
Bug: 214405108 Test: atest com.android.server.app.GameManagerServiceTests Test: Manual with "adb logcat | grep GameManagerService", and opening/backgrounding a game on the attached device. Change-Id: I0883e6bae316199aeeab9f7dda8934ccd67e7243
This commit is contained in:
@@ -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) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user