PageRenderTime 148ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

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

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