Merge "Add GameManager service." into sc-dev

This commit is contained in:
Peiyong Lin
2021-01-29 23:47:19 +00:00
committed by Android (Google) Code Review
13 changed files with 947 additions and 0 deletions

View File

@@ -72,6 +72,7 @@ import android.content.res.Resources;
import android.content.rollback.RollbackManagerFrameworkInitializer;
import android.debug.AdbManager;
import android.debug.IAdbManager;
import android.graphics.GameManager;
import android.graphics.fonts.FontManager;
import android.hardware.ConsumerIrManager;
import android.hardware.ISerialManager;
@@ -1426,6 +1427,16 @@ public final class SystemServiceRegistry {
return new MediaMetricsManager(service, ctx.getUserId());
}});
registerService(Context.GAME_SERVICE, GameManager.class,
new CachedServiceFetcher<GameManager>() {
@Override
public GameManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
return new GameManager(ctx.getOuterContext(),
ctx.mMainThread.getHandler());
}
});
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline

View File

@@ -5417,6 +5417,16 @@ public abstract class Context {
*/
public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition";
/**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.graphics.GameManager}.
*
* @see #getSystemService(String)
*
* @hide
*/
public static final String GAME_SERVICE = "game";
/**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.

View File

@@ -0,0 +1,29 @@
// 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.
android_test {
name: "FrameworksCoreGameManagerTests",
// Include all test java files
srcs: ["src/**/*.java"],
static_libs: [
"androidx.test.rules",
"frameworks-base-testutils",
"junit",
"truth-prebuilt",
],
libs: ["android.test.runner"],
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.graphics.gamemanagertests"
android:sharedUserId="android.uid.system" >
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.graphics.gamemanagertests"
android:label="Game Manager Tests"/>
</manifest>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<configuration description="Runs Game Manager Tests.">
<option name="test-suite-tag" value="apct"/>
<option name="test-suite-tag" value="apct-instrumentation"/>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksCoreGameManagerTests.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.graphics.gamemanagertests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false" />
</test>
</configuration>

View File

@@ -0,0 +1,83 @@
/*
* 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 android.graphics;
import static junit.framework.Assert.assertEquals;
import android.graphics.GameManager.GameMode;
import android.util.ArrayMap;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Unit tests for {@link GameManager}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class GameManagerTests {
private static final String PACKAGE_NAME_0 = "com.android.app0";
private static final String PACKAGE_NAME_1 = "com.android.app1";
private TestGameManagerService mService;
private GameManager mGameManager;
@Before
public void setUp() {
mService = new TestGameManagerService();
mGameManager = new GameManager(
InstrumentationRegistry.getContext(), mService);
}
@Test
public void testGameModeGetterSetter() {
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
mGameManager.getGameMode(PACKAGE_NAME_0));
mGameManager.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD);
assertEquals(GameManager.GAME_MODE_STANDARD,
mGameManager.getGameMode(PACKAGE_NAME_1));
mGameManager.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE);
assertEquals(GameManager.GAME_MODE_PERFORMANCE,
mGameManager.getGameMode(PACKAGE_NAME_1));
}
private final class TestGameManagerService extends IGameManagerService.Stub {
private final ArrayMap<Pair<String, Integer>, Integer> mGameModes = new ArrayMap<>();
@Override
public @GameMode int getGameMode(String packageName, int userId) {
final Pair key = Pair.create(packageName, userId);
if (mGameModes.containsKey(key)) {
return mGameModes.get(key);
}
return GameManager.GAME_MODE_UNSUPPORTED;
}
@Override
public void setGameMode(String packageName, @GameMode int gameMode, int userId) {
mGameModes.put(Pair.create(packageName, userId), gameMode);
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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 android.graphics;
import android.annotation.IntDef;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* The GameManager allows system apps to modify and query the game mode of apps.
*
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@SystemService(Context.GAME_SERVICE)
public final class GameManager {
private static final String TAG = "GameManager";
private final Context mContext;
private final IGameManagerService mService;
@IntDef(flag = false, prefix = { "GAME_MODE_" }, value = {
GAME_MODE_UNSUPPORTED, // 0
GAME_MODE_STANDARD, // 1
GAME_MODE_PERFORMANCE, // 2
GAME_MODE_BATTERY, // 3
})
@Retention(RetentionPolicy.SOURCE)
public @interface GameMode {}
public static final int GAME_MODE_UNSUPPORTED = 0;
public static final int GAME_MODE_STANDARD = 1;
public static final int GAME_MODE_PERFORMANCE = 2;
public static final int GAME_MODE_BATTERY = 3;
public GameManager(Context context, Handler handler) throws ServiceNotFoundException {
mContext = context;
mService = IGameManagerService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.GAME_SERVICE));
}
@VisibleForTesting
public GameManager(Context context, IGameManagerService gameManagerService) {
mContext = context;
mService = gameManagerService;
}
/**
* Returns the game mode for the given package.
*/
// TODO(b/178111358): Add @RequiresPermission.
@UserHandleAware
public @GameMode int getGameMode(String packageName) {
try {
return mService.getGameMode(packageName, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets the game mode for the given package.
*/
// TODO(b/178111358): Add @RequiresPermission.
@UserHandleAware
public void setGameMode(String packageName, @GameMode int gameMode) {
try {
mService.setGameMode(packageName, gameMode, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -0,0 +1,25 @@
/*
** Copyright 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 android.graphics;
/**
* @hide
*/
interface IGameManagerService {
int getGameMode(String packageName, int userId);
void setGameMode(String packageName, int gameMode, int userId);
}

View File

@@ -0,0 +1,215 @@
/*
* 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.graphics;
import android.annotation.NonNull;
import android.content.Context;
import android.graphics.GameManager;
import android.graphics.GameManager.GameMode;
import android.graphics.IGameManagerService;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
/**
* Service to manage game related features.
*
* <p>Game service is a core service that monitors, coordinates game related features,
* as well as collect metrics.</p>
*
* @hide
*/
public final class GameManagerService extends IGameManagerService.Stub {
public static final String TAG = "GameManagerService";
private static final boolean DEBUG = false;
static final int WRITE_SETTINGS = 1;
static final int REMOVE_SETTINGS = 2;
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
private final Context mContext;
private final Object mLock = new Object();
private final Handler mHandler;
@GuardedBy("mLock")
private final ArrayMap<Integer, Settings> mSettings = new ArrayMap<>();
public GameManagerService(Context context) {
this(context, createServiceThread().getLooper());
}
GameManagerService(Context context, Looper looper) {
mContext = context;
mHandler = new SettingsHandler(looper);
}
class SettingsHandler extends Handler {
SettingsHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
doHandleMessage(msg);
}
void doHandleMessage(Message msg) {
switch (msg.what) {
case WRITE_SETTINGS: {
final int userId = (int) msg.obj;
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_SETTINGS, msg.obj);
if (mSettings.containsKey(userId)) {
Settings userSettings = mSettings.get(userId);
userSettings.writePersistentDataLocked();
}
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
break;
}
case REMOVE_SETTINGS: {
final int userId = (int) msg.obj;
synchronized (mLock) {
// Since the user was removed, ignore previous write message
// and do write here.
removeMessages(WRITE_SETTINGS, msg.obj);
removeMessages(REMOVE_SETTINGS, msg.obj);
if (mSettings.containsKey(userId)) {
final Settings userSettings = mSettings.get(userId);
mSettings.remove(userId);
userSettings.writePersistentDataLocked();
}
}
break;
}
}
}
}
/**
* SystemService lifecycle for GameService.
* @hide
*/
public static class Lifecycle extends SystemService {
private GameManagerService mService;
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
mService = new GameManagerService(getContext());
publishBinderService(Context.GAME_SERVICE, mService);
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_BOOT_COMPLETED) {
mService.onBootCompleted();
}
}
@Override
public void onUserStarting(@NonNull TargetUser user) {
mService.onUserStarting(user.getUserIdentifier());
}
@Override
public void onUserStopping(@NonNull TargetUser user) {
mService.onUserStopping(user.getUserIdentifier());
}
}
//TODO(b/178111358) Add proper permission check and multi-user handling
@Override
public @GameMode int getGameMode(String packageName, int userId) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return GameManager.GAME_MODE_UNSUPPORTED;
}
Settings userSettings = mSettings.get(userId);
return userSettings.getGameModeLocked(packageName);
}
}
//TODO(b/178111358) Add proper permission check and multi-user handling
@Override
public void setGameMode(String packageName, @GameMode int gameMode, int userId) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return;
}
Settings userSettings = mSettings.get(userId);
userSettings.setGameModeLocked(packageName, gameMode);
final Message msg = mHandler.obtainMessage(WRITE_SETTINGS);
msg.obj = userId;
if (!mHandler.hasEqualMessages(WRITE_SETTINGS, userId)) {
mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
}
}
}
/**
* Notified when boot is completed.
*/
@VisibleForTesting
void onBootCompleted() {
Slog.d(TAG, "onBootCompleted");
}
void onUserStarting(int userId) {
synchronized (mLock) {
if (mSettings.containsKey(userId)) {
return;
}
Settings userSettings = new Settings(Environment.getDataSystemDeDirectory(userId));
mSettings.put(userId, userSettings);
userSettings.readPersistentDataLocked();
}
}
void onUserStopping(int userId) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return;
}
final Message msg = mHandler.obtainMessage(REMOVE_SETTINGS);
msg.obj = userId;
mHandler.sendMessage(msg);
}
}
private static ServiceThread createServiceThread() {
ServiceThread handlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
handlerThread.start();
return handlerThread;
}
}

