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