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