View File

@@ -0,0 +1,193 @@
/*
* 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.graphics;
import android.graphics.GameManager;
import android.os.FileUtils;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
/**
* Persists all GameService related settings.
* @hide
*/
public class Settings {
// The XML file follows the below format:
// <?xml>
// <packages>
// <package></package>
// ...
// </packages>
private static final String GAME_SERVICE_FILE_NAME = "game-manager-service.xml";
private static final String TAG_PACKAGE = "package";
private static final String TAG_PACKAGES = "packages";
private static final String ATTR_NAME = "name";
private static final String ATTR_GAME_MODE = "gameMode";
private final File mSystemDir;
@VisibleForTesting
final AtomicFile mSettingsFile;
// PackageName -> GameMode
private final ArrayMap<String, Integer> mGameModes = new ArrayMap<>();
Settings(File dataDir) {
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU | FileUtils.S_IRWXG
| FileUtils.S_IROTH | FileUtils.S_IXOTH,
-1, -1);
mSettingsFile = new AtomicFile(new File(mSystemDir, GAME_SERVICE_FILE_NAME));
}
/**
* Return the game mode of a given package.
* This operation must be synced with an external lock.
*/
int getGameModeLocked(String packageName) {
if (mGameModes.containsKey(packageName)) {
return mGameModes.get(packageName);
}
return GameManager.GAME_MODE_UNSUPPORTED;
}
/**
* Set the game mode of a given package.
* This operation must be synced with an external lock.
*/
void setGameModeLocked(String packageName, int gameMode) {
mGameModes.put(packageName, gameMode);
}
/**
* Write all current game service settings into disk.
* This operation must be synced with an external lock.
*/
void writePersistentDataLocked() {
FileOutputStream fstr = null;
try {
fstr = mSettingsFile.startWrite();
final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, TAG_PACKAGES);
for (Map.Entry<String, Integer> entry : mGameModes.entrySet()) {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, entry.getKey());
serializer.attributeInt(null, ATTR_GAME_MODE, entry.getValue());
serializer.endTag(null, TAG_PACKAGE);
}
serializer.endTag(null, TAG_PACKAGES);
serializer.endDocument();
mSettingsFile.finishWrite(fstr);
FileUtils.setPermissions(mSettingsFile.toString(),
FileUtils.S_IRUSR | FileUtils.S_IWUSR
| FileUtils.S_IRGRP | FileUtils.S_IWGRP,
-1, -1);
return;
} catch (java.io.IOException e) {
mSettingsFile.failWrite(fstr);
Slog.wtf(GameManagerService.TAG, "Unable to write game manager service settings, "
+ "current changes will be lost at reboot", e);
}
}
/**
* Read game service settings from the disk.
* This operation must be synced with an external lock.
*/
boolean readPersistentDataLocked() {
mGameModes.clear();
try {
final FileInputStream str = mSettingsFile.openRead();
final TypedXmlPullParser parser = Xml.resolvePullParser(str);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
}
if (type != XmlPullParser.START_TAG) {
Slog.wtf(GameManagerService.TAG,
"No start tag found in package manager settings");
return false;
}
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals(TAG_PACKAGE)) {
readPackage(parser);
} else {
Slog.w(GameManagerService.TAG, "Unknown element: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
} catch (XmlPullParserException | java.io.IOException e) {
Slog.wtf(GameManagerService.TAG, "Error reading package manager settings", e);
return false;
}
return true;
}
private void readPackage(TypedXmlPullParser parser) throws XmlPullParserException,
IOException {
String name = null;
int gameMode = GameManager.GAME_MODE_UNSUPPORTED;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
} catch (XmlPullParserException e) {
Slog.wtf(GameManagerService.TAG, "Error reading game mode", e);
}
if (name != null) {
mGameModes.put(name, gameMode);
} else {
XmlUtils.skipCurrentTag(parser);
}
}
}

