diff options
Diffstat (limited to 'services/java/com/android/server/TwilightService.java')
-rw-r--r-- | services/java/com/android/server/TwilightService.java | 572 |
1 files changed, 0 insertions, 572 deletions
diff --git a/services/java/com/android/server/TwilightService.java b/services/java/com/android/server/TwilightService.java deleted file mode 100644 index 0356faa..0000000 --- a/services/java/com/android/server/TwilightService.java +++ /dev/null @@ -1,572 +0,0 @@ -/* - * 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 = false; - - 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.getElapsedRealtimeNanos() < from.getElapsedRealtimeNanos()) { - 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.getElapsedRealtimeNanos() < - lastKnownLocation.getElapsedRealtimeNanos())) { - 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.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); - - if (DEBUG) { - Slog.d(TAG, "Estimated location from timezone: " + location); - } - } - - setLocation(location); - } - - private void setLocation(Location location) { - mLocation = location; - updateTwilightState(); - } - - private void updateTwilightState() { - if (mLocation == null) { - setTwilightState(null); - return; - } - - 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.setExact(AlarmManager.RTC, 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) { - } - }; -} |