1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app; 18 19 import android.Manifest; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.annotation.SystemService; 26 import android.annotation.TestApi; 27 import android.annotation.UserHandleAware; 28 import android.content.Context; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageManager; 31 import android.os.Build; 32 import android.os.Handler; 33 import android.os.RemoteException; 34 import android.os.ServiceManager; 35 import android.os.ServiceManager.ServiceNotFoundException; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 40 /** 41 * The GameManager allows system apps to modify and query the game mode of apps. 42 */ 43 @SystemService(Context.GAME_SERVICE) 44 public final class GameManager { 45 46 private static final String TAG = "GameManager"; 47 48 private final @Nullable Context mContext; 49 private final IGameManagerService mService; 50 51 /** @hide */ 52 @IntDef(flag = false, prefix = {"GAME_MODE_"}, value = { 53 GAME_MODE_UNSUPPORTED, // 0 54 GAME_MODE_STANDARD, // 1 55 GAME_MODE_PERFORMANCE, // 2 56 GAME_MODE_BATTERY, // 3 57 GAME_MODE_CUSTOM, // 4 58 }) 59 @Retention(RetentionPolicy.SOURCE) 60 public @interface GameMode { 61 } 62 63 /** 64 * Game mode is not supported for this application. 65 */ 66 public static final int GAME_MODE_UNSUPPORTED = 0; 67 68 /** 69 * Standard game mode means the platform will use the game's default 70 * performance characteristics. 71 */ 72 public static final int GAME_MODE_STANDARD = 1; 73 74 /** 75 * Performance game mode maximizes the game's performance. 76 * <p> 77 * This game mode is highly likely to increase battery consumption. 78 */ 79 public static final int GAME_MODE_PERFORMANCE = 2; 80 81 /** 82 * Battery game mode will save battery and give longer game play time. 83 */ 84 public static final int GAME_MODE_BATTERY = 3; 85 86 /** 87 * Custom game mode that has user-provided configuration overrides. 88 * <p> 89 * Custom game mode is expected to be handled only by the platform using users' 90 * preferred config. It is currently not allowed to opt in custom mode in game mode XML file nor 91 * expected to perform app-based optimizations when activated. 92 */ 93 public static final int GAME_MODE_CUSTOM = 4; 94 GameManager(Context context, Handler handler)95 GameManager(Context context, Handler handler) throws ServiceNotFoundException { 96 mContext = context; 97 mService = IGameManagerService.Stub.asInterface( 98 ServiceManager.getServiceOrThrow(Context.GAME_SERVICE)); 99 } 100 101 /** 102 * Return the user selected game mode for this application. 103 * <p> 104 * An application can use <code>android:isGame="true"</code> or 105 * <code>android:appCategory="game"</code> to indicate that the application is a game. If an 106 * application is not a game, always return {@link #GAME_MODE_UNSUPPORTED}. 107 * <p> 108 * Developers should call this API every time the application is resumed. 109 * <p> 110 * If a game's <code>targetSdkVersion</code> is {@link android.os.Build.VERSION_CODES#TIRAMISU} 111 * or lower, and when the game mode is set to {@link #GAME_MODE_CUSTOM} which is available in 112 * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or newer, this API will return 113 * {@link #GAME_MODE_STANDARD} instead for backward compatibility. 114 */ getGameMode()115 public @GameMode int getGameMode() { 116 return getGameModeImpl(mContext.getPackageName(), 117 mContext.getApplicationInfo().targetSdkVersion); 118 } 119 120 /** 121 * Gets the game mode for the given package. 122 * <p> 123 * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. 124 * <p> 125 * Also see {@link #getGameMode()} on how it handles SDK version compatibility. 126 * 127 * @hide 128 */ 129 @TestApi 130 @UserHandleAware 131 @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) getGameMode(@onNull String packageName)132 public @GameMode int getGameMode(@NonNull String packageName) { 133 final int targetSdkVersion; 134 try { 135 final ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo( 136 packageName, PackageManager.ApplicationInfoFlags.of(0)); 137 targetSdkVersion = applicationInfo.targetSdkVersion; 138 } catch (PackageManager.NameNotFoundException ex) { 139 return GAME_MODE_UNSUPPORTED; 140 } 141 return getGameModeImpl(packageName, targetSdkVersion); 142 } 143 144 // This target SDK version check can be performed against any game by a privileged app, and 145 // we don't want a binder call each time to check on behalf of an app using CompatChange. 146 @SuppressWarnings("AndroidFrameworkCompatChange") getGameModeImpl(@onNull String packageName, int targetSdkVersion)147 private @GameMode int getGameModeImpl(@NonNull String packageName, int targetSdkVersion) { 148 final int gameMode; 149 try { 150 gameMode = mService.getGameMode(packageName, 151 mContext.getUserId()); 152 } catch (RemoteException e) { 153 throw e.rethrowFromSystemServer(); 154 } 155 if (gameMode == GAME_MODE_CUSTOM && targetSdkVersion <= Build.VERSION_CODES.TIRAMISU) { 156 return GAME_MODE_STANDARD; 157 } 158 return gameMode; 159 } 160 161 /** 162 * Returns the {@link GameModeInfo} associated with the game associated with 163 * the given {@code packageName}. If the given package is not a game, {@code null} is 164 * always returned. 165 * <p> 166 * An application can use <code>android:isGame="true"</code> or 167 * <code>android:appCategory="game"</code> to indicate that the application is a game. 168 * If the manifest doesn't define a category, the category can also be 169 * provided by the installer via 170 * {@link android.content.pm.PackageManager#setApplicationCategoryHint(String, int)}. 171 * <p> 172 * 173 * @hide 174 */ 175 @SystemApi 176 @UserHandleAware 177 @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) getGameModeInfo(@onNull String packageName)178 public @Nullable GameModeInfo getGameModeInfo(@NonNull String packageName) { 179 try { 180 return mService.getGameModeInfo(packageName, mContext.getUserId()); 181 } catch (RemoteException e) { 182 throw e.rethrowFromSystemServer(); 183 } 184 } 185 186 /** 187 * Sets the game mode for the given package. 188 * <p> 189 * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. 190 * <p> 191 * Setting the game mode on a non-game application or setting a game to 192 * {@link #GAME_MODE_UNSUPPORTED} will have no effect. 193 * @hide 194 */ 195 @SystemApi 196 @UserHandleAware 197 @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) setGameMode(@onNull String packageName, @GameMode int gameMode)198 public void setGameMode(@NonNull String packageName, @GameMode int gameMode) { 199 try { 200 mService.setGameMode(packageName, gameMode, mContext.getUserId()); 201 } catch (RemoteException e) { 202 throw e.rethrowFromSystemServer(); 203 } 204 } 205 206 /** 207 * Returns a list of supported game modes for a given package. 208 * <p> 209 * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. 210 * 211 * @hide 212 */ 213 @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) getAvailableGameModes(@onNull String packageName)214 public @GameMode int[] getAvailableGameModes(@NonNull String packageName) { 215 try { 216 return mService.getAvailableGameModes(packageName, mContext.getUserId()); 217 } catch (RemoteException e) { 218 throw e.rethrowFromSystemServer(); 219 } 220 } 221 222 /** 223 * Returns if ANGLE is enabled for a given package and user ID. 224 * <p> 225 * ANGLE (Almost Native Graphics Layer Engine) can translate OpenGL ES commands to Vulkan 226 * commands. Enabling ANGLE may improve the performance and/or reduce the power consumption of 227 * applications. 228 * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. 229 * 230 * @hide 231 */ 232 @TestApi 233 @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) isAngleEnabled(@onNull String packageName)234 public boolean isAngleEnabled(@NonNull String packageName) { 235 try { 236 return mService.isAngleEnabled(packageName, mContext.getUserId()); 237 } catch (RemoteException e) { 238 throw e.rethrowFromSystemServer(); 239 } 240 } 241 242 /** 243 * Set up the automatic power boost if appropriate. 244 * 245 * @hide 246 */ 247 @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) notifyGraphicsEnvironmentSetup()248 public void notifyGraphicsEnvironmentSetup() { 249 try { 250 mService.notifyGraphicsEnvironmentSetup( 251 mContext.getPackageName(), mContext.getUserId()); 252 } catch (RemoteException e) { 253 throw e.rethrowFromSystemServer(); 254 } 255 } 256 257 /** 258 * Called by games to communicate the current state to the platform. 259 * @param gameState An object set to the current state. 260 */ setGameState(@onNull GameState gameState)261 public void setGameState(@NonNull GameState gameState) { 262 try { 263 mService.setGameState(mContext.getPackageName(), gameState, mContext.getUserId()); 264 } catch (RemoteException e) { 265 throw e.rethrowFromSystemServer(); 266 } 267 } 268 269 270 /** 271 * Sets the game service provider to the given package name for test only. 272 * 273 * <p>Passing in {@code null} will clear a previously set value. 274 * @hide 275 */ 276 @TestApi setGameServiceProvider(@ullable String packageName)277 public void setGameServiceProvider(@Nullable String packageName) { 278 try { 279 mService.setGameServiceProvider(packageName); 280 } catch (RemoteException e) { 281 throw e.rethrowFromSystemServer(); 282 } 283 } 284 285 /** 286 * Updates the config for the game's {@link #GAME_MODE_CUSTOM} mode. 287 * <p> 288 * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. 289 * 290 * @param packageName The package name of the game to update 291 * @param gameModeConfig The configuration to use for game mode interventions 292 * @hide 293 */ 294 @SystemApi 295 @UserHandleAware 296 @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) updateCustomGameModeConfiguration(@onNull String packageName, @NonNull GameModeConfiguration gameModeConfig)297 public void updateCustomGameModeConfiguration(@NonNull String packageName, 298 @NonNull GameModeConfiguration gameModeConfig) { 299 try { 300 mService.updateCustomGameModeConfiguration(packageName, gameModeConfig, 301 mContext.getUserId()); 302 } catch (RemoteException e) { 303 throw e.rethrowFromSystemServer(); 304 } 305 } 306 } 307