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