com.cyanogenmod.settings.device.LtoDownloadService.java Source code

Java tutorial

Introduction

Here is the source code for com.cyanogenmod.settings.device.LtoDownloadService.java

Source

/*
 * Copyright (C) 2013 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.cyanogenmod.settings.device;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
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.preference.PreferenceManager;
import android.util.Log;

import com.cyanogenmod.settings.device.R;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

public class LtoDownloadService extends Service {
    private static final String TAG = "LtoDownloadService";
    private static final boolean ALOGV = true;

    private static final String LTO_SOURCE_URI_PATTERN = "http://gllto.glpals.com/%s/v2/latest/lto2.dat";
    private static final File LTO_DESTINATION_FILE = new File("/data/gps/lto.dat");

    public static final String KEY_ENABLED = "lto_download_enabled";
    public static final String KEY_FILE_TYPE = "lto_download_file";
    public static final String KEY_INTERVAL = "lto_download_interval";
    public static final String KEY_WIFI_ONLY = "lto_download_wifi_only";
    public static final String KEY_LAST_DOWNLOAD = "last_lto_download";

    public static final String EXTRA_FORCE_DOWNLOAD = "force_download";

    public static final String ACTION_STATE_CHANGE = "com.cyanogenmod.settings.device.LtoDownloadService.STATE_CHANGE";
    public static final String EXTRA_STATE = "state";
    public static final String EXTRA_SUCCESS = "success";
    public static final String EXTRA_PROGRESS = "progress";
    public static final String EXTRA_TIMESTAMP = "timestamp";
    public static final int STATE_IDLE = 0;
    public static final int STATE_DOWNLOADING = 1;

    private static final String FILE_TYPE_DEFAULT = "7day";
    private static final boolean WIFI_ONLY_DEFAULT = true;

    private static final int DOWNLOAD_TIMEOUT = 20000; /* 20 seconds */

    private LtoDownloadTask mTask;

    private NotificationManager mNotificationManager;
    private static final int NOTIFICATION_ID = R.string.lto_downloading_data_notification;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        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;
        }

        boolean forceDownload = intent.getBooleanExtra(EXTRA_FORCE_DOWNLOAD, false);
        if (!shouldDownload(forceDownload)) {
            Log.d(TAG, "Service started, but shouldn't download ... stopping");
            stopSelf();
            return START_NOT_STICKY;
        }

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        String type = prefs.getString(KEY_FILE_TYPE, FILE_TYPE_DEFAULT);
        String uri = String.format(LTO_SOURCE_URI_PATTERN, type);

        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        mTask = new LtoDownloadTask(uri, LTO_DESTINATION_FILE);
        mTask.execute();

        return START_REDELIVER_INTENT;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED) {
            mTask.cancel(true);
            mTask = null;
        }
    }

    private boolean shouldDownload(boolean forceDownload) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        boolean hasConnection = false;

        if (info == null || !info.isConnected()) {
            if (ALOGV)
                Log.v(TAG, "No network connection is available for LTO download");
        } else if (forceDownload) {
            if (ALOGV)
                Log.v(TAG, "Download was forced, overriding network type check");
            hasConnection = true;
        } else {
            boolean wifiOnly = prefs.getBoolean(KEY_WIFI_ONLY, WIFI_ONLY_DEFAULT);
            if (wifiOnly && info.getType() != ConnectivityManager.TYPE_WIFI) {
                if (ALOGV) {
                    Log.v(TAG, "Active network is of type " + info.getTypeName() + ", but Wifi only was selected");
                }
            } else {
                hasConnection = true;
            }
        }

        if (!hasConnection) {
            return false;
        }
        if (forceDownload) {
            return true;
        }
        if (!prefs.getBoolean(KEY_ENABLED, false)) {
            return false;
        }

        long now = System.currentTimeMillis();
        long lastDownload = LtoDownloadUtils.getLastDownload(this);
        long due = lastDownload + LtoDownloadUtils.getDownloadInterval(this);

        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();
            reportStateChange(STATE_DOWNLOADING, null, null);
            showDownloadNotification(0, true);
        }

        @Override
        protected Integer doInBackground(Void... params) {
            BufferedInputStream in = null;
            BufferedOutputStream out = null;
            int result = RESULT_SUCCESS;

            try {
                HttpParams httpParams = new BasicHttpParams();
                HttpConnectionParams.setConnectionTimeout(httpParams, DOWNLOAD_TIMEOUT);
                HttpConnectionParams.setSoTimeout(httpParams, DOWNLOAD_TIMEOUT);

                HttpClient client = new DefaultHttpClient(httpParams);
                HttpGet request = new HttpGet();
                request.setURI(new URI(mSource));

                HttpResponse response = client.execute(request);
                HttpEntity entity = response.getEntity();
                File outputFile = mTempFile != null ? mTempFile : mDestination;

                in = new BufferedInputStream(entity.getContent());
                out = new BufferedOutputStream(new FileOutputStream(outputFile));

                byte[] buffer = new byte[2048];
                int count, total = 0;
                long length = entity.getContentLength();

                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);
                    }
                }

                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 (IOException e) {
                Log.w(TAG, "Failed downloading LTO data", e);
                result = RESULT_FAILURE;
            } catch (URISyntaxException e) {
                Log.e(TAG, "URI syntax wrong", 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 onProgressUpdate(Integer... progress) {
            reportStateChange(STATE_DOWNLOADING, null, progress[0]);
            showDownloadNotification(progress[0].intValue(), false);
        }

        @Override
        protected void onPostExecute(Integer result) {
            cancelDownloadNotificacion();
            if (result != null) {
                finish(result);
            }
        }

        @Override
        protected void onCancelled() {
            cancelDownloadNotificacion();
            finish(RESULT_CANCELLED);
        }

        private void finish(int result) {
            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);
            }

            Context context = LtoDownloadService.this;

            if (result == RESULT_SUCCESS) {
                long now = System.currentTimeMillis();
                SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();

                editor.putLong(KEY_LAST_DOWNLOAD, now);
                editor.apply();

                LtoDownloadUtils.scheduleNextDownload(context, now);
            } else if (result == RESULT_FAILURE) {
                /* failure, schedule next download in 1 hour */
                long lastDownload = LtoDownloadUtils.getLastDownload(context);
                lastDownload += 60 * 60 * 1000;
                LtoDownloadUtils.scheduleNextDownload(context, lastDownload);
            } else {
                /* cancelled, likely due to lost network - we'll get restarted
                 * when network comes back */
            }

            reportStateChange(STATE_IDLE, result == RESULT_SUCCESS, null);
            mWakeLock.release();
            stopSelf();
        }
    }

    private void reportStateChange(int state, Boolean success, Integer progress) {
        Intent intent = new Intent(ACTION_STATE_CHANGE);
        intent.putExtra(EXTRA_STATE, state);
        if (success != null) {
            intent.putExtra(EXTRA_SUCCESS, success);
        }
        if (progress != null) {
            intent.putExtra(EXTRA_PROGRESS, progress);
        }
        intent.putExtra(EXTRA_TIMESTAMP, new Date().getTime());
        sendStickyBroadcast(intent);
    }

    private void showDownloadNotification(int progress, boolean indeterminate) {
        // Create a notification
        Bitmap largeIcon = (((BitmapDrawable) getResources()
                .getDrawable(com.cyanogenmod.settings.device.R.drawable.stat_sys_download)).getBitmap());
        String title = getString(R.string.lto_downloading_data_notification);
        Notification.Builder builder = new Notification.Builder(this).setContentTitle(title)
                .setSmallIcon(android.R.drawable.stat_sys_download).setLargeIcon(largeIcon)
                .setProgress(100, progress, indeterminate).setWhen(0);
        Notification notification = builder.build();
        notification.tickerText = title;
        notification.flags = Notification.FLAG_NO_CLEAR;
        mNotificationManager.notify(NOTIFICATION_ID, notification);
    }

    private void cancelDownloadNotificacion() {
        mNotificationManager.cancel(NOTIFICATION_ID);
    }
}