blob: 2982545a5e6f3371ba8a42cec42807c492ba89ac [file] [log] [blame]
/*
* 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.app.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 GameManagerSettings {
// 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<>();
GameManagerSettings(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();
if (!mSettingsFile.exists()) {
Slog.v(GameManagerService.TAG, "Settings file doesn't exists, skip reading");
return false;
}
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) {
// Do nothing
}
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);
}
}
}