blob: 5f23a9039d8887d1ba6769a77ba8f940f734756e [file] [log] [blame]
Dianne Hackborn7299c412010-03-04 18:41:49 -08001/*
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.IActivityManager;
Mike Lockwood924e1642010-03-05 11:56:53 -050023import android.app.KeyguardManager;
Dianne Hackborn7299c412010-03-04 18:41:49 -080024import android.app.IUiModeManager;
25import android.app.Notification;
26import android.app.NotificationManager;
27import android.app.PendingIntent;
28import android.app.StatusBarManager;
29import android.app.UiModeManager;
30import android.content.ActivityNotFoundException;
31import android.content.BroadcastReceiver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.pm.PackageManager;
36import android.content.res.Configuration;
37import android.location.Criteria;
38import android.location.Location;
39import android.location.LocationListener;
40import android.location.LocationManager;
Mike Lockwoode29db6a2010-03-05 13:45:51 -050041import android.os.BatteryManager;
Dianne Hackborn7299c412010-03-04 18:41:49 -080042import android.os.Binder;
43import android.os.Bundle;
44import android.os.Handler;
45import android.os.Message;
Mike Lockwoode29db6a2010-03-05 13:45:51 -050046import android.os.PowerManager;
Dianne Hackborn7299c412010-03-04 18:41:49 -080047import android.os.RemoteException;
48import android.os.ServiceManager;
49import android.text.format.DateUtils;
50import android.text.format.Time;
51import android.util.Slog;
52
53import java.io.FileDescriptor;
54import java.io.PrintWriter;
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;
Mike Lockwoode29db6a2010-03-05 13:45:51 -050085 private boolean mCharging = false;
86 private final boolean mCarModeKeepsScreenOn;
87 private final boolean mDeskModeKeepsScreenOn;
Dianne Hackborn7299c412010-03-04 18:41:49 -080088
89 private boolean mComputedNightMode;
90 private int mCurUiMode = 0;
91
92 private Configuration mConfiguration = new Configuration();
93
94 private boolean mSystemReady;
95
96 private NotificationManager mNotificationManager;
97
98 private AlarmManager mAlarmManager;
99
100 private LocationManager mLocationManager;
101 private Location mLocation;
102 private StatusBarManager mStatusBarManager;
Mike Lockwood924e1642010-03-05 11:56:53 -0500103 private KeyguardManager.KeyguardLock mKeyguardLock;
Mike Lockwoode29db6a2010-03-05 13:45:51 -0500104 private final PowerManager.WakeLock mWakeLock;
Dianne Hackborn7299c412010-03-04 18:41:49 -0800105
106 // The broadcast receiver which receives the result of the ordered broadcast sent when
107 // the dock state changes. The original ordered broadcast is sent with an initial result
108 // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
109 // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
110 private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
111 @Override
112 public void onReceive(Context context, Intent intent) {
113 if (getResultCode() != Activity.RESULT_OK) {
114 return;
115 }
116
117 // Launch a dock activity
118 String category;
119 if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
120 // Only launch car home when car mode is enabled.
121 category = Intent.CATEGORY_CAR_DOCK;
122 } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) {
123 category = Intent.CATEGORY_DESK_DOCK;
124 } else {
125 category = null;
126 }
127 if (category != null) {
128 intent = new Intent(Intent.ACTION_MAIN);
129 intent.addCategory(category);
130 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
131 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
132 try {
133 mContext.startActivity(intent);
134 } catch (ActivityNotFoundException e) {
135 Slog.w(TAG, e.getCause());
136 }
137 }
138 }
139 };
140
141 private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
142 @Override
143 public void onReceive(Context context, Intent intent) {
144 if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
145 mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
146 }
147 }
148 };
149
150 private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
151 @Override
152 public void onReceive(Context context, Intent intent) {
153 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
154 Intent.EXTRA_DOCK_STATE_UNDOCKED);
155 updateDockState(state);
156 }
157 };
158
Mike Lockwoode29db6a2010-03-05 13:45:51 -0500159 private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
160 @Override
161 public void onReceive(Context context, Intent intent) {
162 mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
163 synchronized (mLock) {
164 if (mSystemReady) {
165 updateLocked();
166 }
167 }
168 }
169 };
170
Dianne Hackborn7299c412010-03-04 18:41:49 -0800171 // A LocationListener to initialize the network location provider. The location updates
172 // are handled through the passive location provider.
173 private final LocationListener mEmptyLocationListener = new LocationListener() {
174 public void onLocationChanged(Location location) {
175 }
176
177 public void onProviderDisabled(String provider) {
178 }
179
180 public void onProviderEnabled(String provider) {
181 }
182
183 public void onStatusChanged(String provider, int status, Bundle extras) {
184 }
185 };
186
187 private final LocationListener mLocationListener = new LocationListener() {
188
189 public void onLocationChanged(Location location) {
190 final boolean hasMoved = hasMoved(location);
191 final boolean hasBetterAccuracy = mLocation == null
192 || location.getAccuracy() < mLocation.getAccuracy();
193 if (hasMoved || hasBetterAccuracy) {
194 synchronized (mLock) {
195 mLocation = location;
196 if (hasMoved && isDoingNightMode()
197 && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
198 mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
199 }
200 }
201 }
202 }
203
204 public void onProviderDisabled(String provider) {
205 }
206
207 public void onProviderEnabled(String provider) {
208 }
209
210 public void onStatusChanged(String provider, int status, Bundle extras) {
211 }
212
213 /*
214 * The user has moved if the accuracy circles of the two locations
215 * don't overlap.
216 */
217 private boolean hasMoved(Location location) {
218 if (location == null) {
219 return false;
220 }
221 if (mLocation == null) {
222 return true;
223 }
224
225 /* if new location is older than the current one, the devices hasn't
226 * moved.
227 */
228 if (location.getTime() < mLocation.getTime()) {
229 return false;
230 }
231
232 /* Get the distance between the two points */
233 float distance = mLocation.distanceTo(location);
234
235 /* Get the total accuracy radius for both locations */
236 float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
237
238 /* If the distance is greater than the combined accuracy of the two
239 * points then they can't overlap and hence the user has moved.
240 */
241 return distance >= totalAccuracy;
242 }
243 };
244
245 public UiModeManagerService(Context context) {
246 mContext = context;
247
248 ServiceManager.addService(Context.UI_MODE_SERVICE, this);
249
250 mAlarmManager =
251 (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
252 mLocationManager =
253 (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
254 mContext.registerReceiver(mTwilightUpdateReceiver,
255 new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
256 mContext.registerReceiver(mDockModeReceiver,
257 new IntentFilter(Intent.ACTION_DOCK_EVENT));
Mike Lockwoode29db6a2010-03-05 13:45:51 -0500258 mContext.registerReceiver(mBatteryReceiver,
259 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
260
261 PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
262 mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
Dianne Hackborn7299c412010-03-04 18:41:49 -0800263
264 mConfiguration.setToDefaults();
Mike Lockwoode29db6a2010-03-05 13:45:51 -0500265
266 mCarModeKeepsScreenOn = (context.getResources().getInteger(
267 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
268 mDeskModeKeepsScreenOn = (context.getResources().getInteger(
269 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
Dianne Hackborn7299c412010-03-04 18:41:49 -0800270 }
271
272 public void disableCarMode() {
273 synchronized (mLock) {
274 setCarModeLocked(false);
Mike Lockwood924e1642010-03-05 11:56:53 -0500275 if (mSystemReady) {
276 updateLocked();
277 }
Dianne Hackborn7299c412010-03-04 18:41:49 -0800278 }
279 }
280
281 public void enableCarMode() {
282 mContext.enforceCallingOrSelfPermission(
283 android.Manifest.permission.ENABLE_CAR_MODE,
284 "Need ENABLE_CAR_MODE permission");
285 synchronized (mLock) {
286 setCarModeLocked(true);
Mike Lockwood924e1642010-03-05 11:56:53 -0500287 if (mSystemReady) {
288 updateLocked();
289 }
Dianne Hackborn7299c412010-03-04 18:41:49 -0800290 }
291 }
292
293 public int getCurrentModeType() {
294 synchronized (mLock) {
295 return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
296 }
297 }
298
299 public void setNightMode(int mode) throws RemoteException {
300 synchronized (mLock) {
301 switch (mode) {
302 case UiModeManager.MODE_NIGHT_NO:
303 case UiModeManager.MODE_NIGHT_YES:
304 case UiModeManager.MODE_NIGHT_AUTO:
305 break;
306 default:
307 throw new IllegalArgumentException("Unknown mode: " + mode);
308 }
309 if (!isDoingNightMode()) {
310 return;
311 }
312
313 if (mNightMode != mode) {
314 mNightMode = mode;
315 updateLocked();
316 }
317 }
318 }
319
320 public int getNightMode() throws RemoteException {
321 return mNightMode;
322 }
323
324 void systemReady() {
325 synchronized (mLock) {
326 mSystemReady = true;
327 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
328 updateLocked();
329 mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
330 }
331 }
332
333 boolean isDoingNightMode() {
334 return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
335 }
336
337 void setCarModeLocked(boolean enabled) {
338 if (mCarModeEnabled != enabled) {
339 mCarModeEnabled = enabled;
Mike Lockwood924e1642010-03-05 11:56:53 -0500340
341 // Disable keyguard when in car mode
342 if (mKeyguardLock == null) {
343 KeyguardManager km =
344 (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
345 if (km != null) {
346 mKeyguardLock = km.newKeyguardLock(TAG);
347 }
348 }
349 if (mKeyguardLock != null) {
Tobias Haamel9f938812010-03-08 11:21:59 +0100350 long ident = Binder.clearCallingIdentity();
Mike Lockwood924e1642010-03-05 11:56:53 -0500351 if (enabled) {
352 mKeyguardLock.disableKeyguard();
353 } else {
354 mKeyguardLock.reenableKeyguard();
355 }
Tobias Haamel9f938812010-03-08 11:21:59 +0100356 Binder.restoreCallingIdentity(ident);
Mike Lockwood924e1642010-03-05 11:56:53 -0500357 }
Dianne Hackborn7299c412010-03-04 18:41:49 -0800358 }
359 }
360
361 void updateDockState(int newState) {
362 synchronized (mLock) {
363 if (newState != mDockState) {
364 mDockState = newState;
Mike Lockwood924e1642010-03-05 11:56:53 -0500365 setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR);
Dianne Hackborn7299c412010-03-04 18:41:49 -0800366 if (mSystemReady) {
367 updateLocked();
368 }
369 }
370 }
371 }
Mike Lockwoode29db6a2010-03-05 13:45:51 -0500372
Dianne Hackborn7299c412010-03-04 18:41:49 -0800373 final void updateLocked() {
374 long ident = Binder.clearCallingIdentity();
375
376 try {
377 int uiMode = 0;
378 if (mCarModeEnabled) {
379 uiMode = Configuration.UI_MODE_TYPE_CAR;
380 } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
381 uiMode = Configuration.UI_MODE_TYPE_DESK;
382 }
383 if (uiMode != 0) {
384 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
385 updateTwilightLocked();
386 uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
387 : Configuration.UI_MODE_NIGHT_NO;
388 } else {
389 uiMode |= mNightMode << 4;
390 }
391 } else {
392 // Disabling the car mode clears the night mode.
393 uiMode = Configuration.UI_MODE_TYPE_NORMAL |
394 Configuration.UI_MODE_NIGHT_NO;
395 }
396
397 if (uiMode != mCurUiMode) {
398 mCurUiMode = uiMode;
399
400 try {
401 final IActivityManager am = ActivityManagerNative.getDefault();
402 mConfiguration.uiMode = uiMode;
403 am.updateConfiguration(mConfiguration);
404 } catch (RemoteException e) {
405 Slog.w(TAG, "Failure communicating with activity manager", e);
406 }
407 }
408
409 String action = null;
410 String oldAction = null;
411 if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
412 oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
413 } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) {
414 oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
415 }
416
417 if (mCarModeEnabled) {
418 if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
419 adjustStatusBarCarModeLocked();
420
421 if (oldAction != null) {
422 mContext.sendBroadcast(new Intent(oldAction));
423 }
424 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
425 action = UiModeManager.ACTION_ENTER_CAR_MODE;
426 }
427 } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
428 if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_DESK) {
429 if (oldAction != null) {
430 mContext.sendBroadcast(new Intent(oldAction));
431 }
432 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_DESK;
433 action = UiModeManager.ACTION_ENTER_DESK_MODE;
434 }
435 } else {
436 if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
437 adjustStatusBarCarModeLocked();
438 }
439
440 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
441 action = oldAction;
442 }
443
444 if (action != null) {
445 // Send the ordered broadcast; the result receiver will receive after all
446 // broadcasts have been sent. If any broadcast receiver changes the result
447 // code from the initial value of RESULT_OK, then the result receiver will
448 // not launch the corresponding dock application. This gives apps a chance
449 // to override the behavior and stay in their app even when the device is
450 // placed into a dock.
451 mContext.sendOrderedBroadcast(new Intent(action), null,
452 mResultReceiver, null, Activity.RESULT_OK, null, null);
453 }
Mike Lockwoode29db6a2010-03-05 13:45:51 -0500454
455 // keep screen on when charging and in car mode
456 boolean keepScreenOn = mCharging &&
457 ((mCarModeEnabled && mCarModeKeepsScreenOn) ||
458 (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
459 if (keepScreenOn != mWakeLock.isHeld()) {
460 if (keepScreenOn) {
461 mWakeLock.acquire();
462 } else {
463 mWakeLock.release();
464 }
465 }
Dianne Hackborn7299c412010-03-04 18:41:49 -0800466 } finally {
467 Binder.restoreCallingIdentity(ident);
468 }
469 }
470
471 private void adjustStatusBarCarModeLocked() {
472 if (mStatusBarManager == null) {
473 mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
474 }
475
476 // Fear not: StatusBarService manages a list of requests to disable
477 // features of the status bar; these are ORed together to form the
478 // active disabled list. So if (for example) the device is locked and
479 // the status bar should be totally disabled, the calls below will
480 // have no effect until the device is unlocked.
481 if (mStatusBarManager != null) {
482 mStatusBarManager.disable(mCarModeEnabled
483 ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
484 : StatusBarManager.DISABLE_NONE);
485 }
486
487 if (mNotificationManager == null) {
488 mNotificationManager = (NotificationManager)
489 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
490 }
491
492 if (mNotificationManager != null) {
493 if (mCarModeEnabled) {
494 Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
495
496 Notification n = new Notification();
497 n.icon = R.drawable.stat_notify_car_mode;
498 n.defaults = Notification.DEFAULT_LIGHTS;
499 n.flags = Notification.FLAG_ONGOING_EVENT;
500 n.when = 0;
501 n.setLatestEventInfo(
502 mContext,
503 mContext.getString(R.string.car_mode_disable_notification_title),
504 mContext.getString(R.string.car_mode_disable_notification_message),
505 PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
506 mNotificationManager.notify(0, n);
507 } else {
508 mNotificationManager.cancel(0);
509 }
510 }
511 }
512
513 private final Handler mHandler = new Handler() {
514
515 boolean mPassiveListenerEnabled;
516 boolean mNetworkListenerEnabled;
517
518 @Override
519 public void handleMessage(Message msg) {
520 switch (msg.what) {
521 case MSG_UPDATE_TWILIGHT:
522 synchronized (mLock) {
523 if (isDoingNightMode() && mLocation != null
524 && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
525 updateTwilightLocked();
526 updateLocked();
527 }
528 }
529 break;
530 case MSG_ENABLE_LOCATION_UPDATES:
531 // enable network provider to receive at least location updates for a given
532 // distance.
533 boolean networkLocationEnabled;
534 try {
535 networkLocationEnabled =
536 mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
537 } catch (Exception e) {
538 // we may get IllegalArgumentException if network location provider
539 // does not exist or is not yet installed.
540 networkLocationEnabled = false;
541 }
542 if (!mNetworkListenerEnabled && networkLocationEnabled) {
543 mNetworkListenerEnabled = true;
544 mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
545 LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
546
547 if (mLocation == null) {
548 retrieveLocation();
549 }
550 synchronized (mLock) {
551 if (isDoingNightMode() && mLocation != null
552 && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
553 updateTwilightLocked();
554 updateLocked();
555 }
556 }
557 }
558 // enable passive provider to receive updates from location fixes (gps
559 // and network).
560 boolean passiveLocationEnabled;
561 try {
562 passiveLocationEnabled =
563 mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
564 } catch (Exception e) {
565 // we may get IllegalArgumentException if passive location provider
566 // does not exist or is not yet installed.
567 passiveLocationEnabled = false;
568 }
569 if (!mPassiveListenerEnabled && passiveLocationEnabled) {
570 mPassiveListenerEnabled = true;
571 mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
572 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
573 }
574 if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
575 long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
576 interval *= 1.5;
577 if (interval == 0) {
578 interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
579 } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
580 interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
581 }
582 Bundle bundle = new Bundle();
583 bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
584 Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
585 newMsg.setData(bundle);
586 mHandler.sendMessageDelayed(newMsg, interval);
587 }
588 break;
589 }
590 }
591
592 private void retrieveLocation() {
593 Location location;
594 Criteria criteria = new Criteria();
595 criteria.setSpeedRequired(false);
596 criteria.setAltitudeRequired(false);
597 criteria.setBearingRequired(false);
598 criteria.setAccuracy(Criteria.ACCURACY_FINE);
599 final String bestProvider = mLocationManager.getBestProvider(criteria, true);
600 location = mLocationManager.getLastKnownLocation(bestProvider);
601 // In the case there is no location available (e.g. GPS fix or network location
602 // is not available yet), the longitude of the location is estimated using the timezone,
603 // latitude and accuracy are set to get a good average.
604 if (location == null) {
605 Time currentTime = new Time();
606 currentTime.set(System.currentTimeMillis());
607 double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
608 (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
609 location = new Location("fake");
610 location.setLongitude(lngOffset);
611 location.setLatitude(0);
612 location.setAccuracy(417000.0f);
613 location.setTime(System.currentTimeMillis());
614 }
615 synchronized (mLock) {
616 mLocation = location;
617 }
618 }
619 };
620
621 void updateTwilightLocked() {
622 if (mLocation == null) {
623 return;
624 }
625 final long currentTime = System.currentTimeMillis();
626 boolean nightMode;
627 // calculate current twilight
628 TwilightCalculator tw = new TwilightCalculator();
629 tw.calculateTwilight(currentTime,
630 mLocation.getLatitude(), mLocation.getLongitude());
631 if (tw.mState == TwilightCalculator.DAY) {
632 nightMode = false;
633 } else {
634 nightMode = true;
635 }
636
637 // schedule next update
638 long nextUpdate = 0;
639 if (tw.mSunrise == -1 || tw.mSunset == -1) {
640 // In the case the day or night never ends the update is scheduled 12 hours later.
641 nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
642 } else {
643 final int mLastTwilightState = tw.mState;
644 // add some extra time to be on the save side.
645 nextUpdate += DateUtils.MINUTE_IN_MILLIS;
646 if (currentTime > tw.mSunset) {
647 // next update should be on the following day
648 tw.calculateTwilight(currentTime
649 + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
650 mLocation.getLongitude());
651 }
652
653 if (mLastTwilightState == TwilightCalculator.NIGHT) {
654 nextUpdate += tw.mSunrise;
655 } else {
656 nextUpdate += tw.mSunset;
657 }
658 }
659
660 Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
661 PendingIntent pendingIntent =
662 PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
663 mAlarmManager.cancel(pendingIntent);
664 mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
665
666 mComputedNightMode = nightMode;
667 }
668
669 @Override
670 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
671 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
672 != PackageManager.PERMISSION_GRANTED) {
673
674 pw.println("Permission Denial: can't dump uimode service from from pid="
675 + Binder.getCallingPid()
676 + ", uid=" + Binder.getCallingUid());
677 return;
678 }
679
680 synchronized (mLock) {
681 pw.println("Current UI Mode Service state:");
682 pw.print(" mDockState="); pw.print(mDockState);
683 pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
684 pw.print(" mNightMode="); pw.print(mNightMode);
685 pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
686 pw.print(" mComputedNightMode="); pw.println(mComputedNightMode);
687 pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
688 pw.print(" mSystemReady="); pw.println(mSystemReady);
689 if (mLocation != null) {
690 pw.print(" mLocation="); pw.println(mLocation);
691 }
692 }
693 }
694}