PageRenderTime 103ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/services/java/com/android/server/UiModeManagerService.java

https://bitbucket.org/ProjectOpenCannibal/cm_android_frameworks_base
Java | 884 lines | 690 code | 102 blank | 92 comment | 177 complexity | db93d09f262edff9538cdfabd35758b1 MD5 | raw file
  1. /*
  2. * Copyright (C) 2008 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. package com.android.server;
  17. import android.app.Activity;
  18. import android.app.ActivityManagerNative;
  19. import android.app.AlarmManager;
  20. import android.app.IUiModeManager;
  21. import android.app.Notification;
  22. import android.app.NotificationManager;
  23. import android.app.PendingIntent;
  24. import android.app.StatusBarManager;
  25. import android.app.UiModeManager;
  26. import android.content.ActivityNotFoundException;
  27. import android.content.BroadcastReceiver;
  28. import android.content.Context;
  29. import android.content.Intent;
  30. import android.content.IntentFilter;
  31. import android.content.pm.PackageManager;
  32. import android.content.res.Configuration;
  33. import android.location.Criteria;
  34. import android.location.Location;
  35. import android.location.LocationListener;
  36. import android.location.LocationManager;
  37. import android.os.BatteryManager;
  38. import android.os.Binder;
  39. import android.os.Bundle;
  40. import android.os.Handler;
  41. import android.os.Message;
  42. import android.os.PowerManager;
  43. import android.os.RemoteException;
  44. import android.os.ServiceManager;
  45. import android.os.SystemClock;
  46. import android.provider.Settings;
  47. import android.text.format.DateUtils;
  48. import android.text.format.Time;
  49. import android.util.Slog;
  50. import java.io.FileDescriptor;
  51. import java.io.PrintWriter;
  52. import java.util.Iterator;
  53. import com.android.internal.R;
  54. import com.android.internal.app.DisableCarModeActivity;
  55. import com.android.internal.app.ThemeUtils;
  56. class UiModeManagerService extends IUiModeManager.Stub {
  57. private static final String TAG = UiModeManager.class.getSimpleName();
  58. private static final boolean LOG = false;
  59. private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
  60. // Enable launching of applications when entering the dock.
  61. private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true;
  62. private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
  63. private static final int MSG_UPDATE_TWILIGHT = 0;
  64. private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
  65. private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
  66. private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
  67. private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
  68. private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
  69. private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
  70. private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 15 * DateUtils.MINUTE_IN_MILLIS;
  71. private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
  72. private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
  73. private final Context mContext;
  74. private Context mUiContext;
  75. final Object mLock = new Object();
  76. private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
  77. private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
  78. private int mNightMode = UiModeManager.MODE_NIGHT_NO;
  79. private boolean mCarModeEnabled = false;
  80. private boolean mCharging = false;
  81. private final int mDefaultUiModeType;
  82. private final boolean mCarModeKeepsScreenOn;
  83. private final boolean mDeskModeKeepsScreenOn;
  84. private final boolean mTelevision;
  85. private boolean mComputedNightMode;
  86. private int mCurUiMode = 0;
  87. private int mSetUiMode = 0;
  88. private boolean mHoldingConfiguration = false;
  89. private Configuration mConfiguration = new Configuration();
  90. private boolean mSystemReady;
  91. private NotificationManager mNotificationManager;
  92. private AlarmManager mAlarmManager;
  93. private LocationManager mLocationManager;
  94. private Location mLocation;
  95. private StatusBarManager mStatusBarManager;
  96. private final PowerManager.WakeLock mWakeLock;
  97. static Intent buildHomeIntent(String category) {
  98. Intent intent = new Intent(Intent.ACTION_MAIN);
  99. intent.addCategory(category);
  100. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
  101. | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
  102. return intent;
  103. }
  104. // The broadcast receiver which receives the result of the ordered broadcast sent when
  105. // the dock state changes. The original ordered broadcast is sent with an initial result
  106. // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
  107. // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
  108. private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
  109. @Override
  110. public void onReceive(Context context, Intent intent) {
  111. if (getResultCode() != Activity.RESULT_OK) {
  112. if (LOG) {
  113. Slog.v(TAG, "Handling broadcast result for action " + intent.getAction()
  114. + ": canceled: " + getResultCode());
  115. }
  116. return;
  117. }
  118. final int enableFlags = intent.getIntExtra("enableFlags", 0);
  119. final int disableFlags = intent.getIntExtra("disableFlags", 0);
  120. synchronized (mLock) {
  121. // Launch a dock activity
  122. String category = null;
  123. if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
  124. // Only launch car home when car mode is enabled and the caller
  125. // has asked us to switch to it.
  126. if (ENABLE_LAUNCH_CAR_DOCK_APP
  127. && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
  128. category = Intent.CATEGORY_CAR_DOCK;
  129. }
  130. } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) {
  131. // Only launch car home when desk mode is enabled and the caller
  132. // has asked us to switch to it. Currently re-using the car
  133. // mode flag since we don't have a formal API for "desk mode".
  134. if (ENABLE_LAUNCH_DESK_DOCK_APP
  135. && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
  136. category = Intent.CATEGORY_DESK_DOCK;
  137. }
  138. } else {
  139. // Launch the standard home app if requested.
  140. if ((disableFlags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
  141. category = Intent.CATEGORY_HOME;
  142. }
  143. }
  144. if (LOG) {
  145. Slog.v(TAG, String.format(
  146. "Handling broadcast result for action %s: enable=0x%08x disable=0x%08x category=%s",
  147. intent.getAction(), enableFlags, disableFlags, category));
  148. }
  149. if (category != null) {
  150. // This is the new activity that will serve as home while
  151. // we are in care mode.
  152. Intent homeIntent = buildHomeIntent(category);
  153. // Now we are going to be careful about switching the
  154. // configuration and starting the activity -- we need to
  155. // do this in a specific order under control of the
  156. // activity manager, to do it cleanly. So compute the
  157. // new config, but don't set it yet, and let the
  158. // activity manager take care of both the start and config
  159. // change.
  160. Configuration newConfig = null;
  161. if (mHoldingConfiguration) {
  162. mHoldingConfiguration = false;
  163. updateConfigurationLocked(false);
  164. newConfig = mConfiguration;
  165. }
  166. try {
  167. ActivityManagerNative.getDefault().startActivityWithConfig(
  168. null, homeIntent, null, null, null, 0, 0,
  169. newConfig, null);
  170. mHoldingConfiguration = false;
  171. } catch (RemoteException e) {
  172. Slog.w(TAG, e.getCause());
  173. }
  174. }
  175. if (mHoldingConfiguration) {
  176. mHoldingConfiguration = false;
  177. updateConfigurationLocked(true);
  178. }
  179. }
  180. }
  181. };
  182. private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
  183. @Override
  184. public void onReceive(Context context, Intent intent) {
  185. if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
  186. mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
  187. }
  188. }
  189. };
  190. private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
  191. @Override
  192. public void onReceive(Context context, Intent intent) {
  193. int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
  194. Intent.EXTRA_DOCK_STATE_UNDOCKED);
  195. updateDockState(state);
  196. }
  197. };
  198. private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
  199. @Override
  200. public void onReceive(Context context, Intent intent) {
  201. mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
  202. synchronized (mLock) {
  203. if (mSystemReady) {
  204. updateLocked(0, 0);
  205. }
  206. }
  207. }
  208. };
  209. private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
  210. @Override
  211. public void onReceive(Context context, Intent intent) {
  212. if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
  213. if (!intent.getBooleanExtra("state", false)) {
  214. // Airplane mode is now off!
  215. mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
  216. }
  217. } else {
  218. // Time zone has changed!
  219. mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
  220. }
  221. }
  222. };
  223. private final BroadcastReceiver mThemeChangeReceiver = new BroadcastReceiver() {
  224. @Override
  225. public void onReceive(Context context, Intent intent) {
  226. mUiContext = null;
  227. }
  228. };
  229. // A LocationListener to initialize the network location provider. The location updates
  230. // are handled through the passive location provider.
  231. private final LocationListener mEmptyLocationListener = new LocationListener() {
  232. public void onLocationChanged(Location location) {
  233. }
  234. public void onProviderDisabled(String provider) {
  235. }
  236. public void onProviderEnabled(String provider) {
  237. }
  238. public void onStatusChanged(String provider, int status, Bundle extras) {
  239. }
  240. };
  241. private final LocationListener mLocationListener = new LocationListener() {
  242. public void onLocationChanged(Location location) {
  243. final boolean hasMoved = hasMoved(location);
  244. final boolean hasBetterAccuracy = mLocation == null
  245. || location.getAccuracy() < mLocation.getAccuracy();
  246. if (hasMoved || hasBetterAccuracy) {
  247. synchronized (mLock) {
  248. mLocation = location;
  249. if (hasMoved && isDoingNightMode()
  250. && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
  251. mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
  252. }
  253. }
  254. }
  255. }
  256. public void onProviderDisabled(String provider) {
  257. }
  258. public void onProviderEnabled(String provider) {
  259. }
  260. public void onStatusChanged(String provider, int status, Bundle extras) {
  261. }
  262. /*
  263. * The user has moved if the accuracy circles of the two locations
  264. * don't overlap.
  265. */
  266. private boolean hasMoved(Location location) {
  267. if (location == null) {
  268. return false;
  269. }
  270. if (mLocation == null) {
  271. return true;
  272. }
  273. /* if new location is older than the current one, the devices hasn't
  274. * moved.
  275. */
  276. if (location.getTime() < mLocation.getTime()) {
  277. return false;
  278. }
  279. /* Get the distance between the two points */
  280. float distance = mLocation.distanceTo(location);
  281. /* Get the total accuracy radius for both locations */
  282. float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
  283. /* If the distance is greater than the combined accuracy of the two
  284. * points then they can't overlap and hence the user has moved.
  285. */
  286. return distance >= totalAccuracy;
  287. }
  288. };
  289. public UiModeManagerService(Context context) {
  290. mContext = context;
  291. ServiceManager.addService(Context.UI_MODE_SERVICE, this);
  292. mAlarmManager =
  293. (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
  294. mLocationManager =
  295. (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
  296. mContext.registerReceiver(mTwilightUpdateReceiver,
  297. new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
  298. mContext.registerReceiver(mDockModeReceiver,
  299. new IntentFilter(Intent.ACTION_DOCK_EVENT));
  300. mContext.registerReceiver(mBatteryReceiver,
  301. new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
  302. IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
  303. filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
  304. mContext.registerReceiver(mUpdateLocationReceiver, filter);
  305. ThemeUtils.registerThemeChangeReceiver(mContext, mThemeChangeReceiver);
  306. PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
  307. mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
  308. mConfiguration.setToDefaults();
  309. mDefaultUiModeType = context.getResources().getInteger(
  310. com.android.internal.R.integer.config_defaultUiModeType);
  311. mCarModeKeepsScreenOn = (context.getResources().getInteger(
  312. com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
  313. mDeskModeKeepsScreenOn = (context.getResources().getInteger(
  314. com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
  315. mTelevision = context.getPackageManager().hasSystemFeature(
  316. PackageManager.FEATURE_TELEVISION);
  317. mNightMode = Settings.Secure.getInt(mContext.getContentResolver(),
  318. Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO);
  319. }
  320. public void disableCarMode(int flags) {
  321. synchronized (mLock) {
  322. setCarModeLocked(false);
  323. if (mSystemReady) {
  324. updateLocked(0, flags);
  325. }
  326. }
  327. }
  328. public void enableCarMode(int flags) {
  329. synchronized (mLock) {
  330. setCarModeLocked(true);
  331. if (mSystemReady) {
  332. updateLocked(flags, 0);
  333. }
  334. }
  335. }
  336. public int getCurrentModeType() {
  337. synchronized (mLock) {
  338. return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
  339. }
  340. }
  341. public void setNightMode(int mode) throws RemoteException {
  342. synchronized (mLock) {
  343. switch (mode) {
  344. case UiModeManager.MODE_NIGHT_NO:
  345. case UiModeManager.MODE_NIGHT_YES:
  346. case UiModeManager.MODE_NIGHT_AUTO:
  347. break;
  348. default:
  349. throw new IllegalArgumentException("Unknown mode: " + mode);
  350. }
  351. if (!isDoingNightMode()) {
  352. return;
  353. }
  354. if (mNightMode != mode) {
  355. long ident = Binder.clearCallingIdentity();
  356. Settings.Secure.putInt(mContext.getContentResolver(),
  357. Settings.Secure.UI_NIGHT_MODE, mode);
  358. Binder.restoreCallingIdentity(ident);
  359. mNightMode = mode;
  360. updateLocked(0, 0);
  361. }
  362. }
  363. }
  364. public int getNightMode() throws RemoteException {
  365. return mNightMode;
  366. }
  367. void systemReady() {
  368. synchronized (mLock) {
  369. mSystemReady = true;
  370. mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
  371. updateLocked(0, 0);
  372. mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
  373. }
  374. }
  375. boolean isDoingNightMode() {
  376. return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
  377. }
  378. void setCarModeLocked(boolean enabled) {
  379. if (mCarModeEnabled != enabled) {
  380. mCarModeEnabled = enabled;
  381. }
  382. }
  383. void updateDockState(int newState) {
  384. synchronized (mLock) {
  385. if (newState != mDockState) {
  386. mDockState = newState;
  387. setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR);
  388. if (mSystemReady) {
  389. updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0);
  390. }
  391. }
  392. }
  393. }
  394. final static boolean isDeskDockState(int state) {
  395. switch (state) {
  396. case Intent.EXTRA_DOCK_STATE_DESK:
  397. case Intent.EXTRA_DOCK_STATE_LE_DESK:
  398. case Intent.EXTRA_DOCK_STATE_HE_DESK:
  399. return true;
  400. default:
  401. return false;
  402. }
  403. }
  404. final void updateConfigurationLocked(boolean sendIt) {
  405. int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION
  406. : mDefaultUiModeType;
  407. if (mCarModeEnabled) {
  408. uiMode = Configuration.UI_MODE_TYPE_CAR;
  409. } else if (isDeskDockState(mDockState)) {
  410. uiMode = Configuration.UI_MODE_TYPE_DESK;
  411. }
  412. if (mCarModeEnabled) {
  413. if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
  414. updateTwilightLocked();
  415. uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
  416. : Configuration.UI_MODE_NIGHT_NO;
  417. } else {
  418. uiMode |= mNightMode << 4;
  419. }
  420. } else {
  421. // Disabling the car mode clears the night mode.
  422. uiMode = (uiMode & ~Configuration.UI_MODE_NIGHT_MASK) | Configuration.UI_MODE_NIGHT_NO;
  423. }
  424. if (LOG) {
  425. Slog.d(TAG,
  426. "updateConfigurationLocked: mDockState=" + mDockState
  427. + "; mCarMode=" + mCarModeEnabled
  428. + "; mNightMode=" + mNightMode
  429. + "; uiMode=" + uiMode);
  430. }
  431. mCurUiMode = uiMode;
  432. if (!mHoldingConfiguration && uiMode != mSetUiMode) {
  433. mSetUiMode = uiMode;
  434. mConfiguration.uiMode = uiMode;
  435. if (sendIt) {
  436. try {
  437. ActivityManagerNative.getDefault().updateConfiguration(mConfiguration);
  438. } catch (RemoteException e) {
  439. Slog.w(TAG, "Failure communicating with activity manager", e);
  440. }
  441. }
  442. }
  443. }
  444. final void updateLocked(int enableFlags, int disableFlags) {
  445. long ident = Binder.clearCallingIdentity();
  446. try {
  447. String action = null;
  448. String oldAction = null;
  449. if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
  450. adjustStatusBarCarModeLocked();
  451. oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
  452. } else if (isDeskDockState(mLastBroadcastState)) {
  453. oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
  454. }
  455. if (mCarModeEnabled) {
  456. if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
  457. adjustStatusBarCarModeLocked();
  458. if (oldAction != null) {
  459. mContext.sendBroadcast(new Intent(oldAction));
  460. }
  461. mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
  462. action = UiModeManager.ACTION_ENTER_CAR_MODE;
  463. }
  464. } else if (isDeskDockState(mDockState)) {
  465. if (!isDeskDockState(mLastBroadcastState)) {
  466. if (oldAction != null) {
  467. mContext.sendBroadcast(new Intent(oldAction));
  468. }
  469. mLastBroadcastState = mDockState;
  470. action = UiModeManager.ACTION_ENTER_DESK_MODE;
  471. }
  472. } else {
  473. mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
  474. action = oldAction;
  475. }
  476. if (action != null) {
  477. if (LOG) {
  478. Slog.v(TAG, String.format(
  479. "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x",
  480. action, enableFlags, disableFlags));
  481. }
  482. // Send the ordered broadcast; the result receiver will receive after all
  483. // broadcasts have been sent. If any broadcast receiver changes the result
  484. // code from the initial value of RESULT_OK, then the result receiver will
  485. // not launch the corresponding dock application. This gives apps a chance
  486. // to override the behavior and stay in their app even when the device is
  487. // placed into a dock.
  488. Intent intent = new Intent(action);
  489. intent.putExtra("enableFlags", enableFlags);
  490. intent.putExtra("disableFlags", disableFlags);
  491. mContext.sendOrderedBroadcast(intent, null,
  492. mResultReceiver, null, Activity.RESULT_OK, null, null);
  493. // Attempting to make this transition a little more clean, we are going
  494. // to hold off on doing a configuration change until we have finished
  495. // the broadcast and started the home activity.
  496. mHoldingConfiguration = true;
  497. } else {
  498. Intent homeIntent = null;
  499. if (mCarModeEnabled) {
  500. if (ENABLE_LAUNCH_CAR_DOCK_APP
  501. && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
  502. homeIntent = buildHomeIntent(Intent.CATEGORY_CAR_DOCK);
  503. }
  504. } else if (isDeskDockState(mDockState)) {
  505. if (ENABLE_LAUNCH_DESK_DOCK_APP
  506. && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
  507. homeIntent = buildHomeIntent(Intent.CATEGORY_DESK_DOCK);
  508. }
  509. } else {
  510. if ((disableFlags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
  511. homeIntent = buildHomeIntent(Intent.CATEGORY_HOME);
  512. }
  513. }
  514. if (LOG) {
  515. Slog.v(TAG, "updateLocked: null action, mDockState="
  516. + mDockState +", firing homeIntent: " + homeIntent);
  517. }
  518. if (homeIntent != null) {
  519. try {
  520. mContext.startActivity(homeIntent);
  521. } catch (ActivityNotFoundException e) {
  522. }
  523. }
  524. }
  525. updateConfigurationLocked(true);
  526. // keep screen on when charging and in car mode
  527. boolean keepScreenOn = mCharging &&
  528. ((mCarModeEnabled && mCarModeKeepsScreenOn) ||
  529. (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
  530. if (keepScreenOn != mWakeLock.isHeld()) {
  531. if (keepScreenOn) {
  532. mWakeLock.acquire();
  533. } else {
  534. mWakeLock.release();
  535. }
  536. }
  537. } finally {
  538. Binder.restoreCallingIdentity(ident);
  539. }
  540. }
  541. private void adjustStatusBarCarModeLocked() {
  542. if (mStatusBarManager == null) {
  543. mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
  544. }
  545. // Fear not: StatusBarManagerService manages a list of requests to disable
  546. // features of the status bar; these are ORed together to form the
  547. // active disabled list. So if (for example) the device is locked and
  548. // the status bar should be totally disabled, the calls below will
  549. // have no effect until the device is unlocked.
  550. if (mStatusBarManager != null) {
  551. mStatusBarManager.disable(mCarModeEnabled
  552. ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
  553. : StatusBarManager.DISABLE_NONE);
  554. }
  555. if (mNotificationManager == null) {
  556. mNotificationManager = (NotificationManager)
  557. mContext.getSystemService(Context.NOTIFICATION_SERVICE);
  558. }
  559. if (mNotificationManager != null) {
  560. if (mCarModeEnabled) {
  561. Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
  562. Notification n = new Notification();
  563. n.icon = R.drawable.stat_notify_car_mode;
  564. n.defaults = Notification.DEFAULT_LIGHTS;
  565. n.flags = Notification.FLAG_ONGOING_EVENT;
  566. n.when = 0;
  567. n.setLatestEventInfo(
  568. getUiContext(),
  569. mContext.getString(R.string.car_mode_disable_notification_title),
  570. mContext.getString(R.string.car_mode_disable_notification_message),
  571. PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
  572. mNotificationManager.notify(0, n);
  573. } else {
  574. mNotificationManager.cancel(0);
  575. }
  576. }
  577. }
  578. private final Handler mHandler = new Handler() {
  579. boolean mPassiveListenerEnabled;
  580. boolean mNetworkListenerEnabled;
  581. boolean mDidFirstInit;
  582. long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
  583. @Override
  584. public void handleMessage(Message msg) {
  585. switch (msg.what) {
  586. case MSG_UPDATE_TWILIGHT:
  587. synchronized (mLock) {
  588. if (isDoingNightMode() && mLocation != null
  589. && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
  590. updateTwilightLocked();
  591. updateLocked(0, 0);
  592. }
  593. }
  594. break;
  595. case MSG_GET_NEW_LOCATION_UPDATE:
  596. if (!mNetworkListenerEnabled) {
  597. // Don't do anything -- we are still trying to get a
  598. // location.
  599. return;
  600. }
  601. if ((mLastNetworkRegisterTime+MIN_LOCATION_UPDATE_MS)
  602. >= SystemClock.elapsedRealtime()) {
  603. // Don't do anything -- it hasn't been long enough
  604. // since we last requested an update.
  605. return;
  606. }
  607. // Unregister the current location monitor, so we can
  608. // register a new one for it to get an immediate update.
  609. mNetworkListenerEnabled = false;
  610. mLocationManager.removeUpdates(mEmptyLocationListener);
  611. // Fall through to re-register listener.
  612. case MSG_ENABLE_LOCATION_UPDATES:
  613. // enable network provider to receive at least location updates for a given
  614. // distance.
  615. boolean networkLocationEnabled;
  616. try {
  617. networkLocationEnabled =
  618. mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
  619. } catch (Exception e) {
  620. // we may get IllegalArgumentException if network location provider
  621. // does not exist or is not yet installed.
  622. networkLocationEnabled = false;
  623. }
  624. if (!mNetworkListenerEnabled && networkLocationEnabled) {
  625. mNetworkListenerEnabled = true;
  626. mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
  627. mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
  628. LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
  629. if (!mDidFirstInit) {
  630. mDidFirstInit = true;
  631. if (mLocation == null) {
  632. retrieveLocation();
  633. }
  634. synchronized (mLock) {
  635. if (isDoingNightMode() && mLocation != null
  636. && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
  637. updateTwilightLocked();
  638. updateLocked(0, 0);
  639. }
  640. }
  641. }
  642. }
  643. // enable passive provider to receive updates from location fixes (gps
  644. // and network).
  645. boolean passiveLocationEnabled;
  646. try {
  647. passiveLocationEnabled =
  648. mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
  649. } catch (Exception e) {
  650. // we may get IllegalArgumentException if passive location provider
  651. // does not exist or is not yet installed.
  652. passiveLocationEnabled = false;
  653. }
  654. if (!mPassiveListenerEnabled && passiveLocationEnabled) {
  655. mPassiveListenerEnabled = true;
  656. mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
  657. 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
  658. }
  659. if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
  660. long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
  661. interval *= 1.5;
  662. if (interval == 0) {
  663. interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
  664. } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
  665. interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
  666. }
  667. Bundle bundle = new Bundle();
  668. bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
  669. Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
  670. newMsg.setData(bundle);
  671. mHandler.sendMessageDelayed(newMsg, interval);
  672. }
  673. break;
  674. }
  675. }
  676. private void retrieveLocation() {
  677. Location location = null;
  678. final Iterator<String> providers =
  679. mLocationManager.getProviders(new Criteria(), true).iterator();
  680. while (providers.hasNext()) {
  681. final Location lastKnownLocation =
  682. mLocationManager.getLastKnownLocation(providers.next());
  683. // pick the most recent location
  684. if (location == null || (lastKnownLocation != null &&
  685. location.getTime() < lastKnownLocation.getTime())) {
  686. location = lastKnownLocation;
  687. }
  688. }
  689. // In the case there is no location available (e.g. GPS fix or network location
  690. // is not available yet), the longitude of the location is estimated using the timezone,
  691. // latitude and accuracy are set to get a good average.
  692. if (location == null) {
  693. Time currentTime = new Time();
  694. currentTime.set(System.currentTimeMillis());
  695. double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
  696. (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
  697. location = new Location("fake");
  698. location.setLongitude(lngOffset);
  699. location.setLatitude(0);
  700. location.setAccuracy(417000.0f);
  701. location.setTime(System.currentTimeMillis());
  702. }
  703. synchronized (mLock) {
  704. mLocation = location;
  705. }
  706. }
  707. };
  708. void updateTwilightLocked() {
  709. if (mLocation == null) {
  710. return;
  711. }
  712. final long currentTime = System.currentTimeMillis();
  713. boolean nightMode;
  714. // calculate current twilight
  715. TwilightCalculator tw = new TwilightCalculator();
  716. tw.calculateTwilight(currentTime,
  717. mLocation.getLatitude(), mLocation.getLongitude());
  718. if (tw.mState == TwilightCalculator.DAY) {
  719. nightMode = false;
  720. } else {
  721. nightMode = true;
  722. }
  723. // schedule next update
  724. long nextUpdate = 0;
  725. if (tw.mSunrise == -1 || tw.mSunset == -1) {
  726. // In the case the day or night never ends the update is scheduled 12 hours later.
  727. nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
  728. } else {
  729. final int mLastTwilightState = tw.mState;
  730. // add some extra time to be on the save side.
  731. nextUpdate += DateUtils.MINUTE_IN_MILLIS;
  732. if (currentTime > tw.mSunset) {
  733. // next update should be on the following day
  734. tw.calculateTwilight(currentTime
  735. + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
  736. mLocation.getLongitude());
  737. }
  738. if (mLastTwilightState == TwilightCalculator.NIGHT) {
  739. nextUpdate += tw.mSunrise;
  740. } else {
  741. nextUpdate += tw.mSunset;
  742. }
  743. }
  744. Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
  745. PendingIntent pendingIntent =
  746. PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
  747. mAlarmManager.cancel(pendingIntent);
  748. mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
  749. mComputedNightMode = nightMode;
  750. }
  751. private Context getUiContext() {
  752. if (mUiContext == null) {
  753. mUiContext = ThemeUtils.createUiContext(mContext);
  754. }
  755. return mUiContext != null ? mUiContext : mContext;
  756. }
  757. @Override
  758. protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
  759. if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
  760. != PackageManager.PERMISSION_GRANTED) {
  761. pw.println("Permission Denial: can't dump uimode service from from pid="
  762. + Binder.getCallingPid()
  763. + ", uid=" + Binder.getCallingUid());
  764. return;
  765. }
  766. synchronized (mLock) {
  767. pw.println("Current UI Mode Service state:");
  768. pw.print(" mDockState="); pw.print(mDockState);
  769. pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
  770. pw.print(" mNightMode="); pw.print(mNightMode);
  771. pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
  772. pw.print(" mComputedNightMode="); pw.println(mComputedNightMode);
  773. pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
  774. pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
  775. pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration);
  776. pw.print(" mSystemReady="); pw.println(mSystemReady);
  777. if (mLocation != null) {
  778. pw.print(" mLocation="); pw.println(mLocation);
  779. }
  780. }
  781. }
  782. }