diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 9ba9d78591ddd..b813bc48118a5 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -159,7 +159,7 @@ public final class GameManagerService extends IGameManagerService.Stub { mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) { mGameServiceController = new GameServiceController( - BackgroundThread.getExecutor(), + context, BackgroundThread.getExecutor(), new GameServiceProviderSelectorImpl( context.getResources(), context.getPackageManager()), @@ -376,9 +376,10 @@ public final class GameManagerService extends IGameManagerService.Stub { /** * Called by games to communicate the current state to the platform. + * * @param packageName The client package name. - * @param gameState An object set to the current state. - * @param userId The user associated with this state. + * @param gameState An object set to the current state. + * @param userId The user associated with this state. */ public void setGameState(String packageName, @NonNull GameState gameState, @UserIdInt int userId) { @@ -1373,7 +1374,7 @@ public final class GameManagerService extends IGameManagerService.Stub { * @hide */ @VisibleForTesting - void updateConfigsForUser(@UserIdInt int userId, String ...packageNames) { + void updateConfigsForUser(@UserIdInt int userId, String... packageNames) { try { synchronized (mDeviceConfigLock) { for (final String packageName : packageNames) { @@ -1442,7 +1443,7 @@ public final class GameManagerService extends IGameManagerService.Stub { final List packages = mPackageManager.getInstalledPackagesAsUser(0, userId); return packages.stream().filter(e -> e.applicationInfo != null && e.applicationInfo.category - == ApplicationInfo.CATEGORY_GAME) + == ApplicationInfo.CATEGORY_GAME) .map(e -> e.packageName) .toArray(String[]::new); } diff --git a/services/core/java/com/android/server/app/GameServiceConfiguration.java b/services/core/java/com/android/server/app/GameServiceConfiguration.java new file mode 100644 index 0000000000000..1f31a87e157f0 --- /dev/null +++ b/services/core/java/com/android/server/app/GameServiceConfiguration.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2021 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 com.android.server.app; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.os.UserHandle; +import android.text.TextUtils; + +import java.util.Objects; + +/** + * Representation of a {@link android.service.games.GameService} provider configuration. + */ +final class GameServiceConfiguration { + private final String mPackageName; + @Nullable + private final GameServiceComponentConfiguration mGameServiceComponentConfiguration; + + GameServiceConfiguration( + @NonNull String packageName, + @Nullable GameServiceComponentConfiguration gameServiceComponentConfiguration) { + Objects.requireNonNull(packageName); + + mPackageName = packageName; + mGameServiceComponentConfiguration = gameServiceComponentConfiguration; + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + @Nullable + public GameServiceComponentConfiguration getGameServiceComponentConfiguration() { + return mGameServiceComponentConfiguration; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof GameServiceConfiguration)) { + return false; + } + + GameServiceConfiguration that = (GameServiceConfiguration) o; + return TextUtils.equals(mPackageName, that.mPackageName) + && Objects.equals(mGameServiceComponentConfiguration, + that.mGameServiceComponentConfiguration); + } + + @Override + public int hashCode() { + return Objects.hash(mPackageName, mGameServiceComponentConfiguration); + } + + @Override + public String toString() { + return "GameServiceConfiguration{" + + "packageName=" + + mPackageName + + ", gameServiceComponentConfiguration=" + + mGameServiceComponentConfiguration + + '}'; + } + + static final class GameServiceComponentConfiguration { + private final UserHandle mUserHandle; + private final ComponentName mGameServiceComponentName; + private final ComponentName mGameSessionServiceComponentName; + + GameServiceComponentConfiguration( + @NonNull UserHandle userHandle, @NonNull ComponentName gameServiceComponentName, + @NonNull ComponentName gameSessionServiceComponentName) { + Objects.requireNonNull(userHandle); + Objects.requireNonNull(gameServiceComponentName); + Objects.requireNonNull(gameSessionServiceComponentName); + + mUserHandle = userHandle; + mGameServiceComponentName = gameServiceComponentName; + mGameSessionServiceComponentName = gameSessionServiceComponentName; + } + + @NonNull + public UserHandle getUserHandle() { + return mUserHandle; + } + + @NonNull + public ComponentName getGameServiceComponentName() { + return mGameServiceComponentName; + } + + @NonNull + public ComponentName getGameSessionServiceComponentName() { + return mGameSessionServiceComponentName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof GameServiceComponentConfiguration)) { + return false; + } + + GameServiceComponentConfiguration that = + (GameServiceComponentConfiguration) o; + return mUserHandle.equals(that.mUserHandle) && mGameServiceComponentName.equals( + that.mGameServiceComponentName) + && mGameSessionServiceComponentName.equals( + that.mGameSessionServiceComponentName); + } + + @Override + public int hashCode() { + return Objects.hash(mUserHandle, + mGameServiceComponentName, + mGameSessionServiceComponentName); + } + + @Override + public String toString() { + return "GameServiceComponentConfiguration{" + + "userHandle=" + + mUserHandle + + ", gameServiceComponentName=" + + mGameServiceComponentName + + ", gameSessionServiceComponentName=" + + mGameSessionServiceComponentName + + "}"; + } + } +} diff --git a/services/core/java/com/android/server/app/GameServiceController.java b/services/core/java/com/android/server/app/GameServiceController.java index 397439a356f8b..db1ca97df888d 100644 --- a/services/core/java/com/android/server/app/GameServiceController.java +++ b/services/core/java/com/android/server/app/GameServiceController.java @@ -19,10 +19,17 @@ package com.android.server.app; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.WorkerThread; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.PatternMatcher; +import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; +import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration; import java.util.Objects; import java.util.concurrent.Executor; @@ -36,8 +43,8 @@ import java.util.concurrent.Executor; final class GameServiceController { private static final String TAG = "GameServiceController"; - private final Object mLock = new Object(); + private final Context mContext; private final Executor mBackgroundExecutor; private final GameServiceProviderSelector mGameServiceProviderSelector; private final GameServiceProviderInstanceFactory mGameServiceProviderInstanceFactory; @@ -46,18 +53,24 @@ final class GameServiceController { @Nullable private volatile String mGameServiceProviderOverride; @Nullable + private BroadcastReceiver mGameServicePackageChangedReceiver; + @Nullable private volatile SystemService.TargetUser mCurrentForegroundUser; @GuardedBy("mLock") @Nullable - private volatile GameServiceProviderConfiguration mActiveGameServiceProviderConfiguration; + private volatile GameServiceComponentConfiguration mActiveGameServiceComponentConfiguration; @GuardedBy("mLock") @Nullable private volatile GameServiceProviderInstance mGameServiceProviderInstance; + @GuardedBy("mLock") + @Nullable + private volatile String mActiveGameServiceProviderPackage; GameServiceController( - @NonNull Executor backgroundExecutor, + @NonNull Context context, @NonNull Executor backgroundExecutor, @NonNull GameServiceProviderSelector gameServiceProviderSelector, @NonNull GameServiceProviderInstanceFactory gameServiceProviderInstanceFactory) { + mContext = context; mGameServiceProviderInstanceFactory = gameServiceProviderInstanceFactory; mBackgroundExecutor = backgroundExecutor; mGameServiceProviderSelector = gameServiceProviderSelector; @@ -139,35 +152,92 @@ final class GameServiceController { } synchronized (mLock) { - GameServiceProviderConfiguration selectedGameServiceProviderConfiguration = + final GameServiceConfiguration selectedGameServiceConfiguration = mGameServiceProviderSelector.get(mCurrentForegroundUser, mGameServiceProviderOverride); + final String gameServicePackage = + selectedGameServiceConfiguration == null ? null : + selectedGameServiceConfiguration.getPackageName(); + final GameServiceComponentConfiguration gameServiceComponentConfiguration = + selectedGameServiceConfiguration == null ? null + : selectedGameServiceConfiguration + .getGameServiceComponentConfiguration(); - boolean didActiveGameServiceProviderChanged = - !Objects.equals(selectedGameServiceProviderConfiguration, - mActiveGameServiceProviderConfiguration); - if (!didActiveGameServiceProviderChanged) { + evaluateGameServiceProviderPackageChangedListenerLocked(gameServicePackage); + + boolean didActiveGameServiceProviderChange = + !Objects.equals(gameServiceComponentConfiguration, + mActiveGameServiceComponentConfiguration); + if (!didActiveGameServiceProviderChange) { return; } if (mGameServiceProviderInstance != null) { Slog.i(TAG, "Stopping Game Service provider: " - + mActiveGameServiceProviderConfiguration); + + mActiveGameServiceComponentConfiguration); mGameServiceProviderInstance.stop(); + mGameServiceProviderInstance = null; } - mActiveGameServiceProviderConfiguration = selectedGameServiceProviderConfiguration; - - if (mActiveGameServiceProviderConfiguration == null) { + mActiveGameServiceComponentConfiguration = gameServiceComponentConfiguration; + if (mActiveGameServiceComponentConfiguration == null) { return; } Slog.i(TAG, - "Starting Game Service provider: " + mActiveGameServiceProviderConfiguration); + "Starting Game Service provider: " + mActiveGameServiceComponentConfiguration); mGameServiceProviderInstance = mGameServiceProviderInstanceFactory.create( - mActiveGameServiceProviderConfiguration); + mActiveGameServiceComponentConfiguration); mGameServiceProviderInstance.start(); } } + + @GuardedBy("mLock") + private void evaluateGameServiceProviderPackageChangedListenerLocked( + @Nullable String gameServicePackage) { + if (TextUtils.equals(mActiveGameServiceProviderPackage, gameServicePackage)) { + return; + } + + if (mGameServicePackageChangedReceiver != null) { + mContext.unregisterReceiver(mGameServicePackageChangedReceiver); + mGameServicePackageChangedReceiver = null; + } + + mActiveGameServiceProviderPackage = gameServicePackage; + + if (TextUtils.isEmpty(mActiveGameServiceProviderPackage)) { + return; + } + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); + intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + intentFilter.addDataScheme("package"); + intentFilter.addDataSchemeSpecificPart(gameServicePackage, PatternMatcher.PATTERN_LITERAL); + mGameServicePackageChangedReceiver = new PackageChangedBroadcastReceiver( + gameServicePackage); + mContext.registerReceiver( + mGameServicePackageChangedReceiver, + intentFilter); + } + + private final class PackageChangedBroadcastReceiver extends BroadcastReceiver { + private final String mPackageName; + + PackageChangedBroadcastReceiver(String packageName) { + mPackageName = packageName; + } + + @Override + public void onReceive(Context context, Intent intent) { + if (!TextUtils.equals(intent.getData().getSchemeSpecificPart(), mPackageName)) { + return; + } + mBackgroundExecutor.execute( + GameServiceController.this::evaluateActiveGameServiceProvider); + } + } } diff --git a/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java b/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java deleted file mode 100644 index 7c8f251f35fed..0000000000000 --- a/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.app; - -import android.annotation.NonNull; -import android.content.ComponentName; -import android.os.UserHandle; - -import java.util.Objects; - -/** - * Representation of a {@link android.service.games.GameService} provider configuration. - */ -final class GameServiceProviderConfiguration { - private final UserHandle mUserHandle; - private final ComponentName mGameServiceComponentName; - private final ComponentName mGameSessionServiceComponentName; - - GameServiceProviderConfiguration( - @NonNull UserHandle userHandle, - @NonNull ComponentName gameServiceComponentName, - @NonNull ComponentName gameSessionServiceComponentName) { - Objects.requireNonNull(userHandle); - Objects.requireNonNull(gameServiceComponentName); - Objects.requireNonNull(gameSessionServiceComponentName); - - this.mUserHandle = userHandle; - this.mGameServiceComponentName = gameServiceComponentName; - this.mGameSessionServiceComponentName = gameSessionServiceComponentName; - } - - @NonNull - public UserHandle getUserHandle() { - return mUserHandle; - } - - @NonNull - public ComponentName getGameServiceComponentName() { - return mGameServiceComponentName; - } - - @NonNull - public ComponentName getGameSessionServiceComponentName() { - return mGameSessionServiceComponentName; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof GameServiceProviderConfiguration)) { - return false; - } - - GameServiceProviderConfiguration that = (GameServiceProviderConfiguration) o; - return mUserHandle.equals(that.mUserHandle) - && mGameServiceComponentName.equals(that.mGameServiceComponentName) - && mGameSessionServiceComponentName.equals(that.mGameSessionServiceComponentName); - } - - @Override - public int hashCode() { - return Objects.hash(mUserHandle, mGameServiceComponentName, - mGameSessionServiceComponentName); - } - - @Override - public String toString() { - return "GameServiceProviderConfiguration{" - + "mUserHandle=" - + mUserHandle - + ", gameServiceComponentName=" - + mGameServiceComponentName - + ", gameSessionServiceComponentName=" - + mGameSessionServiceComponentName - + '}'; - } -} diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java index 7640cc555446f..7dfaec0fc7ab8 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java @@ -18,12 +18,13 @@ package com.android.server.app; import android.annotation.NonNull; +import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration; + /** * Factory for creating {@link GameServiceProviderInstance}. */ interface GameServiceProviderInstanceFactory { @NonNull - GameServiceProviderInstance create(@NonNull - GameServiceProviderConfiguration gameServiceProviderConfiguration); + GameServiceProviderInstance create(@NonNull GameServiceComponentConfiguration configuration); } diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java index 73278e4710626..0abab6aafe736 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java @@ -30,6 +30,7 @@ import android.service.games.IGameSessionService; import com.android.internal.infra.ServiceConnector; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; +import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerService; @@ -43,9 +44,9 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide @NonNull @Override public GameServiceProviderInstance create( - @NonNull GameServiceProviderConfiguration gameServiceProviderConfiguration) { + @NonNull GameServiceComponentConfiguration configuration) { return new GameServiceProviderInstanceImpl( - gameServiceProviderConfiguration.getUserHandle(), + configuration.getUserHandle(), BackgroundThread.getExecutor(), mContext, new GameClassifierImpl(mContext.getPackageManager()), @@ -53,8 +54,8 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide ActivityTaskManager.getService(), (WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE), LocalServices.getService(WindowManagerInternal.class), - new GameServiceConnector(mContext, gameServiceProviderConfiguration), - new GameSessionServiceConnector(mContext, gameServiceProviderConfiguration)); + new GameServiceConnector(mContext, configuration), + new GameSessionServiceConnector(mContext, configuration)); } private static final class GameServiceConnector extends ServiceConnector.Impl { @@ -63,7 +64,7 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide GameServiceConnector( @NonNull Context context, - @NonNull GameServiceProviderConfiguration configuration) { + @NonNull GameServiceComponentConfiguration configuration) { super(context, new Intent(GameService.ACTION_GAME_SERVICE) .setComponent(configuration.getGameServiceComponentName()), BINDING_FLAGS, configuration.getUserHandle().getIdentifier(), @@ -86,7 +87,7 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide GameSessionServiceConnector( @NonNull Context context, - @NonNull GameServiceProviderConfiguration configuration) { + @NonNull GameServiceComponentConfiguration configuration) { super(context, new Intent(GameSessionService.ACTION_GAME_SESSION_SERVICE) .setComponent(configuration.getGameSessionServiceComponentName()), BINDING_FLAGS, configuration.getUserHandle().getIdentifier(), diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index 4eba77168b8e5..e8d9dadcb17af 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -244,11 +244,11 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan // TODO(b/204503192): It is possible that the game service is disconnected. In this // case we should avoid rebinding just to shut it down again. - AndroidFuture unusedPostDisconnectedFuture = - mGameServiceConnector.post(gameService -> { - gameService.disconnected(); - }); - mGameServiceConnector.unbind(); + mGameServiceConnector.post(gameService -> { + gameService.disconnected(); + }).whenComplete((result, t) -> { + mGameServiceConnector.unbind(); + }); mGameSessionServiceConnector.unbind(); } diff --git a/services/core/java/com/android/server/app/GameServiceProviderSelector.java b/services/core/java/com/android/server/app/GameServiceProviderSelector.java index 0f55b9ff31f44..d125f214751b6 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderSelector.java +++ b/services/core/java/com/android/server/app/GameServiceProviderSelector.java @@ -26,10 +26,10 @@ import com.android.server.SystemService; interface GameServiceProviderSelector { /** - * Returns the {@link GameServiceProviderConfiguration} associated with the selected Game + * Returns the {@link GameServiceConfiguration} associated with the selected Game * Service provider for the given user or {@code null} if none should be used. */ @Nullable - GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user, + GameServiceConfiguration get(@Nullable SystemService.TargetUser user, @Nullable String packageNameOverride); } diff --git a/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java b/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java index c1ad6685fbbbe..fc8530874a6ff 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java @@ -34,6 +34,7 @@ import android.util.Slog; import android.util.Xml; import com.android.server.SystemService; +import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -57,7 +58,7 @@ final class GameServiceProviderSelectorImpl implements GameServiceProviderSelect @Override @Nullable - public GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user, + public GameServiceConfiguration get(@Nullable SystemService.TargetUser user, @Nullable String packageNameOverride) { if (user == null) { return null; @@ -98,10 +99,10 @@ final class GameServiceProviderSelectorImpl implements GameServiceProviderSelect if (gameServiceResolveInfos == null || gameServiceResolveInfos.isEmpty()) { Slog.w(TAG, "No available game service found for user id: " + userId); - return null; + return new GameServiceConfiguration(gameServicePackage, null); } - GameServiceProviderConfiguration selectedProvider = null; + GameServiceConfiguration selectedProvider = null; for (ResolveInfo resolveInfo : gameServiceResolveInfos) { if (resolveInfo.serviceInfo == null) { continue; @@ -115,16 +116,18 @@ final class GameServiceProviderSelectorImpl implements GameServiceProviderSelect } selectedProvider = - new GameServiceProviderConfiguration( - new UserHandle(userId), - gameServiceServiceInfo.getComponentName(), - gameSessionServiceComponentName); + new GameServiceConfiguration( + gameServicePackage, + new GameServiceComponentConfiguration( + new UserHandle(userId), + gameServiceServiceInfo.getComponentName(), + gameSessionServiceComponentName)); break; } if (selectedProvider == null) { Slog.w(TAG, "No valid game service found for user id: " + userId); - return null; + return new GameServiceConfiguration(gameServicePackage, null); } return selectedProvider; diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java index 1c480eef96a1a..03eff2a1831b2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java @@ -20,13 +20,18 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; import android.content.pm.UserInfo; +import android.net.Uri; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; @@ -35,11 +40,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ConcurrentUtils; import com.android.server.SystemService; +import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; @@ -61,10 +68,14 @@ public final class GameServiceControllerTest { new ComponentName(PROVIDER_A_PACKAGE_NAME, "com.provider.a.ServiceA"); private static final ComponentName PROVIDER_A_SERVICE_B = new ComponentName(PROVIDER_A_PACKAGE_NAME, "com.provider.a.ServiceB"); + private static final ComponentName PROVIDER_A_SERVICE_C = + new ComponentName(PROVIDER_A_PACKAGE_NAME, "com.provider.a.ServiceC"); private MockitoSession mMockingSession; private GameServiceController mGameServiceManager; @Mock + private Context mMockContext; + @Mock private GameServiceProviderSelector mMockGameServiceProviderSelector; @Mock private GameServiceProviderInstanceFactory mMockGameServiceProviderInstanceFactory; @@ -77,7 +88,7 @@ public final class GameServiceControllerTest { .startMocking(); mGameServiceManager = new GameServiceController( - ConcurrentUtils.DIRECT_EXECUTOR, + mMockContext, ConcurrentUtils.DIRECT_EXECUTOR, mMockGameServiceProviderSelector, mMockGameServiceProviderInstanceFactory); } @@ -96,25 +107,30 @@ public final class GameServiceControllerTest { @Test public void notifyUserStarted_createsAndStartsNewInstance() { - GameServiceProviderConfiguration configurationA = - new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A, - PROVIDER_A_SERVICE_B); + GameServiceConfiguration configurationA = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_B)); FakeGameServiceProviderInstance instanceA = seedConfigurationForUser(USER_10, configurationA); mGameServiceManager.onBootComplete(); mGameServiceManager.notifyUserStarted(USER_10); - verify(mMockGameServiceProviderInstanceFactory).create(configurationA); + verify(mMockGameServiceProviderInstanceFactory).create( + configurationA.getGameServiceComponentConfiguration()); verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory); assertThat(instanceA.getIsRunning()).isTrue(); } @Test public void notifyUserStarted_sameUser_doesNotCreateNewInstance() { - GameServiceProviderConfiguration configurationA = - new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A, - PROVIDER_A_SERVICE_B); + GameServiceConfiguration configurationA = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_B)); FakeGameServiceProviderInstance instanceA = seedConfigurationForUser(USER_10, configurationA); @@ -122,16 +138,19 @@ public final class GameServiceControllerTest { mGameServiceManager.notifyUserStarted(USER_10); mGameServiceManager.notifyUserStarted(USER_10); - verify(mMockGameServiceProviderInstanceFactory).create(configurationA); + verify(mMockGameServiceProviderInstanceFactory).create( + configurationA.getGameServiceComponentConfiguration()); verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory); assertThat(instanceA.getIsRunning()).isTrue(); } @Test public void notifyUserUnlocking_noForegroundUser_ignores() { - GameServiceProviderConfiguration configurationA = - new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A, - PROVIDER_A_SERVICE_B); + GameServiceConfiguration configurationA = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_B)); FakeGameServiceProviderInstance instanceA = seedConfigurationForUser(USER_10, configurationA); @@ -144,9 +163,11 @@ public final class GameServiceControllerTest { @Test public void notifyUserUnlocking_sameAsForegroundUser_evaluatesProvider() { - GameServiceProviderConfiguration configurationA = - new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A, - PROVIDER_A_SERVICE_B); + GameServiceConfiguration configurationA = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_B)); seedNoConfigurationForUser(USER_10); mGameServiceManager.onBootComplete(); @@ -155,16 +176,19 @@ public final class GameServiceControllerTest { seedConfigurationForUser(USER_10, configurationA); mGameServiceManager.notifyUserUnlocking(USER_10); - verify(mMockGameServiceProviderInstanceFactory).create(configurationA); + verify(mMockGameServiceProviderInstanceFactory).create( + configurationA.getGameServiceComponentConfiguration()); verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory); assertThat(instanceA.getIsRunning()).isTrue(); } @Test public void notifyUserUnlocking_differentFromForegroundUser_ignores() { - GameServiceProviderConfiguration configurationA = - new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A, - PROVIDER_A_SERVICE_B); + GameServiceConfiguration configurationA = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_B)); seedNoConfigurationForUser(USER_10); mGameServiceManager.onBootComplete(); @@ -180,14 +204,18 @@ public final class GameServiceControllerTest { @Test public void notifyNewForegroundUser_differentUser_stopsPreviousInstanceAndThenStartsNewInstance() { - GameServiceProviderConfiguration configurationA = - new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A, - PROVIDER_A_SERVICE_B); + GameServiceConfiguration configurationA = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_B)); FakeGameServiceProviderInstance instanceA = seedConfigurationForUser(USER_10, configurationA); - GameServiceProviderConfiguration configurationB = - new GameServiceProviderConfiguration(USER_HANDLE_11, PROVIDER_A_SERVICE_A, - PROVIDER_A_SERVICE_B); + GameServiceConfiguration configurationB = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_11, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_B)); FakeGameServiceProviderInstance instanceB = seedConfigurationForUser(USER_11, configurationB); InOrder instancesInOrder = Mockito.inOrder(instanceA, instanceB); @@ -196,8 +224,50 @@ public final class GameServiceControllerTest { mGameServiceManager.notifyUserStarted(USER_10); mGameServiceManager.notifyNewForegroundUser(USER_11); - verify(mMockGameServiceProviderInstanceFactory).create(configurationA); - verify(mMockGameServiceProviderInstanceFactory).create(configurationB); + verify(mMockGameServiceProviderInstanceFactory).create( + configurationA.getGameServiceComponentConfiguration()); + verify(mMockGameServiceProviderInstanceFactory).create( + configurationB.getGameServiceComponentConfiguration()); + instancesInOrder.verify(instanceA).start(); + instancesInOrder.verify(instanceA).stop(); + instancesInOrder.verify(instanceB).start(); + verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory); + assertThat(instanceA.getIsRunning()).isFalse(); + assertThat(instanceB.getIsRunning()).isTrue(); + } + + @Test + public void packageChanges_reevaluatesGameServiceProvider() { + GameServiceConfiguration configurationA = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_B)); + FakeGameServiceProviderInstance instanceA = + seedConfigurationForUser(USER_10, configurationA); + + mGameServiceManager.onBootComplete(); + mGameServiceManager.notifyUserStarted(USER_10); + ArgumentCaptor broadcastReceiverArgumentCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mMockContext).registerReceiver(broadcastReceiverArgumentCaptor.capture(), any()); + + GameServiceConfiguration configurationB = + new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + PROVIDER_A_SERVICE_A, + PROVIDER_A_SERVICE_C)); + FakeGameServiceProviderInstance instanceB = + seedConfigurationForUser(USER_10, configurationA); + Intent intent = new Intent(); + intent.setData(Uri.parse("package:" + PROVIDER_A_PACKAGE_NAME)); + broadcastReceiverArgumentCaptor.getValue().onReceive(mMockContext, intent); + + InOrder instancesInOrder = Mockito.inOrder(instanceA, instanceB); + verify(mMockGameServiceProviderInstanceFactory).create( + configurationA.getGameServiceComponentConfiguration()); + verify(mMockGameServiceProviderInstanceFactory).create( + configurationB.getGameServiceComponentConfiguration()); instancesInOrder.verify(instanceA).start(); instancesInOrder.verify(instanceA).stop(); instancesInOrder.verify(instanceB).start(); @@ -207,15 +277,16 @@ public final class GameServiceControllerTest { } private void seedNoConfigurationForUser(SystemService.TargetUser user) { - when(mMockGameServiceProviderSelector.get(user, "")).thenReturn(null); + when(mMockGameServiceProviderSelector.get(user, null)).thenReturn(null); } private FakeGameServiceProviderInstance seedConfigurationForUser(SystemService.TargetUser user, - GameServiceProviderConfiguration configuration) { - when(mMockGameServiceProviderSelector.get(user, "")).thenReturn(configuration); + GameServiceConfiguration configuration) { + when(mMockGameServiceProviderSelector.get(user, null)).thenReturn(configuration); FakeGameServiceProviderInstance instanceForConfiguration = spy(new FakeGameServiceProviderInstance()); - when(mMockGameServiceProviderInstanceFactory.create(configuration)) + when(mMockGameServiceProviderInstanceFactory.create( + configuration.getGameServiceComponentConfiguration())) .thenReturn(instanceForConfiguration); return instanceForConfiguration; diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java index 23a6a49856f7d..cf9ba1e2638f0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java @@ -18,6 +18,7 @@ package com.android.server.app; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.google.common.truth.Truth.assertThat; @@ -26,7 +27,6 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.ComponentName; @@ -49,6 +49,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.SystemService; +import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration; import com.google.common.collect.ImmutableList; @@ -137,10 +138,10 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(null, null); - assertThat(gameServiceProviderConfiguration).isNull(); + assertThat(gameServiceConfiguration).isNull(); } @Test @@ -154,15 +155,16 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(managedTargetUser(USER_HANDLE_10), null); - assertThat(gameServiceProviderConfiguration).isNull(); + assertThat(gameServiceConfiguration).isNull(); } @Test public void get_noSystemGameService_returnsNull() throws Exception { + seedSystemGameServicePackageName(""); seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10, resolveInfo(GAME_SERVICE_SERVICE_INFO)); seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT); @@ -170,14 +172,14 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null); - assertThat(gameServiceProviderConfiguration).isNull(); + assertThat(gameServiceConfiguration).isNull(); } @Test - public void get_noGameServiceProvidersAvailable_returnsNull() + public void get_noGameServiceProvidersAvailable_returnsGameServicePackageName() throws Exception { seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME); seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10); @@ -186,28 +188,30 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null); - assertThat(gameServiceProviderConfiguration).isNull(); + assertThat(gameServiceConfiguration).isEqualTo( + new GameServiceConfiguration(GAME_SERVICE_PACKAGE_NAME, null)); } @Test - public void get_gameServiceProviderHasNoMetaData_returnsNull() + public void get_gameServiceProviderHasNoMetaData_returnsGameServicePackageName() throws Exception { seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME); seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10, resolveInfo(GAME_SERVICE_SERVICE_INFO_WITHOUT_META_DATA)); seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null); - assertThat(gameServiceProviderConfiguration).isNull(); + assertThat(gameServiceConfiguration).isEqualTo( + new GameServiceConfiguration(GAME_SERVICE_PACKAGE_NAME, null)); } @Test - public void get_gameSessionServiceDoesNotExist_returnsNull() + public void get_gameSessionServiceDoesNotExist_returnsGameServicePackageName() throws Exception { seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME); seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10, @@ -217,14 +221,15 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null); - assertThat(gameServiceProviderConfiguration).isNull(); + assertThat(gameServiceConfiguration).isEqualTo( + new GameServiceConfiguration(GAME_SERVICE_PACKAGE_NAME, null)); } @Test - public void get_metaDataWrongFirstTag_returnsNull() throws Exception { + public void get_metaDataWrongFirstTag_returnsGameServicePackageName() throws Exception { seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME); seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10, resolveInfo(GAME_SERVICE_SERVICE_INFO)); @@ -233,10 +238,11 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_wrong_first_tag.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null); - assertThat(gameServiceProviderConfiguration).isNull(); + assertThat(gameServiceConfiguration).isEqualTo( + new GameServiceConfiguration(GAME_SERVICE_PACKAGE_NAME, null)); } @Test @@ -250,15 +256,17 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null); - GameServiceProviderConfiguration expectedGameServiceProviderConfiguration = - new GameServiceProviderConfiguration(USER_HANDLE_10, - GAME_SERVICE_COMPONENT, - GAME_SESSION_SERVICE_COMPONENT); - assertThat(gameServiceProviderConfiguration).isEqualTo( - expectedGameServiceProviderConfiguration); + GameServiceConfiguration expectedGameServiceConfiguration = + new GameServiceConfiguration( + GAME_SERVICE_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + GAME_SERVICE_COMPONENT, + GAME_SESSION_SERVICE_COMPONENT)); + assertThat(gameServiceConfiguration).isEqualTo( + expectedGameServiceConfiguration); } @Test @@ -276,15 +284,17 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null); - GameServiceProviderConfiguration expectedGameServiceProviderConfiguration = - new GameServiceProviderConfiguration(USER_HANDLE_10, - GAME_SERVICE_B_COMPONENT, - GAME_SESSION_SERVICE_COMPONENT); - assertThat(gameServiceProviderConfiguration).isEqualTo( - expectedGameServiceProviderConfiguration); + GameServiceConfiguration expectedGameServiceConfiguration = + new GameServiceConfiguration( + GAME_SERVICE_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + GAME_SERVICE_B_COMPONENT, + GAME_SESSION_SERVICE_COMPONENT)); + assertThat(gameServiceConfiguration).isEqualTo( + expectedGameServiceConfiguration); } @Test @@ -299,15 +309,17 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null); - GameServiceProviderConfiguration expectedGameServiceProviderConfiguration = - new GameServiceProviderConfiguration(USER_HANDLE_10, - GAME_SERVICE_COMPONENT, - GAME_SESSION_SERVICE_COMPONENT); - assertThat(gameServiceProviderConfiguration).isEqualTo( - expectedGameServiceProviderConfiguration); + GameServiceConfiguration expectedGameServiceConfiguration = + new GameServiceConfiguration( + GAME_SERVICE_PACKAGE_NAME, + new GameServiceComponentConfiguration(USER_HANDLE_10, + GAME_SERVICE_COMPONENT, + GAME_SESSION_SERVICE_COMPONENT)); + assertThat(gameServiceConfiguration).isEqualTo( + expectedGameServiceConfiguration); } @Test @@ -322,16 +334,19 @@ public final class GameServiceProviderSelectorImplTest { GAME_SERVICE_META_DATA_RES_ID, "res/xml/game_service_metadata_valid.xml"); - GameServiceProviderConfiguration gameServiceProviderConfiguration = + GameServiceConfiguration gameServiceConfiguration = mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), GAME_SERVICE_PACKAGE_NAME); - GameServiceProviderConfiguration expectedGameServiceProviderConfiguration = - new GameServiceProviderConfiguration(USER_HANDLE_10, - GAME_SERVICE_COMPONENT, - GAME_SESSION_SERVICE_COMPONENT); - assertThat(gameServiceProviderConfiguration).isEqualTo( - expectedGameServiceProviderConfiguration); + GameServiceConfiguration expectedGameServiceConfiguration = + new GameServiceConfiguration( + GAME_SERVICE_PACKAGE_NAME, + new GameServiceComponentConfiguration( + USER_HANDLE_10, + GAME_SERVICE_COMPONENT, + GAME_SESSION_SERVICE_COMPONENT)); + assertThat(gameServiceConfiguration).isEqualTo( + expectedGameServiceConfiguration); } private void seedSystemGameServicePackageName(String gameServicePackageName) {