1 /* 2 * Copyright (C) 2010 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.annotation.IntDef; 20 import android.annotation.IntRange; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.annotation.SystemService; 25 import android.annotation.TestApi; 26 import android.compat.annotation.UnsupportedAppUsage; 27 import android.content.Context; 28 import android.content.res.Configuration; 29 import android.os.RemoteException; 30 import android.os.ServiceManager; 31 import android.os.ServiceManager.ServiceNotFoundException; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 36 /** 37 * This class provides access to the system uimode services. These services 38 * allow applications to control UI modes of the device. 39 * It provides functionality to disable the car mode and it gives access to the 40 * night mode settings. 41 * 42 * <p>These facilities are built on top of the underlying 43 * {@link android.content.Intent#ACTION_DOCK_EVENT} broadcasts that are sent when the user 44 * physical places the device into and out of a dock. When that happens, 45 * the UiModeManager switches the system {@link android.content.res.Configuration} 46 * to the appropriate UI mode, sends broadcasts about the mode switch, and 47 * starts the corresponding mode activity if appropriate. See the 48 * broadcasts {@link #ACTION_ENTER_CAR_MODE} and 49 * {@link #ACTION_ENTER_DESK_MODE} for more information. 50 * 51 * <p>In addition, the user may manually switch the system to car mode without 52 * physically being in a dock. While in car mode -- whether by manual action 53 * from the user or being physically placed in a dock -- a notification is 54 * displayed allowing the user to exit dock mode. Thus the dock mode 55 * represented here may be different than the current state of the underlying 56 * dock event broadcast. 57 */ 58 @SystemService(Context.UI_MODE_SERVICE) 59 public class UiModeManager { 60 private static final String TAG = "UiModeManager"; 61 62 /** 63 * Broadcast sent when the device's UI has switched to car mode, either 64 * by being placed in a car dock or explicit action of the user. After 65 * sending the broadcast, the system will start the intent 66 * {@link android.content.Intent#ACTION_MAIN} with category 67 * {@link android.content.Intent#CATEGORY_CAR_DOCK} 68 * to display the car UI, which typically what an application would 69 * implement to provide their own interface. However, applications can 70 * also monitor this Intent in order to be informed of mode changes or 71 * prevent the normal car UI from being displayed by setting the result 72 * of the broadcast to {@link Activity#RESULT_CANCELED}. 73 * <p> 74 * This intent is broadcast when {@link #getCurrentModeType()} transitions to 75 * {@link Configuration#UI_MODE_TYPE_CAR} from some other ui mode. 76 */ 77 public static String ACTION_ENTER_CAR_MODE = "android.app.action.ENTER_CAR_MODE"; 78 79 /** 80 * Broadcast sent when an app has entered car mode using either {@link #enableCarMode(int)} or 81 * {@link #enableCarMode(int, int)}. 82 * <p> 83 * Unlike {@link #ACTION_ENTER_CAR_MODE}, which is only sent when the global car mode state 84 * (i.e. {@link #getCurrentModeType()}) transitions to {@link Configuration#UI_MODE_TYPE_CAR}, 85 * this intent is sent any time an app declares it has entered car mode. Thus, this intent is 86 * intended for use by a component which needs to know not only when the global car mode state 87 * changed, but also when the highest priority app declaring car mode has changed. 88 * <p> 89 * This broadcast includes the package name of the app which requested to enter car mode in 90 * {@link #EXTRA_CALLING_PACKAGE}. The priority the app entered car mode at is specified in 91 * {@link #EXTRA_PRIORITY}. 92 * <p> 93 * This is primarily intended to be received by other components of the Android OS. 94 * <p> 95 * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES} 96 * @hide 97 */ 98 @SystemApi 99 public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED = 100 "android.app.action.ENTER_CAR_MODE_PRIORITIZED"; 101 102 /** 103 * Broadcast sent when the device's UI has switch away from car mode back 104 * to normal mode. Typically used by a car mode app, to dismiss itself 105 * when the user exits car mode. 106 * <p> 107 * This intent is broadcast when {@link #getCurrentModeType()} transitions from 108 * {@link Configuration#UI_MODE_TYPE_CAR} to some other ui mode. 109 */ 110 public static String ACTION_EXIT_CAR_MODE = "android.app.action.EXIT_CAR_MODE"; 111 112 /** 113 * Broadcast sent when an app has exited car mode using {@link #disableCarMode(int)}. 114 * <p> 115 * Unlike {@link #ACTION_EXIT_CAR_MODE}, which is only sent when the global car mode state 116 * (i.e. {@link #getCurrentModeType()}) transitions to a non-car mode state such as 117 * {@link Configuration#UI_MODE_TYPE_NORMAL}, this intent is sent any time an app declares it 118 * has exited car mode. Thus, this intent is intended for use by a component which needs to 119 * know not only when the global car mode state changed, but also when the highest priority app 120 * declaring car mode has changed. 121 * <p> 122 * This broadcast includes the package name of the app which requested to exit car mode in 123 * {@link #EXTRA_CALLING_PACKAGE}. The priority the app originally entered car mode at is 124 * specified in {@link #EXTRA_PRIORITY}. 125 * <p> 126 * If {@link #DISABLE_CAR_MODE_ALL_PRIORITIES} is used when disabling car mode (i.e. this is 127 * initiated by the user via the persistent car mode notification), this broadcast is sent once 128 * for each priority level for which car mode is being disabled. 129 * <p> 130 * This is primarily intended to be received by other components of the Android OS. 131 * <p> 132 * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES} 133 * @hide 134 */ 135 @SystemApi 136 public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED = 137 "android.app.action.EXIT_CAR_MODE_PRIORITIZED"; 138 139 /** 140 * Broadcast sent when the device's UI has switched to desk mode, 141 * by being placed in a desk dock. After 142 * sending the broadcast, the system will start the intent 143 * {@link android.content.Intent#ACTION_MAIN} with category 144 * {@link android.content.Intent#CATEGORY_DESK_DOCK} 145 * to display the desk UI, which typically what an application would 146 * implement to provide their own interface. However, applications can 147 * also monitor this Intent in order to be informed of mode changes or 148 * prevent the normal desk UI from being displayed by setting the result 149 * of the broadcast to {@link Activity#RESULT_CANCELED}. 150 */ 151 public static String ACTION_ENTER_DESK_MODE = "android.app.action.ENTER_DESK_MODE"; 152 153 /** 154 * Broadcast sent when the device's UI has switched away from desk mode back 155 * to normal mode. Typically used by a desk mode app, to dismiss itself 156 * when the user exits desk mode. 157 */ 158 public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE"; 159 160 /** 161 * String extra used with {@link #ACTION_ENTER_CAR_MODE_PRIORITIZED} and 162 * {@link #ACTION_EXIT_CAR_MODE_PRIORITIZED} to indicate the package name of the app which 163 * requested to enter or exit car mode. 164 * @hide 165 */ 166 @SystemApi 167 public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE"; 168 169 /** 170 * Integer extra used with {@link #ACTION_ENTER_CAR_MODE_PRIORITIZED} and 171 * {@link #ACTION_EXIT_CAR_MODE_PRIORITIZED} to indicate the priority level at which car mode 172 * is being disabled. 173 * @hide 174 */ 175 @SystemApi 176 public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY"; 177 178 /** @hide */ 179 @IntDef(prefix = { "MODE_" }, value = { 180 MODE_NIGHT_AUTO, 181 MODE_NIGHT_NO, 182 MODE_NIGHT_YES 183 }) 184 @Retention(RetentionPolicy.SOURCE) 185 public @interface NightMode {} 186 187 /** 188 * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}: 189 * automatically switch night mode on and off based on the time. 190 */ 191 public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4; 192 193 /** 194 * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}: 195 * never run in night mode. 196 */ 197 public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4; 198 199 /** 200 * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}: 201 * always run in night mode. 202 */ 203 public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4; 204 205 private IUiModeManager mService; 206 207 /** 208 * Context required for getting the opPackageName of API caller; maybe be {@code null} if the 209 * old constructor marked with UnSupportedAppUsage is used. 210 */ 211 private @Nullable Context mContext; 212 213 @UnsupportedAppUsage UiModeManager()214 /*package*/ UiModeManager() throws ServiceNotFoundException { 215 this(null /* context */); 216 } 217 UiModeManager(Context context)218 /*package*/ UiModeManager(Context context) throws ServiceNotFoundException { 219 mService = IUiModeManager.Stub.asInterface( 220 ServiceManager.getServiceOrThrow(Context.UI_MODE_SERVICE)); 221 mContext = context; 222 } 223 224 /** 225 * Flag for use with {@link #enableCarMode(int)}: go to the car 226 * home activity as part of the enable. Enabling this way ensures 227 * a clean transition between the current activity (in non-car-mode) and 228 * the car home activity that will serve as home while in car mode. This 229 * will switch to the car home activity even if we are already in car mode. 230 */ 231 public static final int ENABLE_CAR_MODE_GO_CAR_HOME = 0x0001; 232 233 /** 234 * Flag for use with {@link #enableCarMode(int)}: allow sleep mode while in car mode. 235 * By default, when this flag is not set, the system may hold a full wake lock to keep the 236 * screen turned on and prevent the system from entering sleep mode while in car mode. 237 * Setting this flag disables such behavior and the system may enter sleep mode 238 * if there is no other user activity and no other wake lock held. 239 * Setting this flag can be relevant for a car dock application that does not require the 240 * screen kept on. 241 */ 242 public static final int ENABLE_CAR_MODE_ALLOW_SLEEP = 0x0002; 243 244 /** @hide */ 245 @IntDef(prefix = { "ENABLE_CAR_MODE_" }, value = { 246 ENABLE_CAR_MODE_GO_CAR_HOME, 247 ENABLE_CAR_MODE_ALLOW_SLEEP 248 }) 249 @Retention(RetentionPolicy.SOURCE) 250 public @interface EnableCarMode {} 251 252 /** 253 * Force device into car mode, like it had been placed in the car dock. 254 * This will cause the device to switch to the car home UI as part of 255 * the mode switch. 256 * @param flags Must be 0. 257 */ enableCarMode(int flags)258 public void enableCarMode(int flags) { 259 enableCarMode(DEFAULT_PRIORITY, flags); 260 } 261 262 /** 263 * Force device into car mode, like it had been placed in the car dock. This will cause the 264 * device to switch to the car home UI as part of the mode switch. 265 * <p> 266 * An app may request to enter car mode when the system is already in car mode. The app may 267 * specify a "priority" when entering car mode. The device will remain in car mode 268 * (i.e. {@link #getCurrentModeType()} is {@link Configuration#UI_MODE_TYPE_CAR}) as long as 269 * there is a priority level at which car mode have been enabled. 270 * <p> 271 * Specifying a priority level when entering car mode is important in cases where multiple apps 272 * on a device implement a car-mode {@link android.telecom.InCallService} (see 273 * {@link android.telecom.TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI}). The 274 * {@link android.telecom.InCallService} associated with the highest priority app which entered 275 * car mode will be bound to by Telecom and provided with information about ongoing calls on 276 * the device. 277 * <p> 278 * System apps holding the required permission can enable car mode when the app determines the 279 * correct conditions exist for that app to be in car mode. The device maker should ensure that 280 * where multiple apps exist on the device which can potentially enter car mode, appropriate 281 * priorities are used to ensure that calls delivered by the 282 * {@link android.telecom.InCallService} API are sent to the highest priority app given the 283 * desired behavior of the car mode experience on the device. 284 * <p> 285 * If app A and app B both meet their own criteria to enable car mode, and it is desired that 286 * app B should be the one which should receive call information in that scenario, the priority 287 * for app B should be higher than the one for app A. The higher priority of app B compared to 288 * A means it will be bound to during calls and app A will not. When app B no longer meets its 289 * criteria for providing a car mode experience it uses {@link #disableCarMode(int)} to disable 290 * car mode at its priority level. The system will then unbind from app B and bind to app A as 291 * it has the next highest priority. 292 * <p> 293 * When an app enables car mode at a certain priority, it can disable car mode at the specified 294 * priority level using {@link #disableCarMode(int)}. An app may only enable car mode at a 295 * single priority. 296 * <p> 297 * Public apps are assumed to enter/exit car mode at the lowest priority, 298 * {@link #DEFAULT_PRIORITY}. 299 * 300 * @param priority The declared priority for the caller, where {@link #DEFAULT_PRIORITY} (0) is 301 * the lowest priority and higher numbers represent a higher priority. 302 * The priorities apps declare when entering car mode is determined by the 303 * device manufacturer based on the desired car mode experience. 304 * @param flags Car mode flags. 305 * @hide 306 */ 307 @SystemApi 308 @TestApi 309 @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) enableCarMode(@ntRangefrom = 0) int priority, @EnableCarMode int flags)310 public void enableCarMode(@IntRange(from = 0) int priority, @EnableCarMode int flags) { 311 if (mService != null) { 312 try { 313 mService.enableCarMode(flags, priority, 314 mContext == null ? null : mContext.getOpPackageName()); 315 } catch (RemoteException e) { 316 throw e.rethrowFromSystemServer(); 317 } 318 } 319 } 320 321 /** 322 * Flag for use with {@link #disableCarMode(int)}: go to the normal 323 * home activity as part of the disable. Disabling this way ensures 324 * a clean transition between the current activity (in car mode) and 325 * the original home activity (which was typically last running without 326 * being in car mode). 327 */ 328 public static final int DISABLE_CAR_MODE_GO_HOME = 0x0001; 329 330 /** 331 * Flag for use with {@link #disableCarMode(int)}: Disables car mode at ALL priority levels. 332 * Primarily intended for use from {@link com.android.internal.app.DisableCarModeActivity} to 333 * provide the user with a means to exit car mode at all priority levels. 334 * @hide 335 */ 336 public static final int DISABLE_CAR_MODE_ALL_PRIORITIES = 0x0002; 337 338 /** @hide */ 339 @IntDef(prefix = { "DISABLE_CAR_MODE_" }, value = { 340 DISABLE_CAR_MODE_GO_HOME 341 }) 342 @Retention(RetentionPolicy.SOURCE) 343 public @interface DisableCarMode {} 344 345 /** 346 * The default priority used for entering car mode. 347 * <p> 348 * Callers of the {@link #enableCarMode(int)} priority will be assigned the default priority. 349 * This is considered the lowest possible priority for enabling car mode. 350 * <p> 351 * System apps can specify a priority other than the default priority when using 352 * {@link #enableCarMode(int, int)} to enable car mode. 353 * @hide 354 */ 355 @SystemApi 356 public static final int DEFAULT_PRIORITY = 0; 357 358 /** 359 * Turn off special mode if currently in car mode. 360 * @param flags One of the disable car mode flags. 361 */ disableCarMode(@isableCarMode int flags)362 public void disableCarMode(@DisableCarMode int flags) { 363 if (mService != null) { 364 try { 365 mService.disableCarModeByCallingPackage(flags, 366 mContext == null ? null : mContext.getOpPackageName()); 367 } catch (RemoteException e) { 368 throw e.rethrowFromSystemServer(); 369 } 370 } 371 } 372 373 /** 374 * Return the current running mode type. May be one of 375 * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL}, 376 * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, 377 * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, 378 * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TELEVISION}, 379 * {@link Configuration#UI_MODE_TYPE_APPLIANCE Configuration.UI_MODE_TYPE_APPLIANCE}, 380 * {@link Configuration#UI_MODE_TYPE_WATCH Configuration.UI_MODE_TYPE_WATCH}, or 381 * {@link Configuration#UI_MODE_TYPE_VR_HEADSET Configuration.UI_MODE_TYPE_VR_HEADSET}. 382 */ getCurrentModeType()383 public int getCurrentModeType() { 384 if (mService != null) { 385 try { 386 return mService.getCurrentModeType(); 387 } catch (RemoteException e) { 388 throw e.rethrowFromSystemServer(); 389 } 390 } 391 return Configuration.UI_MODE_TYPE_NORMAL; 392 } 393 394 /** 395 * Sets the system-wide night mode. 396 * <p> 397 * The mode can be one of: 398 * <ul> 399 * <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into 400 * {@code notnight} mode</li> 401 * <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into 402 * {@code night} mode</li> 403 * <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between 404 * {@code night} and {@code notnight} based on the device's current 405 * location and certain other sensors</li> 406 * </ul> 407 * <p> 408 * <strong>Note:</strong> On API 22 and below, changes to the night mode 409 * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car} 410 * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a 411 * device. On API 23 through API 28, changes to night mode are always effective. 412 * <p> 413 * Starting in API 29, when the device is in car mode and this method is called, night mode 414 * will change, but the new setting is not persisted and the previously persisted setting 415 * will be restored when the device exits car mode. 416 * <p> 417 * Changes to night mode take effect globally and will result in a configuration change 418 * (and potentially an Activity lifecycle event) being applied to all running apps. 419 * Developers interested in an app-local implementation of night mode should consider using 420 * {@link android.support.v7.app.AppCompatDelegate#setDefaultNightMode(int)} to manage the 421 * -night qualifier locally. 422 * 423 * @param mode the night mode to set 424 * @see #getNightMode() 425 */ setNightMode(@ightMode int mode)426 public void setNightMode(@NightMode int mode) { 427 if (mService != null) { 428 try { 429 mService.setNightMode(mode); 430 } catch (RemoteException e) { 431 throw e.rethrowFromSystemServer(); 432 } 433 } 434 } 435 436 /** 437 * Returns the currently configured night mode. 438 * <p> 439 * May be one of: 440 * <ul> 441 * <li>{@link #MODE_NIGHT_NO}</li> 442 * <li>{@link #MODE_NIGHT_YES}</li> 443 * <li>{@link #MODE_NIGHT_AUTO}</li> 444 * <li>{@code -1} on error</li> 445 * </ul> 446 * 447 * @return the current night mode, or {@code -1} on error 448 * @see #setNightMode(int) 449 */ getNightMode()450 public @NightMode int getNightMode() { 451 if (mService != null) { 452 try { 453 return mService.getNightMode(); 454 } catch (RemoteException e) { 455 throw e.rethrowFromSystemServer(); 456 } 457 } 458 return -1; 459 } 460 461 /** 462 * @return If UI mode is locked or not. When UI mode is locked, calls to change UI mode 463 * like {@link #enableCarMode(int)} will silently fail. 464 * @hide 465 */ 466 @TestApi isUiModeLocked()467 public boolean isUiModeLocked() { 468 if (mService != null) { 469 try { 470 return mService.isUiModeLocked(); 471 } catch (RemoteException e) { 472 throw e.rethrowFromSystemServer(); 473 } 474 } 475 return true; 476 } 477 478 /** 479 * Returns whether night mode is locked or not. 480 * <p> 481 * When night mode is locked, only privileged system components may change 482 * night mode and calls from non-privileged applications to change night 483 * mode will fail silently. 484 * 485 * @return {@code true} if night mode is locked or {@code false} otherwise 486 * @hide 487 */ 488 @TestApi isNightModeLocked()489 public boolean isNightModeLocked() { 490 if (mService != null) { 491 try { 492 return mService.isNightModeLocked(); 493 } catch (RemoteException e) { 494 throw e.rethrowFromSystemServer(); 495 } 496 } 497 return true; 498 } 499 500 /** 501 * @hide* 502 */ setNightModeActivated(boolean active)503 public boolean setNightModeActivated(boolean active) { 504 if (mService != null) { 505 try { 506 return mService.setNightModeActivated(active); 507 } catch (RemoteException e) { 508 throw e.rethrowFromSystemServer(); 509 } 510 } 511 return false; 512 } 513 } 514