am 2416e096: Extract the twilight detection into its own service.

* commit '2416e09649eb6ab767eba458796e126196c77a34':
  Extract the twilight detection into its own service.
This commit is contained in:
Jeff Brown
2012-08-22 00:09:12 -07:00
committed by Android Git Automerger
3 changed files with 607 additions and 321 deletions

View File

@@ -139,6 +139,7 @@ class ServerThread extends Thread {
DockObserver dock = null;
UsbService usb = null;
SerialService serial = null;
TwilightService twilight = null;
UiModeManagerService uiMode = null;
RecognitionManagerService recognition = null;
ThrottleService throttle = null;
@@ -585,10 +586,17 @@ class ServerThread extends Thread {
Slog.e(TAG, "Failure starting SerialService", e);
}
try {
Slog.i(TAG, "Twilight Service");
twilight = new TwilightService(context);
} catch (Throwable e) {
reportWtf("starting TwilightService", e);
}
try {
Slog.i(TAG, "UI Mode Manager Service");
// Listen for UI mode changes
uiMode = new UiModeManagerService(context);
uiMode = new UiModeManagerService(context, twilight);
} catch (Throwable e) {
reportWtf("starting UiModeManagerService", e);
}
@@ -750,6 +758,7 @@ class ServerThread extends Thread {
final DockObserver dockF = dock;
final UsbService usbF = usb;
final ThrottleService throttleF = throttle;
final TwilightService twilightF = twilight;
final UiModeManagerService uiModeF = uiMode;
final AppWidgetService appWidgetF = appWidget;
final WallpaperManagerService wallpaperF = wallpaper;
@@ -809,6 +818,11 @@ class ServerThread extends Thread {
} catch (Throwable e) {
reportWtf("making USB Service ready", e);
}
try {
if (twilightF != null) twilightF.systemReady();
} catch (Throwable e) {
reportWtf("makin Twilight Service ready", e);
}
try {
if (uiModeF != null) uiModeF.systemReady();
} catch (Throwable e) {

View File

@@ -0,0 +1,567 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Slog;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import libcore.util.Objects;
/**
* Figures out whether it's twilight time based on the user's location.
*
* Used by the UI mode manager and other components to adjust night mode
* effects based on sunrise and sunset.
*/
public final class TwilightService {
private static final String TAG = "TwilightService";
private static final boolean DEBUG = true;
private static final String ACTION_UPDATE_TWILIGHT_STATE =
"com.android.server.action.UPDATE_TWILIGHT_STATE";
private final Context mContext;
private final AlarmManager mAlarmManager;
private final LocationManager mLocationManager;
private final LocationHandler mLocationHandler;
private final Object mLock = new Object();
private final ArrayList<TwilightListenerRecord> mListeners =
new ArrayList<TwilightListenerRecord>();
private boolean mSystemReady;
private TwilightState mTwilightState;
public TwilightService(Context context) {
mContext = context;
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
mLocationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
mLocationHandler = new LocationHandler();
}
void systemReady() {
synchronized (mLock) {
mSystemReady = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(ACTION_UPDATE_TWILIGHT_STATE);
mContext.registerReceiver(mUpdateLocationReceiver, filter);
if (!mListeners.isEmpty()) {
mLocationHandler.enableLocationUpdates();
}
}
}
/**
* Gets the current twilight state.
*
* @return The current twilight state, or null if no information is available.
*/
public TwilightState getCurrentState() {
synchronized (mLock) {
return mTwilightState;
}
}
/**
* Listens for twilight time.
*
* @param listener The listener.
* @param handler The handler on which to post calls into the listener.
*/
public void registerListener(TwilightListener listener, Handler handler) {
synchronized (mLock) {
mListeners.add(new TwilightListenerRecord(listener, handler));
if (mSystemReady && mListeners.size() == 1) {
mLocationHandler.enableLocationUpdates();
}
}
}
private void setTwilightState(TwilightState state) {
synchronized (mLock) {
if (!Objects.equal(mTwilightState, state)) {
if (DEBUG) {
Slog.d(TAG, "Twilight state changed: " + state);
}
mTwilightState = state;
int count = mListeners.size();
for (int i = 0; i < count; i++) {
mListeners.get(i).post();
}
}
}
}
// The user has moved if the accuracy circles of the two locations don't overlap.
private static boolean hasMoved(Location from, Location to) {
if (to == null) {
return false;
}
if (from == null) {
return true;
}
// if new location is older than the current one, the device hasn't moved.
if (to.getElapsedRealtimeNano() < from.getElapsedRealtimeNano()) {
return false;
}
// Get the distance between the two points.
float distance = from.distanceTo(to);
// Get the total accuracy radius for both locations.
float totalAccuracy = from.getAccuracy() + to.getAccuracy();
// If the distance is greater than the combined accuracy of the two
// points then they can't overlap and hence the user has moved.
return distance >= totalAccuracy;
}
/**
* Describes whether it is day or night.
* This object is immutable.
*/
public static final class TwilightState {
private final boolean mIsNight;
private final long mYesterdaySunset;
private final long mTodaySunrise;
private final long mTodaySunset;
private final long mTomorrowSunrise;
TwilightState(boolean isNight,
long yesterdaySunset,
long todaySunrise, long todaySunset,
long tomorrowSunrise) {
mIsNight = isNight;
mYesterdaySunset = yesterdaySunset;
mTodaySunrise = todaySunrise;
mTodaySunset = todaySunset;
mTomorrowSunrise = tomorrowSunrise;
}
/**
* Returns true if it is currently night time.
*/
public boolean isNight() {
return mIsNight;
}
/**
* Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase,
* or -1 if the sun never sets.
*/
public long getYesterdaySunset() {
return mYesterdaySunset;
}
/**
* Returns the time of today's sunrise in the System.currentTimeMillis() timebase,
* or -1 if the sun never rises.
*/
public long getTodaySunrise() {
return mTodaySunrise;
}
/**
* Returns the time of today's sunset in the System.currentTimeMillis() timebase,
* or -1 if the sun never sets.
*/
public long getTodaySunset() {
return mTodaySunset;
}
/**
* Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase,
* or -1 if the sun never rises.
*/
public long getTomorrowSunrise() {
return mTomorrowSunrise;
}
@Override
public boolean equals(Object o) {
return o instanceof TwilightState && equals((TwilightState)o);
}
public boolean equals(TwilightState other) {
return other != null
&& mIsNight == other.mIsNight
&& mYesterdaySunset == other.mYesterdaySunset
&& mTodaySunrise == other.mTodaySunrise
&& mTodaySunset == other.mTodaySunset
&& mTomorrowSunrise == other.mTomorrowSunrise;
}
@Override
public int hashCode() {
return 0; // don't care
}
@Override
public String toString() {
DateFormat f = DateFormat.getDateTimeInstance();
return "{TwilightState: isNight=" + mIsNight
+ ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset))
+ ", mTodaySunrise=" + f.format(new Date(mTodaySunrise))
+ ", mTodaySunset=" + f.format(new Date(mTodaySunset))
+ ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise))
+ "}";
}
}
/**
* Listener for changes in twilight state.
*/
public interface TwilightListener {
public void onTwilightStateChanged();
}
private static final class TwilightListenerRecord implements Runnable {
private final TwilightListener mListener;
private final Handler mHandler;
public TwilightListenerRecord(TwilightListener listener, Handler handler) {
mListener = listener;
mHandler = handler;
}
public void post() {
mHandler.post(this);
}
@Override
public void run() {
mListener.onTwilightStateChanged();
}
}
private final class LocationHandler extends Handler {
private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
private static final int MSG_PROCESS_NEW_LOCATION = 3;
private static final int MSG_DO_TWILIGHT_UPDATE = 4;
private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX =
15 * DateUtils.MINUTE_IN_MILLIS;
private static final double FACTOR_GMT_OFFSET_LONGITUDE =
1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
private boolean mPassiveListenerEnabled;
private boolean mNetworkListenerEnabled;
private boolean mDidFirstInit;
private long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
private long mLastUpdateInterval;
private Location mLocation;
private final TwilightCalculator mTwilightCalculator = new TwilightCalculator();
public void processNewLocation(Location location) {
Message msg = obtainMessage(MSG_PROCESS_NEW_LOCATION, location);
sendMessage(msg);
}
public void enableLocationUpdates() {
sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
}
public void requestLocationUpdate() {
sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
}
public void requestTwilightUpdate() {
sendEmptyMessage(MSG_DO_TWILIGHT_UPDATE);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PROCESS_NEW_LOCATION: {
final Location location = (Location)msg.obj;
final boolean hasMoved = hasMoved(mLocation, location);
final boolean hasBetterAccuracy = mLocation == null
|| location.getAccuracy() < mLocation.getAccuracy();
if (DEBUG) {
Slog.d(TAG, "Processing new location: " + location
+ ", hasMoved=" + hasMoved
+ ", hasBetterAccuracy=" + hasBetterAccuracy);
}
if (hasMoved || hasBetterAccuracy) {
setLocation(location);
}
break;
}
case MSG_GET_NEW_LOCATION_UPDATE:
if (!mNetworkListenerEnabled) {
// Don't do anything -- we are still trying to get a
// location.
return;
}
if ((mLastNetworkRegisterTime + MIN_LOCATION_UPDATE_MS) >=
SystemClock.elapsedRealtime()) {
// Don't do anything -- it hasn't been long enough
// since we last requested an update.
return;
}
// Unregister the current location monitor, so we can
// register a new one for it to get an immediate update.
mNetworkListenerEnabled = false;
mLocationManager.removeUpdates(mEmptyLocationListener);
// Fall through to re-register listener.
case MSG_ENABLE_LOCATION_UPDATES:
// enable network provider to receive at least location updates for a given
// distance.
boolean networkLocationEnabled;
try {
networkLocationEnabled =
mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception e) {
// we may get IllegalArgumentException if network location provider
// does not exist or is not yet installed.
networkLocationEnabled = false;
}
if (!mNetworkListenerEnabled && networkLocationEnabled) {
mNetworkListenerEnabled = true;
mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
if (!mDidFirstInit) {
mDidFirstInit = true;
if (mLocation == null) {
retrieveLocation();
}
}
}
// enable passive provider to receive updates from location fixes (gps
// and network).
boolean passiveLocationEnabled;
try {
passiveLocationEnabled =
mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
} catch (Exception e) {
// we may get IllegalArgumentException if passive location provider
// does not exist or is not yet installed.
passiveLocationEnabled = false;
}
if (!mPassiveListenerEnabled && passiveLocationEnabled) {
mPassiveListenerEnabled = true;
mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
}
if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
mLastUpdateInterval *= 1.5;
if (mLastUpdateInterval == 0) {
mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
} else if (mLastUpdateInterval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
}
sendEmptyMessageDelayed(MSG_ENABLE_LOCATION_UPDATES, mLastUpdateInterval);
}
break;
case MSG_DO_TWILIGHT_UPDATE:
updateTwilightState();
break;
}
}
private void retrieveLocation() {
Location location = null;
final Iterator<String> providers =
mLocationManager.getProviders(new Criteria(), true).iterator();
while (providers.hasNext()) {
final Location lastKnownLocation =
mLocationManager.getLastKnownLocation(providers.next());
// pick the most recent location
if (location == null || (lastKnownLocation != null &&
location.getElapsedRealtimeNano() <
lastKnownLocation.getElapsedRealtimeNano())) {
location = lastKnownLocation;
}
}
// In the case there is no location available (e.g. GPS fix or network location
// is not available yet), the longitude of the location is estimated using the timezone,
// latitude and accuracy are set to get a good average.
if (location == null) {
Time currentTime = new Time();
currentTime.set(System.currentTimeMillis());
double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
(currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
location = new Location("fake");
location.setLongitude(lngOffset);
location.setLatitude(0);
location.setAccuracy(417000.0f);
location.setTime(System.currentTimeMillis());
location.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
if (DEBUG) {
Slog.d(TAG, "Estimated location from timezone: " + location);
}
}
setLocation(location);
}
private void setLocation(Location location) {
mLocation = location;
updateTwilightState();
}
private void updateTwilightState() {
final long now = System.currentTimeMillis();
// calculate yesterday's twilight
mTwilightCalculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS,
mLocation.getLatitude(), mLocation.getLongitude());
final long yesterdaySunset = mTwilightCalculator.mSunset;
// calculate today's twilight
mTwilightCalculator.calculateTwilight(now,
mLocation.getLatitude(), mLocation.getLongitude());
final boolean isNight = (mTwilightCalculator.mState == TwilightCalculator.NIGHT);
final long todaySunrise = mTwilightCalculator.mSunrise;
final long todaySunset = mTwilightCalculator.mSunset;
// calculate tomorrow's twilight
mTwilightCalculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS,
mLocation.getLatitude(), mLocation.getLongitude());
final long tomorrowSunrise = mTwilightCalculator.mSunrise;
// set twilight state
TwilightState state = new TwilightState(isNight, yesterdaySunset,
todaySunrise, todaySunset, tomorrowSunrise);
if (DEBUG) {
Slog.d(TAG, "Updating twilight state: " + state);
}
setTwilightState(state);
// schedule next update
long nextUpdate = 0;
if (todaySunrise == -1 || todaySunset == -1) {
// In the case the day or night never ends the update is scheduled 12 hours later.
nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS;
} else {
// add some extra time to be on the safe side.
nextUpdate += DateUtils.MINUTE_IN_MILLIS;
if (now > todaySunset) {
nextUpdate += tomorrowSunrise;
} else if (now > todaySunrise) {
nextUpdate += todaySunset;
} else {
nextUpdate += todaySunrise;
}
}
if (DEBUG) {
Slog.d(TAG, "Next update in " + (nextUpdate - now) + " ms");
}
Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
mAlarmManager.cancel(pendingIntent);
mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
}
};
private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())
&& !intent.getBooleanExtra("state", false)) {
// Airplane mode is now off!
mLocationHandler.requestLocationUpdate();
return;
}
// Time zone has changed or alarm expired.
mLocationHandler.requestTwilightUpdate();
}
};
// A LocationListener to initialize the network location provider. The location updates
// are handled through the passive location provider.
private final LocationListener mEmptyLocationListener = new LocationListener() {
public void onLocationChanged(Location location) {
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
private final LocationListener mLocationListener = new LocationListener() {
public void onLocationChanged(Location location) {
mLocationHandler.processNewLocation(location);
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
}

View File

@@ -18,7 +18,6 @@ package com.android.server;
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.IUiModeManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -32,55 +31,33 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Slog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Iterator;
import com.android.internal.R;
import com.android.internal.app.DisableCarModeActivity;
import com.android.server.TwilightService.TwilightState;
class UiModeManagerService extends IUiModeManager.Stub {
private static final String TAG = UiModeManager.class.getSimpleName();
private static final boolean LOG = false;
private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
// Enable launching of applications when entering the dock.
private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true;
private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
private static final int MSG_UPDATE_TWILIGHT = 0;
private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 15 * DateUtils.MINUTE_IN_MILLIS;
private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
private final Context mContext;
private final TwilightService mTwilightService;
private final Handler mHandler = new Handler();
final Object mLock = new Object();
@@ -106,10 +83,6 @@ class UiModeManagerService extends IUiModeManager.Stub {
private NotificationManager mNotificationManager;
private AlarmManager mAlarmManager;
private LocationManager mLocationManager;
private Location mLocation;
private StatusBarManager mStatusBarManager;
private final PowerManager.WakeLock mWakeLock;
@@ -206,15 +179,6 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
};
private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
}
}
};
private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -236,113 +200,24 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
};
private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
private final TwilightService.TwilightListener mTwilightListener =
new TwilightService.TwilightListener() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
if (!intent.getBooleanExtra("state", false)) {
// Airplane mode is now off!
mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
}
} else {
// Time zone has changed!
mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
}
public void onTwilightStateChanged() {
updateTwilight();
}
};
// A LocationListener to initialize the network location provider. The location updates
// are handled through the passive location provider.
private final LocationListener mEmptyLocationListener = new LocationListener() {
public void onLocationChanged(Location location) {
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
private final LocationListener mLocationListener = new LocationListener() {
public void onLocationChanged(Location location) {
final boolean hasMoved = hasMoved(location);
final boolean hasBetterAccuracy = mLocation == null
|| location.getAccuracy() < mLocation.getAccuracy();
if (hasMoved || hasBetterAccuracy) {
synchronized (mLock) {
mLocation = location;
if (hasMoved && isDoingNightMode()
&& mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
}
}
}
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
/*
* The user has moved if the accuracy circles of the two locations
* don't overlap.
*/
private boolean hasMoved(Location location) {
if (location == null) {
return false;
}
if (mLocation == null) {
return true;
}
/* if new location is older than the current one, the devices hasn't
* moved.
*/
if (location.getElapsedRealtimeNano() < mLocation.getElapsedRealtimeNano()) {
return false;
}
/* Get the distance between the two points */
float distance = mLocation.distanceTo(location);
/* Get the total accuracy radius for both locations */
float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
/* If the distance is greater than the combined accuracy of the two
* points then they can't overlap and hence the user has moved.
*/
return distance >= totalAccuracy;
}
};
public UiModeManagerService(Context context) {
public UiModeManagerService(Context context, TwilightService twilight) {
mContext = context;
mTwilightService = twilight;
ServiceManager.addService(Context.UI_MODE_SERVICE, this);
mAlarmManager =
(AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
mLocationManager =
(LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
mContext.registerReceiver(mTwilightUpdateReceiver,
new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
mContext.registerReceiver(mDockModeReceiver,
new IntentFilter(Intent.ACTION_DOCK_EVENT));
mContext.registerReceiver(mBatteryReceiver,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
mContext.registerReceiver(mUpdateLocationReceiver, filter);
PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
@@ -360,6 +235,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
mNightMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO);
mTwilightService.registerListener(mTwilightListener, mHandler);
}
public void disableCarMode(int flags) {
@@ -419,8 +296,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
synchronized (mLock) {
mSystemReady = true;
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
updateComputedNightModeLocked();
updateLocked(0, 0);
mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
}
}
@@ -467,7 +344,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
if (mCarModeEnabled) {
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
updateTwilightLocked();
updateComputedNightModeLocked();
uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
: Configuration.UI_MODE_NIGHT_NO;
} else {
@@ -651,191 +528,20 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
}
private final Handler mHandler = new Handler() {
boolean mPassiveListenerEnabled;
boolean mNetworkListenerEnabled;
boolean mDidFirstInit;
long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_TWILIGHT:
synchronized (mLock) {
if (isDoingNightMode() && mLocation != null
&& mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
updateTwilightLocked();
updateLocked(0, 0);
}
}
break;
case MSG_GET_NEW_LOCATION_UPDATE:
if (!mNetworkListenerEnabled) {
// Don't do anything -- we are still trying to get a
// location.
return;
}
if ((mLastNetworkRegisterTime+MIN_LOCATION_UPDATE_MS)
>= SystemClock.elapsedRealtime()) {
// Don't do anything -- it hasn't been long enough
// since we last requested an update.
return;
}
// Unregister the current location monitor, so we can
// register a new one for it to get an immediate update.
mNetworkListenerEnabled = false;
mLocationManager.removeUpdates(mEmptyLocationListener);
// Fall through to re-register listener.
case MSG_ENABLE_LOCATION_UPDATES:
// enable network provider to receive at least location updates for a given
// distance.
boolean networkLocationEnabled;
try {
networkLocationEnabled =
mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception e) {
// we may get IllegalArgumentException if network location provider
// does not exist or is not yet installed.
networkLocationEnabled = false;
}
if (!mNetworkListenerEnabled && networkLocationEnabled) {
mNetworkListenerEnabled = true;
mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
if (!mDidFirstInit) {
mDidFirstInit = true;
if (mLocation == null) {
retrieveLocation();
}
synchronized (mLock) {
if (isDoingNightMode() && mLocation != null
&& mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
updateTwilightLocked();
updateLocked(0, 0);
}
}
}
}
// enable passive provider to receive updates from location fixes (gps
// and network).
boolean passiveLocationEnabled;
try {
passiveLocationEnabled =
mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
} catch (Exception e) {
// we may get IllegalArgumentException if passive location provider
// does not exist or is not yet installed.
passiveLocationEnabled = false;
}
if (!mPassiveListenerEnabled && passiveLocationEnabled) {
mPassiveListenerEnabled = true;
mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
}
if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
interval *= 1.5;
if (interval == 0) {
interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
} else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
}
Bundle bundle = new Bundle();
bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
newMsg.setData(bundle);
mHandler.sendMessageDelayed(newMsg, interval);
}
break;
private void updateTwilight() {
synchronized (mLock) {
if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
updateComputedNightModeLocked();
updateLocked(0, 0);
}
}
}
private void retrieveLocation() {
Location location = null;
final Iterator<String> providers =
mLocationManager.getProviders(new Criteria(), true).iterator();
while (providers.hasNext()) {
final Location lastKnownLocation =
mLocationManager.getLastKnownLocation(providers.next());
// pick the most recent location
if (location == null || (lastKnownLocation != null &&
location.getElapsedRealtimeNano() <
lastKnownLocation.getElapsedRealtimeNano())) {
location = lastKnownLocation;
}
}
// In the case there is no location available (e.g. GPS fix or network location
// is not available yet), the longitude of the location is estimated using the timezone,
// latitude and accuracy are set to get a good average.
if (location == null) {
Time currentTime = new Time();
currentTime.set(System.currentTimeMillis());
double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
(currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
location = new Location("fake");
location.setLongitude(lngOffset);
location.setLatitude(0);
location.setAccuracy(417000.0f);
location.setTime(System.currentTimeMillis());
location.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
}
synchronized (mLock) {
mLocation = location;
}
private void updateComputedNightModeLocked() {
TwilightState state = mTwilightService.getCurrentState();
if (state != null) {
mComputedNightMode = state.isNight();
}
};
void updateTwilightLocked() {
if (mLocation == null) {
return;
}
final long currentTime = System.currentTimeMillis();
boolean nightMode;
// calculate current twilight
TwilightCalculator tw = new TwilightCalculator();
tw.calculateTwilight(currentTime,
mLocation.getLatitude(), mLocation.getLongitude());
if (tw.mState == TwilightCalculator.DAY) {
nightMode = false;
} else {
nightMode = true;
}
// schedule next update
long nextUpdate = 0;
if (tw.mSunrise == -1 || tw.mSunset == -1) {
// In the case the day or night never ends the update is scheduled 12 hours later.
nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
} else {
final int mLastTwilightState = tw.mState;
// add some extra time to be on the save side.
nextUpdate += DateUtils.MINUTE_IN_MILLIS;
if (currentTime > tw.mSunset) {
// next update should be on the following day
tw.calculateTwilight(currentTime
+ DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
mLocation.getLongitude());
}
if (mLastTwilightState == TwilightCalculator.NIGHT) {
nextUpdate += tw.mSunrise;
} else {
nextUpdate += tw.mSunset;
}
}
Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
mAlarmManager.cancel(pendingIntent);
mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
mComputedNightMode = nightMode;
}
@Override
@@ -860,9 +566,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration);
pw.print(" mSystemReady="); pw.println(mSystemReady);
if (mLocation != null) {
pw.print(" mLocation="); pw.println(mLocation);
}
pw.print(" mTwilightService.getCurrentState()=");
pw.println(mTwilightService.getCurrentState());
}
}
}