Merge "Reevaluate game service when provider changes" into tm-dev

This commit is contained in:
Shannon Chen
2022-02-18 22:21:02 +00:00
committed by Android (Google) Code Review
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) {