diff options
Diffstat (limited to 'src/com/android/settings/cyanogenmod/LtoService.java')
-rw-r--r-- | src/com/android/settings/cyanogenmod/LtoService.java | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/src/com/android/settings/cyanogenmod/LtoService.java b/src/com/android/settings/cyanogenmod/LtoService.java new file mode 100644 index 0000000..8bbf8c8 --- /dev/null +++ b/src/com/android/settings/cyanogenmod/LtoService.java @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.settings.cyanogenmod; + +import static android.hardware.CmHardwareManager.FEATURE_LONG_TERM_ORBITS; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.hardware.CmHardwareManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.os.UserHandle; +import android.preference.PreferenceManager; +import android.util.Log; + +import com.android.settings.location.LocationSettings; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Date; + +public class LtoService extends Service { + private static final String TAG = "LtoService"; + private static final boolean ALOGV = true; + + private static final String KEY_LAST_DOWNLOAD = "lto_last_download"; + + public static final String ACTION_NEW_GPS_DATA = "com.cyanogenmod.actions.NEW_GPS_DATA"; + + private static final int DOWNLOAD_TIMEOUT = 45000; /* 45 seconds */ + + private CmHardwareManager mCmHardwareManager; + private LtoDownloadTask mTask; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (mCmHardwareManager == null || + !mCmHardwareManager.isSupported(FEATURE_LONG_TERM_ORBITS)) { + if (ALOGV) Log.v(TAG, "LTO is not supported by this device"); + return START_NOT_STICKY; + } + if (!LocationSettings.isLocationModeEnabled(this)) { + if (ALOGV) Log.v(TAG, "Location mode not enabled in this device"); + return START_NOT_STICKY; + } + + if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED) { + if (ALOGV) Log.v(TAG, "LTO download is still active, not starting new download"); + return START_REDELIVER_INTENT; + } + + if (!shouldDownload()) { + Log.d(TAG, "Service started, but shouldn't download ... stopping"); + stopSelf(); + return START_NOT_STICKY; + } + + mTask = new LtoDownloadTask(mCmHardwareManager.getLtoSource(), + new File(mCmHardwareManager.getLtoDestination())); + mTask.execute(); + + return START_REDELIVER_INTENT; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + mCmHardwareManager = (CmHardwareManager) getSystemService(Context.CMHW_SERVICE); + } + + @Override + public void onDestroy() { + if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED) { + mTask.cancel(true); + mTask = null; + } + } + + private boolean shouldDownload() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); + NetworkInfo info = cm.getActiveNetworkInfo(); + + if (info == null || !info.isConnected()) { + if (ALOGV) Log.v(TAG, "No network connection is available for LTO download"); + } else { + boolean wifiOnly = prefs.getBoolean( + LocationSettings.KEY_LTO_DOWNLOAD_DATA_WIFI_ONLY, false); + if (wifiOnly && info.getType() != ConnectivityManager.TYPE_WIFI) { + if (ALOGV) { + Log.v(TAG, "Active network is of type " + + info.getTypeName() + ", but Wifi only was selected"); + } + return false; + } + } + + long now = System.currentTimeMillis(); + long lastDownload = getLastDownload(); + long due = lastDownload + mCmHardwareManager.getLtoDownloadInterval(); + + if (ALOGV) { + Log.v(TAG, "Now " + now + " due " + due + "(" + new Date(due) + ")"); + } + + if (lastDownload != 0 && now < due) { + if (ALOGV) Log.v(TAG, "LTO download is not due yet"); + return false; + } + + return true; + } + + private class LtoDownloadTask extends AsyncTask<Void, Integer, Integer> { + private String mSource; + private File mDestination; + private File mTempFile; + private WakeLock mWakeLock; + + private static final int RESULT_SUCCESS = 0; + private static final int RESULT_FAILURE = 1; + private static final int RESULT_CANCELLED = 2; + + public LtoDownloadTask(String source, File destination) { + mSource = source; + mDestination = destination; + try { + mTempFile = File.createTempFile("lto-download", null, getCacheDir()); + } catch (IOException e) { + Log.w(TAG, "Could not create temporary file", e); + } + + PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + } + + @Override + protected void onPreExecute() { + mWakeLock.acquire(); + } + + @Override + protected Integer doInBackground(Void... params) { + BufferedInputStream in = null; + BufferedOutputStream out = null; + int result = RESULT_SUCCESS; + + try { + final URLConnection connection = new URL(mSource).openConnection(); + connection.setRequestProperty("Connection", "close"); + connection.setConnectTimeout(DOWNLOAD_TIMEOUT); + connection.setReadTimeout(DOWNLOAD_TIMEOUT); + final Thread interruptThread = new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(DOWNLOAD_TIMEOUT); + ((HttpURLConnection)connection).disconnect(); + } catch (InterruptedException e) { + } + } + }); + interruptThread.start(); + + File outputFile = mTempFile != null ? mTempFile : mDestination; + + in = new BufferedInputStream(connection.getInputStream()); + out = new BufferedOutputStream(new FileOutputStream(outputFile)); + + byte[] buffer = new byte[2048]; + int count, total = 0; + long length = 0; + try { + String value = connection.getHeaderField("Content-Length"); + if (value != null) { + length = Long.parseLong(value); + } + } catch (NumberFormatException ex) { + // Ignore + } + + // Read all the buffer + while ((count = in.read(buffer, 0, buffer.length)) != -1) { + if (isCancelled()) { + result = RESULT_CANCELLED; + break; + } + out.write(buffer, 0, count); + total += count; + + if (length > 0) { + float progress = (float) total * 100 / length; + publishProgress((int) progress); + } + } + + // The file is currently downloaded. Interrupt the timeout thread + interruptThread.interrupt(); + + Log.d(TAG, "Downloaded " + total + "/" + length + " bytes of LTO data"); + if (total == 0 || (length > 0 && total != length)) { + result = RESULT_FAILURE; + } + in.close(); + out.close(); + } catch (MalformedURLException e) { + Log.e(TAG, "URI syntax wrong", e); + result = RESULT_FAILURE; + } catch (IOException e) { + Log.e(TAG, "Failed downloading LTO data", e); + result = RESULT_FAILURE; + } finally { + try { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + Log.d(TAG, "return " + result); + return result; + } + + @Override + protected void onPostExecute(Integer result) { + if (result != null) { + finish(result); + } + } + + @Override + protected void onCancelled() { + finish(RESULT_CANCELLED); + } + + private void finish(int result) { + final Context context = LtoService.this; + + if (mTempFile != null) { + if (result == RESULT_SUCCESS) { + mDestination.delete(); + if (!mTempFile.renameTo(mDestination)) { + Log.w(TAG, "Could not move temporary file to destination"); + } else { + mDestination.setReadable(true, false); + } + } + mTempFile.delete(); + } else if (result != RESULT_SUCCESS) { + mDestination.delete(); + } else { + mDestination.setReadable(true, false); + } + + if (result == RESULT_SUCCESS) { + long now = System.currentTimeMillis(); + SharedPreferences.Editor editor = + PreferenceManager.getDefaultSharedPreferences(context).edit(); + editor.putLong(KEY_LAST_DOWNLOAD, now); + editor.apply(); + scheduleNextDownload(now); + notifyNewGpsData(); + + } else if (result == RESULT_FAILURE) { + /* failure, schedule next download in 1 hour */ + long lastDownload = getLastDownload() + (60 * 60 * 1000); + scheduleNextDownload(lastDownload); + } else { + /* cancelled, likely due to lost network - we'll get restarted + * when network comes back */ + } + + mWakeLock.release(); + stopSelf(); + } + } + + private void notifyNewGpsData() { + Intent intent = new Intent(ACTION_NEW_GPS_DATA); + sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + private PendingIntent scheduleNextDownload(long lastDownload) { + AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(this, LtoService.class); + PendingIntent pi = PendingIntent.getService(this, 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT); + + long nextLtoDownload = lastDownload + mCmHardwareManager.getLtoDownloadInterval(); + am.set(AlarmManager.RTC, nextLtoDownload, pi); + return pi; + } + + private long getLastDownload() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + return prefs.getLong(LtoService.KEY_LAST_DOWNLOAD, 0); + } +} |