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 Merged-In: I0883e6bae316199aeeab9f7dda8934ccd67e7243
This commit is contained in:
@@ -51,6 +51,7 @@ import android.app.GameManager.GameMode;
|
||||
import android.app.GameModeInfo;
|
||||
import android.app.GameState;
|
||||
import android.app.IGameManagerService;
|
||||
import android.app.IUidObserver;
|
||||
import android.app.compat.PackageOverride;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -118,6 +119,7 @@ import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Service to manage game related features.
|
||||
@@ -171,40 +173,19 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
private final ArrayMap<String, GamePackageConfiguration> mOverrideConfigs = 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);
|
||||
mPlatformCompat = IPlatformCompat.Stub.asInterface(
|
||||
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
|
||||
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)
|
||||
@@ -237,6 +218,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
|
||||
@@ -1874,4 +1863,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" />
|
||||
|
||||
@@ -29,13 +29,16 @@ 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;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.GameManager;
|
||||
import android.app.GameModeInfo;
|
||||
import android.app.GameState;
|
||||
@@ -203,6 +206,23 @@ public class GameManagerServiceTests {
|
||||
LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
|
||||
}
|
||||
|
||||
private void mockAppCategory(String packageName, @ApplicationInfo.Category int category)
|
||||
throws Exception {
|
||||
reset(mMockPackageManager);
|
||||
final ApplicationInfo gameApplicationInfo = new ApplicationInfo();
|
||||
gameApplicationInfo.category = category;
|
||||
gameApplicationInfo.packageName = packageName;
|
||||
final PackageInfo pi = new PackageInfo();
|
||||
pi.packageName = packageName;
|
||||
pi.applicationInfo = gameApplicationInfo;
|
||||
final List<PackageInfo> packages = new ArrayList<>();
|
||||
packages.add(pi);
|
||||
when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
|
||||
.thenReturn(packages);
|
||||
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(gameApplicationInfo);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
LocalServices.removeServiceForTest(PowerManagerInternal.class);
|
||||
@@ -1597,4 +1617,113 @@ public class GameManagerServiceTests {
|
||||
ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
|
||||
ArgumentMatchers.eq(0.0f));
|
||||
}
|
||||
|
||||
private GameManagerService createServiceAndStartUser(int userId) {
|
||||
GameManagerService gameManagerService = new GameManagerService(mMockContext,
|
||||
mTestLooper.getLooper());
|
||||
startUser(gameManagerService, userId);
|
||||
return gameManagerService;
|
||||
}
|
||||
|
||||
@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