Reevaluate game service when provider changes

The goal is to have the system server be responsive
to changes in the game service provider package
and reevaluate the GameService and GameSessionService
accordingly. So, for example, if the GameService
component is disabled and then becomes enabled,
the system server should detect that and bind
to the newly enabled GameService. This is a
likely real-world scenario if a game service
provider uses a server-side flag to enable
or disable the GameService component after
install time.

This change also fixes an issue where
GameService#disconnect() would never be called
because the service connector was always unbound
before the posted disconnect() call could complete.

Bug: 217215722
Test: atest CtsGameServiceTestCases GameServiceControllerTest GameServiceProviderSelectorImplTest
Change-Id: I5d0a603b98f341b956ea6abe82930fa13f4f7d0e
Merged-In: I5d0a603b98f341b956ea6abe82930fa13f4f7d0e
This commit is contained in:
shannonchen
2022-02-11 21:43:11 +00:00
committed by Shannon Chen
parent 4769e90c13
commit 0f5e540c2d
11 changed files with 436 additions and 214 deletions

View File

@@ -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<PackageInfo> 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);
}

View File

@@ -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
+ "}";
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
+ '}';
}
}

View File

@@ -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);
}

View File

@@ -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<IGameService> {
@@ -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(),

View File

@@ -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<Void> unusedPostDisconnectedFuture =
mGameServiceConnector.post(gameService -> {
gameService.disconnected();
});
mGameServiceConnector.unbind();
mGameServiceConnector.post(gameService -> {
gameService.disconnected();
}).whenComplete((result, t) -> {
mGameServiceConnector.unbind();
});
mGameSessionServiceConnector.unbind();
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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<BroadcastReceiver> 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;

View File

@@ -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) {