blob: 71826ffddc7dc95f7bbcff8428108fb8c36dbb5c [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) {
350 if (enabled) {
351 mKeyguardLock.disableKeyguard();
352 } else {
353 mKeyguardLock.reenableKeyguard();
354 }
355 }
Dianne Hackborn7299c412010-03-04 18:41:49 -0800356 }
357 }
358
359 void updateDockState(int newState) {
360 synchronized (mLock) {
361 if (newState != mDockState) {
362 mDockState = newState;
Mike Lockwood924e1642010-03-05 11:56:53 -0500363 setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR);
Dianne Hackborn7299c412010-03-04 18:41:49 -0800364 if (mSystemReady) {
365 updateLocked();
366 }
367 }
368 }
369 }
Mike Lockwoode29db6a2010-03-05 13:45:51 -0500370
Dianne Hackborn7299c412010-03-04 18:41:49 -0800371 final void updateLocked() {
372 long ident = Binder.clearCallingIdentity();
373
374 try {
375 int uiMode = 0;
376 if (mCarModeEnabled) {
377 uiMode = Configuration.UI_MODE_TYPE_CAR;
378 } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
379 uiMode = Configuration.UI_MODE_TYPE_DESK;
380 }
381 if (uiMode != 0) {
382 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
383 updateTwilightLocked();
384 uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
385 : Configuration.UI_MODE_NIGHT_NO;
386 } else {
387 uiMode |= mNightMode << 4;
388 }
389 } else {
390 // Disabling the car mode clears the night mode.
391 uiMode = Configuration.UI_MODE_TYPE_NORMAL |
392 Configuration.UI_MODE_NIGHT_NO;
393 }
394
395 if (uiMode != mCurUiMode) {
396 mCurUiMode = uiMode;
397
398 try {
399 final IActivityManager am = ActivityManagerNative.getDefault();
400 mConfiguration.uiMode = uiMode;
401 am.updateConfiguration(mConfiguration);
402 } catch (RemoteException e) {
403 Slog.w(TAG, "Failure communicating with activity manager", e);
404 }
405 }
406
407 String action = null;
408 String oldAction = null;
409 if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
410 oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
411 } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) {
412 oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
413 }
414
415 if (mCarModeEnabled) {
416 if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
417 adjustStatusBarCarModeLocked();
418
419 if (oldAction != null) {
420 mContext.sendBroadcast(new Intent(oldAction));
421 }
422 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
423 action = UiModeManager.ACTION_ENTER_CAR_MODE;
424 }
425 } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
426 if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_DESK) {
427 if (oldAction != null) {
428 mContext.sendBroadcast(new Intent(oldAction));
429 }
430 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_DESK;
431 action = UiModeManager.ACTION_ENTER_DESK_MODE;
432 }
433 } else {
434 if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
435 adjustStatusBarCarModeLocked();
436 }
437
438 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
439 action = oldAction;
440 }
441
442 if (action != null) {
443 // Send the ordered broadcast; the result receiver will receive after all
444 // broadcasts have been sent. If any broadcast receiver changes the result
445 // code from the initial value of RESULT_OK, then the result receiver will
446 // not launch the corresponding dock application. This gives apps a chance
447 // to override the behavior and stay in their app even when the device is
448 // placed into a dock.
449 mContext.sendOrderedBroadcast(new Intent(action), null,
450 mResultReceiver, null, Activity.RESULT_OK, null, null);
451 }
Mike Lockwoode29db6a2010-03-05 13:45:51 -0500452
453 // keep screen on when charging and in car mode
454 boolean keepScreenOn = mCharging &&
455 ((mCarModeEnabled && mCarModeKeepsScreenOn) ||
456 (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
457 if (keepScreenOn != mWakeLock.isHeld()) {
458 if (keepScreenOn) {
459 mWakeLock.acquire();
460 } else {
461 mWakeLock.release();
462 }
463 }
Dianne Hackborn7299c412010-03-04 18:41:49 -0800464 } finally {
465 Binder.restoreCallingIdentity(ident);
466 }
467 }
468
469 private void adjustStatusBarCarModeLocked() {
470 if (mStatusBarManager == null) {
471 mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
472 }
473
474 // Fear not: StatusBarService manages a list of requests to disable
475 // features of the status bar; these are ORed together to form the
476 // active disabled list. So if (for example) the device is locked and
477 // the status bar should be totally disabled, the calls below will
478 // have no effect until the device is unlocked.
479 if (mStatusBarManager != null) {
480 mStatusBarManager.disable(mCarModeEnabled
481 ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
482 : StatusBarManager.DISABLE_NONE);
483 }
484
485 if (mNotificationManager == null) {
486 mNotificationManager = (NotificationManager)
487 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
488 }
489
490 if (mNotificationManager != null) {
491 if (mCarModeEnabled) {
492 Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
493
494 Notification n = new Notification();
495 n.icon = R.drawable.stat_notify_car_mode;
496 n.defaults = Notification.DEFAULT_LIGHTS;
497 n.flags = Notification.FLAG_ONGOING_EVENT;
498 n.when = 0;
499 n.setLatestEventInfo(
500 mContext,
501 mContext.getString(R.string.car_mode_disable_notification_title),
502 mContext.getString(R.string.car_mode_disable_notification_message),
503 PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
504 mNotificationManager.notify(0, n);
505 } else {
506 mNotificationManager.cancel(0);
507 }
508 }
509 }
510
511 private final Handler mHandler = new Handler() {
512
513 boolean mPassiveListenerEnabled;
514 boolean mNetworkListenerEnabled;
515
516 @Override
517 public void handleMessage(Message msg) {
518 switch (msg.what) {
519 case MSG_UPDATE_TWILIGHT:
520 synchronized (mLock) {
521 if (isDoingNightMode() && mLocation != null
522 && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
523 updateTwilightLocked();
524 updateLocked();
525 }
526 }
527 break;
528 case MSG_ENABLE_LOCATION_UPDATES:
529 // enable network provider to receive at least location updates for a given
530 // distance.
531 boolean networkLocationEnabled;
532 try {
533 networkLocationEnabled =
534 mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
535 } catch (Exception e) {
536 // we may get IllegalArgumentException if network location provider
537 // does not exist or is not yet installed.
538 networkLocationEnabled = false;
539 }
540 if (!mNetworkListenerEnabled && networkLocationEnabled) {
541 mNetworkListenerEnabled = true;
542 mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
543 LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
544
545 if (mLocation == null) {
546 retrieveLocation();
547 }
548 synchronized (mLock) {
549 if (isDoingNightMode() && mLocation != null
550 && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
551 updateTwilightLocked();
552 updateLocked();
553 }
554 }
555 }
556 // enable passive provider to receive updates from location fixes (gps
557 // and network).
558 boolean passiveLocationEnabled;
559 try {
560 passiveLocationEnabled =
561 mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
562 } catch (Exception e) {
563 // we may get IllegalArgumentException if passive location provider
564 // does not exist or is not yet installed.
565 passiveLocationEnabled = false;
566 }
567 if (!mPassiveListenerEnabled && passiveLocationEnabled) {
568 mPassiveListenerEnabled = true;
569 mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
570 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
571 }
572 if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
573 long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
574 interval *= 1.5;
575 if (interval == 0) {
576 interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
577 } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
578 interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
579 }
580 Bundle bundle = new Bundle();
581 bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
582 Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
583 newMsg.setData(bundle);
584 mHandler.sendMessageDelayed(newMsg, interval);
585 }
586 break;
587 }
588 }
589
590 private void retrieveLocation() {
591 Location location;
592 Criteria criteria = new Criteria();
593 criteria.setSpeedRequired(false);
594 criteria.setAltitudeRequired(false);
595 criteria.setBearingRequired(false);
596 criteria.setAccuracy(Criteria.ACCURACY_FINE);
597 final String bestProvider = mLocationManager.getBestProvider(criteria, true);
598 location = mLocationManager.getLastKnownLocation(bestProvider);
599 // In the case there is no location available (e.g. GPS fix or network location
600 // is not available yet), the longitude of the location is estimated using the timezone,
601 // latitude and accuracy are set to get a good average.
602 if (location == null) {
603 Time currentTime = new Time();
604 currentTime.set(System.currentTimeMillis());
605 double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
606 (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
607 location = new Location("fake");
608 location.setLongitude(lngOffset);
609 location.setLatitude(0);
610 location.setAccuracy(417000.0f);
611 location.setTime(System.currentTimeMillis());
612 }
613 synchronized (mLock) {
614 mLocation = location;
615 }
616 }
617 };
618
619 void updateTwilightLocked() {
620 if (mLocation == null) {
621 return;
622 }
623 final long currentTime = System.currentTimeMillis();
624 boolean nightMode;
625 // calculate current twilight
626 TwilightCalculator tw = new TwilightCalculator();
627 tw.calculateTwilight(currentTime,
628 mLocation.getLatitude(), mLocation.getLongitude());
629 if (tw.mState == TwilightCalculator.DAY) {
630 nightMode = false;
631 } else {
632 nightMode = true;
633 }
634
635 // schedule next update
636 long nextUpdate = 0;
637 if (tw.mSunrise == -1 || tw.mSunset == -1) {
638 // In the case the day or night never ends the update is scheduled 12 hours later.
639 nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
640 } else {
641 final int mLastTwilightState = tw.mState;
642 // add some extra time to be on the save side.
643 nextUpdate += DateUtils.MINUTE_IN_MILLIS;
644 if (currentTime > tw.mSunset) {
645 // next update should be on the following day
646 tw.calculateTwilight(currentTime
647 + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
648 mLocation.getLongitude());
649 }
650
651 if (mLastTwilightState == TwilightCalculator.NIGHT) {
652 nextUpdate += tw.mSunrise;
653 } else {
654 nextUpdate += tw.mSunset;
655 }
656 }
657
658 Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
659 PendingIntent pendingIntent =
660 PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
661 mAlarmManager.cancel(pendingIntent);
662 mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
663
664 mComputedNightMode = nightMode;
665 }
666
667 @Override
668 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
669 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
670 != PackageManager.PERMISSION_GRANTED) {
671
672 pw.println("Permission Denial: can't dump uimode service from from pid="
673 + Binder.getCallingPid()
674 + ", uid=" + Binder.getCallingUid());
675 return;
676 }
677
678 synchronized (mLock) {
679 pw.println("Current UI Mode Service state:");
680 pw.print(" mDockState="); pw.print(mDockState);
681 pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
682 pw.print(" mNightMode="); pw.print(mNightMode);
683 pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
684 pw.print(" mComputedNightMode="); pw.println(mComputedNightMode);
685 pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
686 pw.print(" mSystemReady="); pw.println(mSystemReady);
687 if (mLocation != null) {
688 pw.print(" mLocation="); pw.println(mLocation);
689 }
690 }
691 }
692}