Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a3174d08 authored by Xiang Wang's avatar Xiang Wang Committed by Android (Google) Code Review
Browse files

Merge "Add IGameStateListener" into udc-qpr-dev

parents 20fb6e46 2b0c8364
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.GameModeConfiguration;
import android.app.GameModeInfo;
import android.app.GameModeInfo;
import android.app.GameState;
import android.app.GameState;
import android.app.IGameModeListener;
import android.app.IGameModeListener;
import android.app.IGameStateListener;


/**
/**
 * @hide
 * @hide
@@ -49,4 +50,6 @@ interface IGameManagerService {
    void addGameModeListener(IGameModeListener gameModeListener);
    void addGameModeListener(IGameModeListener gameModeListener);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)")
    void removeGameModeListener(IGameModeListener gameModeListener);
    void removeGameModeListener(IGameModeListener gameModeListener);
    void addGameStateListener(IGameStateListener gameStateListener);
    void removeGameStateListener(IGameStateListener gameStateListener);
}
}
+27 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
}
+51 −0
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ import android.app.GameModeInfo;
import android.app.GameState;
import android.app.GameState;
import android.app.IGameManagerService;
import android.app.IGameManagerService;
import android.app.IGameModeListener;
import android.app.IGameModeListener;
import android.app.IGameStateListener;
import android.app.StatsManager;
import android.app.StatsManager;
import android.app.UidObserver;
import android.app.UidObserver;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
@@ -148,6 +149,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
    private final Object mLock = new Object();
    private final Object mLock = new Object();
    private final Object mDeviceConfigLock = new Object();
    private final Object mDeviceConfigLock = new Object();
    private final Object mGameModeListenerLock = new Object();
    private final Object mGameModeListenerLock = new Object();
    private final Object mGameStateListenerLock = new Object();
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    final Handler mHandler;
    final Handler mHandler;
    private final PackageManager mPackageManager;
    private final PackageManager mPackageManager;
@@ -164,6 +166,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
    // listener to caller uid map
    // listener to caller uid map
    @GuardedBy("mGameModeListenerLock")
    @GuardedBy("mGameModeListenerLock")
    private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>();
    private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>();
    @GuardedBy("mGameStateListenerLock")
    private final ArrayMap<IGameStateListener, Integer> mGameStateListeners = new ArrayMap<>();
    @Nullable
    @Nullable
    private final GameServiceController mGameServiceController;
    private final GameServiceController mGameServiceController;
    private final Object mUidObserverLock = new Object();
    private final Object mUidObserverLock = new Object();
@@ -352,6 +356,16 @@ public final class GameManagerService extends IGameManagerService.Stub {
                                    loadingBoostDuration);
                                    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;
                    break;
                }
                }
                case CANCEL_GAME_LOADING_MODE: {
                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.
     * Notified when boot is completed.
     */
     */
+66 −0
Original line number Original line Diff line number Diff line
@@ -52,6 +52,7 @@ import android.app.GameModeConfiguration;
import android.app.GameModeInfo;
import android.app.GameModeInfo;
import android.app.GameState;
import android.app.GameState;
import android.app.IGameModeListener;
import android.app.IGameModeListener;
import android.app.IGameStateListener;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.ContextWrapper;
@@ -1578,6 +1579,71 @@ public class GameManagerServiceTests {
        assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
        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 {
    private List<String> readGameModeInterventionList() throws Exception {
        final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(),
        final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(),
                "system/game_mode_intervention.list");
                "system/game_mode_intervention.list");