Game Loading Time improvement by boosting CPU via ADPF

Adds the concept of Game State (supplied by the application) to the
GameManager API. This allows the setting of various modes to describe
the current high-level runtime state of the game, as well as the loading
state. In Performance Mode, during loading a hint is sent to the device
PowerHAL via the PowerManager.

Bug: 201769701
Test: atest android.gamemanager.cts.GameManagerTest
Ignore-AOSP-First: Mentions a confidential API
Change-Id: I695e45b12f9a30c1824df210bfefbd5a9b4bd935
This commit is contained in:
Jim Blackler
2021-11-17 16:22:25 +00:00
parent 9205050ae2
commit 620c5cccd1
9 changed files with 295 additions and 13 deletions

View File

@@ -5438,12 +5438,30 @@ package android.app {
public final class GameManager {
method public int getGameMode();
method public void setGameState(@NonNull android.app.GameState);
field public static final int GAME_MODE_BATTERY = 3; // 0x3
field public static final int GAME_MODE_PERFORMANCE = 2; // 0x2
field public static final int GAME_MODE_STANDARD = 1; // 0x1
field public static final int GAME_MODE_UNSUPPORTED = 0; // 0x0
}
public final class GameState implements android.os.Parcelable {
ctor public GameState(boolean, int);
ctor public GameState(boolean, int, @Nullable String, @NonNull android.os.Bundle);
method public int describeContents();
method @Nullable public String getDescription();
method @NonNull public android.os.Bundle getMetadata();
method public int getMode();
method public boolean isLoading();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.GameState> CREATOR;
field public static final int MODE_CONTENT = 4; // 0x4
field public static final int MODE_GAMEPLAY_INTERRUPTIBLE = 2; // 0x2
field public static final int MODE_GAMEPLAY_UNINTERRUPTIBLE = 3; // 0x3
field public static final int MODE_NONE = 1; // 0x1
field public static final int MODE_UNKNOWN = 0; // 0x0
}
public class Instrumentation {
ctor public Instrumentation();
method public android.os.TestLooperManager acquireLooperManager(android.os.Looper);

View File

@@ -167,4 +167,16 @@ public final class GameManager {
throw e.rethrowFromSystemServer();
}
}
/**
* Called by games to communicate the current state to the platform.
* @param gameState An object set to the current state.
*/
public void setGameState(@NonNull GameState gameState) {
try {
mService.setGameState(mContext.getPackageName(), gameState, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -0,0 +1,19 @@
/*
* 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.app;
parcelable GameState;

View File

@@ -0,0 +1,179 @@
/*
* 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.app;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* State of the game passed to the GameManager.
*
* This includes a top-level state for the game (indicating if the game can be interrupted without
* interfering with content that can't be paused). Since content can be loaded in any state, it
* includes an independent boolean flag to indicate loading status.
*
* Free-form metadata (as a Bundle) and a string description can also be specified by the
* application.
*/
public final class GameState implements Parcelable {
/**
* Default Game mode is unknown.
*/
public static final int MODE_UNKNOWN = 0;
/**
* No mode means that the game is not in active play, for example the user is using the game
* menu.
*/
public static final int MODE_NONE = 1;
/**
* Indicates if the game is in active, but interruptible, game play.
*/
public static final int MODE_GAMEPLAY_INTERRUPTIBLE = 2;
/**
* Indicates if the game is in active user play mode, which is real time and cannot be
* interrupted.
*/
public static final int MODE_GAMEPLAY_UNINTERRUPTIBLE = 3;
/**
* Indicates that the current content shown is not gameplay related. For example it can be an
* ad, a web page, a text, or a video.
*/
public static final int MODE_CONTENT = 4;
/**
* Implement the parcelable interface.
*/
public static final @NonNull Creator<GameState> CREATOR = new Creator<GameState>() {
@Override
public GameState createFromParcel(Parcel in) {
return new GameState(in);
}
@Override
public GameState[] newArray(int size) {
return new GameState[size];
}
};
// Indicates if the game is loading assets/resources/compiling/etc. This is independent of game
// mode because there could be a loading UI displayed, or there could be loading in the
// background.
private final boolean mIsLoading;
// One of the states listed above.
private final @GameStateMode int mMode;
// This is a game specific description. For example can be level or scene name.
private final @Nullable String mDescription;
// This contains any other game specific parameters not covered by the fields above. It can be
// quality parameter data, settings, or game modes.
private final @NonNull Bundle mMetaData;
/**
* Create a GameState with the specified loading status.
* @param isLoading Whether the game is in the loading state.
* @param mode The game state mode of type @GameStateMode.
*/
public GameState(boolean isLoading, @GameStateMode int mode) {
this(isLoading, mode, null, new Bundle());
}
/**
* Create a GameState with the given state variables.
* @param isLoading Whether the game is in the loading state.
* @param mode The game state mode of type @GameStateMode.
* @param description An optional description of the state.
* @param metaData Optional metadata.
*/
public GameState(boolean isLoading, @GameStateMode int mode, @Nullable String description,
@NonNull Bundle metaData) {
mIsLoading = isLoading;
mMode = mode;
mDescription = description;
mMetaData = metaData;
}
private GameState(Parcel in) {
mIsLoading = in.readBoolean();
mMode = in.readInt();
mDescription = in.readString();
mMetaData = in.readBundle();
}
/**
* @return If the game is loading assets/resources/compiling/etc.
*/
public boolean isLoading() {
return mIsLoading;
}
/**
* @return The game state mode.
*/
public @GameStateMode int getMode() {
return mMode;
}
/**
* @return The state description, or null if one is not set.
*/
public @Nullable String getDescription() {
return mDescription;
}
/**
* @return metadata associated with the state.
*/
public @NonNull Bundle getMetadata() {
return mMetaData;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mIsLoading);
parcel.writeInt(mMode);
parcel.writeString(mDescription);
parcel.writeBundle(mMetaData);
}
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({MODE_UNKNOWN, MODE_NONE, MODE_GAMEPLAY_INTERRUPTIBLE, MODE_GAMEPLAY_UNINTERRUPTIBLE,
MODE_CONTENT
})
@interface GameStateMode {}
}

View File

@@ -16,6 +16,8 @@
package android.app;
import android.app.GameState;
/**
* @hide
*/
@@ -24,4 +26,5 @@ interface IGameManagerService {
void setGameMode(String packageName, int gameMode, int userId);
int[] getAvailableGameModes(String packageName);
boolean getAngleEnabled(String packageName, int userId);
void setGameState(String packageName, in GameState gameState, int userId);
}

View File

@@ -31,6 +31,7 @@ per-file SystemServiceRegistry.java = file:/services/core/java/com/android/serve
per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS
per-file UiAutomation.java = file:/services/accessibility/OWNERS
per-file GameManager* = file:/GAME_MANAGER_OWNERS
per-file GameState* = file:/GAME_MANAGER_OWNERS
per-file IGameManager* = file:/GAME_MANAGER_OWNERS
# ActivityThread

View File

@@ -128,8 +128,6 @@ java_library_static {
"android.hardware.common-V2-java",
"android.hardware.light-V2.0-java",
"android.hardware.gnss-V2-java",
"android.hardware.power-V1-java",
"android.hardware.power-V1.0-java",
"android.hardware.vibrator-V2-java",
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
@@ -167,6 +165,7 @@ java_library_static {
"android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
"android.hardware.power.stats-V1-java",
"android.hardware.power-V3-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
"icu4j_calendar_astronomer",

View File

@@ -42,6 +42,7 @@ import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
import android.app.GameState;
import android.app.IGameManagerService;
import android.app.compat.PackageOverride;
import android.content.BroadcastReceiver;
@@ -51,12 +52,15 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.hardware.power.Mode;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -72,6 +76,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
@@ -95,11 +100,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
static final int WRITE_SETTINGS = 1;
static final int REMOVE_SETTINGS = 2;
static final int POPULATE_GAME_MODE_SETTINGS = 3;
static final int SET_GAME_STATE = 4;
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
static final PackageOverride COMPAT_ENABLED = new PackageOverride.Builder().setEnabled(true)
.build();
static final PackageOverride COMPAT_DISABLED = new PackageOverride.Builder().setEnabled(false)
.build();
private static final String PACKAGE_NAME_MSG_KEY = "packageName";
private static final String USER_ID_MSG_KEY = "userId";
private final Context mContext;
private final Object mLock = new Object();
@@ -107,6 +115,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final Handler mHandler;
private final PackageManager mPackageManager;
private final IPlatformCompat mPlatformCompat;
private final PowerManagerInternal mPowerManagerInternal;
private DeviceConfigListener mDeviceConfigListener;
@GuardedBy("mLock")
private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
@@ -123,6 +132,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
mPackageManager = mContext.getPackageManager();
mPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
}
@Override
@@ -200,6 +210,19 @@ public final class GameManagerService extends IGameManagerService.Stub {
updateConfigsForUser(userId, packageNames);
break;
}
case SET_GAME_STATE: {
if (mPowerManagerInternal == null) {
final Bundle data = msg.getData();
Slog.d(TAG, "Error setting loading mode for package "
+ data.getString(PACKAGE_NAME_MSG_KEY)
+ " and userId " + data.getInt(USER_ID_MSG_KEY));
break;
}
final GameState gameState = (GameState) msg.obj;
final boolean isLoading = gameState.isLoading();
mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
break;
}
}
}
}
@@ -257,6 +280,32 @@ public final class GameManagerService extends IGameManagerService.Stub {
return 0;
}
/**
* 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.
*/
public void setGameState(String packageName, @NonNull GameState gameState, int userId) {
if (!isPackageGame(packageName, userId)) {
// Restrict to games only.
return;
}
if (getGameMode(packageName, userId) != GameManager.GAME_MODE_PERFORMANCE) {
// Requires performance mode to be enabled.
return;
}
final Message msg = mHandler.obtainMessage(SET_GAME_STATE);
final Bundle data = new Bundle();
data.putString(PACKAGE_NAME_MSG_KEY, packageName);
data.putInt(USER_ID_MSG_KEY, userId);
msg.setData(data);
msg.obj = gameState;
mHandler.sendMessage(msg);
}
/**
* GamePackageConfiguration manages all game mode config details for its associated package.
*/
@@ -620,6 +669,16 @@ public final class GameManagerService extends IGameManagerService.Stub {
return getGameModeFromSettings(packageName, userId);
}
private boolean isPackageGame(String packageName, int userId) {
try {
final ApplicationInfo applicationInfo = mPackageManager
.getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
return applicationInfo.category == ApplicationInfo.CATEGORY_GAME;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
/**
* Sets the Game Mode for the package name.
* Verifies that the calling process has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
@@ -630,16 +689,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
throws SecurityException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
// Restrict to games only.
try {
final ApplicationInfo applicationInfo = mPackageManager
.getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
// Ignore attempt to set the game mode for applications that are not identified
// as game. See {@link PackageManager#setApplicationCategoryHint(String, int)}
return;
}
} catch (PackageManager.NameNotFoundException e) {
if (!isPackageGame(packageName, userId)) {
// Restrict to games only.
return;
}

View File

@@ -161,7 +161,7 @@ cc_defaults {
"android.hardware.memtrack-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"android.hardware.power-V2-cpp",
"android.hardware.power-V3-cpp",
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-V1-ndk",
"android.hardware.thermal@1.0",