View File

@@ -360,6 +360,8 @@ public final class SystemServer implements Dumpable {
private static final String IP_CONNECTIVITY_METRICS_CLASS =
"com.android.server.connectivity.IpConnectivityMetrics";
private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
private static final String GAME_MANAGER_SERVICE_CLASS =
"com.android.server.graphics.GameManagerService$Lifecycle";
private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
@@ -2514,6 +2516,10 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
t.traceBegin("GameManagerService");
mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS);
t.traceEnd();
t.traceBegin("StartBootPhaseDeviceSpecificServicesReady");
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
t.traceEnd();

View File

@@ -0,0 +1,129 @@
/*
* 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.graphics;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import android.content.Context;
import android.util.AtomicFile;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class GameManagerServiceSettingsTests {
private static final String TAG = "GameServiceSettingsTests";
private static final String PACKAGE_NAME_1 = "com.android.app1";
private static final String PACKAGE_NAME_2 = "com.android.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
private void writeFile(File file, byte[] data) {
file.mkdirs();
try {
AtomicFile aFile = new AtomicFile(file);
FileOutputStream fos = aFile.startWrite();
fos.write(data);
aFile.finishWrite(fos);
} catch (IOException ioe) {
Log.e(TAG, "Cannot write file " + file.getPath());
}
}
private void writeGameServiceXml() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
"system/game-manager-service.xml"),
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<packages>\n"
+ " <package name=\"com.android.app1\" gameMode=\"1\">\n"
+ " </package>\n"
+ " <package name=\"com.android.app2\" gameMode=\"2\">\n"
+ " </package>\n"
+ " <package name=\"com.android.app3\" gameMode=\"3\">\n"
+ " </package>\n"
+ "</packages>\n").getBytes());
}
private void deleteSystemFolder() {
File systemFolder = new File(InstrumentationRegistry.getContext().getFilesDir(), "system");
deleteFolder(systemFolder);
}
private static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File file : files) {
deleteFolder(file);
}
}
folder.delete();
}
private void writeOldFiles() {
deleteSystemFolder();
writeGameServiceXml();
}
private void verifyGameServiceSettingsData(Settings settings) {
assertThat(settings.getGameModeLocked(PACKAGE_NAME_1), is(1));
assertThat(settings.getGameModeLocked(PACKAGE_NAME_2), is(2));
assertThat(settings.getGameModeLocked(PACKAGE_NAME_3), is(3));
}
@After
public void tearDown() throws Exception {
deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
}
/** read in data and verify */
@Test
public void testReadGameServiceSettings() {
/* write out files and read */
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
Settings settings = new Settings(context.getFilesDir());
assertThat(settings.readPersistentDataLocked(), is(true));
verifyGameServiceSettingsData(settings);
}
/** read in data, write it out, and read it back in. Verify same. */
@Test
public void testWriteGameServiceSettings() {
// write out files and read
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
Settings settings = new Settings(context.getFilesDir());
assertThat(settings.readPersistentDataLocked(), is(true));
// write out, read back in and verify the same
settings.writePersistentDataLocked();
assertThat(settings.readPersistentDataLocked(), is(true));
verifyGameServiceSettingsData(settings);
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.graphics;
import static org.junit.Assert.assertEquals;
import android.graphics.GameManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class GameManagerServiceTests {
private static final String TAG = "GameServiceTests";
private static final String PACKAGE_NAME_0 = "com.android.app0";
private static final String PACKAGE_NAME_1 = "com.android.app1";
private static final int USER_ID_1 = 1001;
private static final int USER_ID_2 = 1002;
/**
* By default game mode is not supported.
*/
@Test
public void testGameModeDefaultValue() {
GameManagerService gameManagerService =
new GameManagerService(InstrumentationRegistry.getContext());
gameManagerService.onUserStarting(USER_ID_1);
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_0, USER_ID_1));
}
/**
* Test the default behaviour for a nonexistent user.
*/
@Test
public void testDefaultValueForNonexistentUser() {
GameManagerService gameManagerService =
new GameManagerService(InstrumentationRegistry.getContext());
gameManagerService.onUserStarting(USER_ID_1);
gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_2);
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_2));
}
/**
* Test getter and setter of game modes.
*/
@Test
public void testGameMode() {
GameManagerService gameManagerService =
new GameManagerService(InstrumentationRegistry.getContext());
gameManagerService.onUserStarting(USER_ID_1);
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE,
USER_ID_1);
assertEquals(GameManager.GAME_MODE_PERFORMANCE,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
}
}