• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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