PageRenderTime 27ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

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

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