Merge "Add IGameStateListener" into udc-qpr-dev
This commit is contained in:
@@ -20,6 +20,7 @@ import android.app.GameModeConfiguration;
|
||||
import android.app.GameModeInfo;
|
||||
import android.app.GameState;
|
||||
import android.app.IGameModeListener;
|
||||
import android.app.IGameStateListener;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
@@ -49,4 +50,6 @@ interface IGameManagerService {
|
||||
void addGameModeListener(IGameModeListener gameModeListener);
|
||||
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)")
|
||||
void removeGameModeListener(IGameModeListener gameModeListener);
|
||||
void addGameStateListener(IGameStateListener gameStateListener);
|
||||
void removeGameStateListener(IGameStateListener gameStateListener);
|
||||
}
|
||||
|
||||
27
core/java/android/app/IGameStateListener.aidl
Normal file
27
core/java/android/app/IGameStateListener.aidl
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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;
|
||||
|
||||
import android.app.GameState;
|
||||
|
||||
/** @hide */
|
||||
interface IGameStateListener {
|
||||
/**
|
||||
* Called when the state of the game has changed.
|
||||
*/
|
||||
oneway void onGameStateChanged(String packageName, in GameState state, int userId);
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import android.app.GameModeInfo;
|
||||
import android.app.GameState;
|
||||
import android.app.IGameManagerService;
|
||||
import android.app.IGameModeListener;
|
||||
import android.app.IGameStateListener;
|
||||
import android.app.StatsManager;
|
||||
import android.app.UidObserver;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -148,6 +149,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
private final Object mLock = new Object();
|
||||
private final Object mDeviceConfigLock = new Object();
|
||||
private final Object mGameModeListenerLock = new Object();
|
||||
private final Object mGameStateListenerLock = new Object();
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
|
||||
final Handler mHandler;
|
||||
private final PackageManager mPackageManager;
|
||||
@@ -164,6 +166,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
// listener to caller uid map
|
||||
@GuardedBy("mGameModeListenerLock")
|
||||
private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>();
|
||||
@GuardedBy("mGameStateListenerLock")
|
||||
private final ArrayMap<IGameStateListener, Integer> mGameStateListeners = new ArrayMap<>();
|
||||
@Nullable
|
||||
private final GameServiceController mGameServiceController;
|
||||
private final Object mUidObserverLock = new Object();
|
||||
@@ -352,6 +356,16 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
loadingBoostDuration);
|
||||
}
|
||||
}
|
||||
synchronized (mGameStateListenerLock) {
|
||||
for (IGameStateListener listener : mGameStateListeners.keySet()) {
|
||||
try {
|
||||
listener.onGameStateChanged(packageName, gameState, userId);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Cannot notify game state change for listener added by "
|
||||
+ mGameStateListeners.get(listener));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CANCEL_GAME_LOADING_MODE: {
|
||||
@@ -1473,6 +1487,43 @@ public final class GameManagerService extends IGameManagerService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a game state listener.
|
||||
*/
|
||||
@Override
|
||||
public void addGameStateListener(@NonNull IGameStateListener listener) {
|
||||
try {
|
||||
final IBinder listenerBinder = listener.asBinder();
|
||||
listenerBinder.linkToDeath(new DeathRecipient() {
|
||||
@Override public void binderDied() {
|
||||
removeGameStateListenerUnchecked(listener);
|
||||
listenerBinder.unlinkToDeath(this, 0 /*flags*/);
|
||||
}
|
||||
}, 0 /*flags*/);
|
||||
synchronized (mGameStateListenerLock) {
|
||||
mGameStateListeners.put(listener, Binder.getCallingUid());
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
Slog.e(TAG,
|
||||
"Failed to link death recipient for IGameStateListener from caller "
|
||||
+ Binder.getCallingUid() + ", abandoned its listener registration", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a game state listener.
|
||||
*/
|
||||
@Override
|
||||
public void removeGameStateListener(@NonNull IGameStateListener listener) {
|
||||
removeGameStateListenerUnchecked(listener);
|
||||
}
|
||||
|
||||
private void removeGameStateListenerUnchecked(IGameStateListener listener) {
|
||||
synchronized (mGameStateListenerLock) {
|
||||
mGameStateListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified when boot is completed.
|
||||
*/
|
||||
|
||||
@@ -52,6 +52,7 @@ import android.app.GameModeConfiguration;
|
||||
import android.app.GameModeInfo;
|
||||
import android.app.GameState;
|
||||
import android.app.IGameModeListener;
|
||||
import android.app.IGameStateListener;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
@@ -1578,6 +1579,71 @@ public class GameManagerServiceTests {
|
||||
assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddGameStateListener() throws Exception {
|
||||
mockModifyGameModeGranted();
|
||||
GameManagerService gameManagerService =
|
||||
new GameManagerService(mMockContext, mTestLooper.getLooper());
|
||||
mockDeviceConfigAll();
|
||||
startUser(gameManagerService, USER_ID_1);
|
||||
|
||||
IGameStateListener mockListener = Mockito.mock(IGameStateListener.class);
|
||||
IBinder binder = Mockito.mock(IBinder.class);
|
||||
when(mockListener.asBinder()).thenReturn(binder);
|
||||
gameManagerService.addGameStateListener(mockListener);
|
||||
verify(binder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt());
|
||||
|
||||
mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO);
|
||||
GameState gameState = new GameState(true, GameState.MODE_NONE);
|
||||
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
|
||||
assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt());
|
||||
|
||||
mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME);
|
||||
gameState = new GameState(true, GameState.MODE_NONE);
|
||||
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
|
||||
assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1);
|
||||
reset(mockListener);
|
||||
|
||||
gameState = new GameState(false, GameState.MODE_CONTENT);
|
||||
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1);
|
||||
reset(mockListener);
|
||||
|
||||
mDeathRecipientCaptor.getValue().binderDied();
|
||||
verify(binder).unlinkToDeath(eq(mDeathRecipientCaptor.getValue()), anyInt());
|
||||
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
|
||||
assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveGameStateListener() throws Exception {
|
||||
mockModifyGameModeGranted();
|
||||
GameManagerService gameManagerService =
|
||||
new GameManagerService(mMockContext, mTestLooper.getLooper());
|
||||
mockDeviceConfigAll();
|
||||
startUser(gameManagerService, USER_ID_1);
|
||||
|
||||
IGameStateListener mockListener = Mockito.mock(IGameStateListener.class);
|
||||
IBinder binder = Mockito.mock(IBinder.class);
|
||||
when(mockListener.asBinder()).thenReturn(binder);
|
||||
|
||||
gameManagerService.addGameStateListener(mockListener);
|
||||
gameManagerService.removeGameStateListener(mockListener);
|
||||
mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME);
|
||||
GameState gameState = new GameState(false, GameState.MODE_CONTENT);
|
||||
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
|
||||
assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt());
|
||||
}
|
||||
|
||||
private List<String> readGameModeInterventionList() throws Exception {
|
||||
final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(),
|
||||
"system/game_mode_intervention.list");
|
||||
|
||||
Reference in New Issue
Block a user