mobile/android/base/updater/UpdateService.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 package org.mozilla.gecko.updater;
     8 import org.mozilla.gecko.AppConstants;
     9 import org.mozilla.gecko.R;
    11 import org.mozilla.apache.commons.codec.binary.Hex;
    13 import org.w3c.dom.Document;
    14 import org.w3c.dom.Node;
    15 import org.w3c.dom.NodeList;
    17 import android.app.AlarmManager;
    18 import android.app.IntentService;
    19 import android.app.Notification;
    20 import android.app.NotificationManager;
    21 import android.app.PendingIntent;
    22 import android.app.Service;
    23 import android.content.Context;
    24 import android.content.Intent;
    25 import android.content.SharedPreferences;
    26 import android.net.ConnectivityManager;
    27 import android.net.NetworkInfo;
    28 import android.net.Uri;
    29 import android.os.Environment;
    30 import android.support.v4.app.NotificationCompat;
    31 import android.support.v4.app.NotificationCompat.Builder;
    32 import android.util.Log;
    34 import java.io.BufferedInputStream;
    35 import java.io.BufferedOutputStream;
    36 import java.io.File;
    37 import java.io.FileInputStream;
    38 import java.io.FileOutputStream;
    39 import java.io.InputStream;
    40 import java.io.OutputStream;
    41 import java.net.Proxy;
    42 import java.net.ProxySelector;
    43 import java.net.URL;
    44 import java.net.URLConnection;
    45 import java.security.MessageDigest;
    46 import java.util.Calendar;
    47 import java.util.GregorianCalendar;
    48 import java.util.List;
    49 import java.util.TimeZone;
    51 import javax.xml.parsers.DocumentBuilder;
    52 import javax.xml.parsers.DocumentBuilderFactory;
    54 public class UpdateService extends IntentService {
    55     private static final int BUFSIZE = 8192;
    56     private static final int NOTIFICATION_ID = 0x3e40ddbd;
    58     private static final String LOGTAG = "UpdateService";
    60     private static final int INTERVAL_LONG = 86400000; // in milliseconds
    61     private static final int INTERVAL_SHORT = 14400000; // again, in milliseconds
    62     private static final int INTERVAL_RETRY = 3600000;
    64     private static final String PREFS_NAME = "UpdateService";
    65     private static final String KEY_LAST_BUILDID = "UpdateService.lastBuildID";
    66     private static final String KEY_LAST_HASH_FUNCTION = "UpdateService.lastHashFunction";
    67     private static final String KEY_LAST_HASH_VALUE = "UpdateService.lastHashValue";
    68     private static final String KEY_LAST_FILE_NAME = "UpdateService.lastFileName";
    69     private static final String KEY_LAST_ATTEMPT_DATE = "UpdateService.lastAttemptDate";
    70     private static final String KEY_AUTODOWNLOAD_POLICY = "UpdateService.autoDownloadPolicy";
    72     private SharedPreferences mPrefs;
    74     private NotificationManager mNotificationManager;
    75     private ConnectivityManager mConnectivityManager;
    76     private Builder mBuilder;
    78     private boolean mDownloading;
    79     private boolean mCancelDownload;
    80     private boolean mApplyImmediately;
    82     public UpdateService() {
    83         super("updater");
    84     }
    86     @Override
    87     public void onCreate () {
    88         super.onCreate();
    90         mPrefs = getSharedPreferences(PREFS_NAME, 0);
    91         mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
    92         mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    93         mCancelDownload = false;
    94     }
    96     @Override
    97     public synchronized int onStartCommand (Intent intent, int flags, int startId) {
    98         // If we are busy doing a download, the new Intent here would normally be queued for
    99         // execution once that is done. In this case, however, we want to flip the boolean
   100         // while that is running, so handle that now.
   101         if (mDownloading && UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) {
   102             Log.i(LOGTAG, "will apply update when download finished");
   104             mApplyImmediately = true;
   105             showDownloadNotification();
   106         } else if (UpdateServiceHelper.ACTION_CANCEL_DOWNLOAD.equals(intent.getAction())) {
   107             mCancelDownload = true;
   108         } else {
   109             super.onStartCommand(intent, flags, startId);
   110         }
   112         return Service.START_REDELIVER_INTENT;
   113     }
   115     @Override
   116     protected void onHandleIntent (Intent intent) {
   117         if (UpdateServiceHelper.ACTION_REGISTER_FOR_UPDATES.equals(intent.getAction())) {
   118             int policy = intent.getIntExtra(UpdateServiceHelper.EXTRA_AUTODOWNLOAD_NAME, -1);
   119             if (policy >= 0) {
   120                 setAutoDownloadPolicy(policy);
   121             }
   123             registerForUpdates(false);
   124         } else if (UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE.equals(intent.getAction())) {
   125             startUpdate(intent.getIntExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, 0));
   126             // Use this instead for forcing a download from about:fennec
   127             // startUpdate(UpdateServiceHelper.FLAG_FORCE_DOWNLOAD | UpdateServiceHelper.FLAG_REINSTALL);
   128         } else if (UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE.equals(intent.getAction())) {
   129             // We always want to do the download and apply it here
   130             mApplyImmediately = true;
   131             startUpdate(UpdateServiceHelper.FLAG_FORCE_DOWNLOAD);
   132         } else if (UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) {
   133             applyUpdate(intent.getStringExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME));
   134         }
   135     }
   137     private static boolean hasFlag(int flags, int flag) {
   138         return (flags & flag) == flag;
   139     }
   141     private void sendCheckUpdateResult(UpdateServiceHelper.CheckUpdateResult result) {
   142         Intent resultIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_UPDATE_RESULT);
   143         resultIntent.putExtra("result", result.toString());
   144         sendBroadcast(resultIntent);
   145     }
   147     private int getUpdateInterval(boolean isRetry) {
   148         int interval;
   149         if (isRetry) {
   150             interval = INTERVAL_RETRY;
   151         } else if (!AppConstants.RELEASE_BUILD) {
   152             interval = INTERVAL_SHORT;
   153         } else {
   154             interval = INTERVAL_LONG;
   155         }
   157         return interval;
   158     }
   160     private void registerForUpdates(boolean isRetry) {
   161         Calendar lastAttempt = getLastAttemptDate();
   162         Calendar now = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
   164         int interval = getUpdateInterval(isRetry);
   166         if (lastAttempt == null || (now.getTimeInMillis() - lastAttempt.getTimeInMillis()) > interval) {
   167             // We've either never attempted an update, or we are passed the desired
   168             // time. Start an update now.
   169             Log.i(LOGTAG, "no update has ever been attempted, checking now");
   170             startUpdate(0);
   171             return;
   172         }
   174         AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
   175         if (manager == null)
   176             return;
   178         PendingIntent pending = PendingIntent.getService(this, 0, new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE, null, this, UpdateService.class), PendingIntent.FLAG_UPDATE_CURRENT);
   179         manager.cancel(pending);
   181         lastAttempt.setTimeInMillis(lastAttempt.getTimeInMillis() + interval);
   182         Log.i(LOGTAG, "next update will be at: " + lastAttempt.getTime());
   184         manager.set(AlarmManager.RTC_WAKEUP, lastAttempt.getTimeInMillis(), pending);
   185     }
   187     private void startUpdate(int flags) {
   188         setLastAttemptDate();
   190         NetworkInfo netInfo = mConnectivityManager.getActiveNetworkInfo();
   191         if (netInfo == null || !netInfo.isConnected()) {
   192             Log.i(LOGTAG, "not connected to the network");
   193             registerForUpdates(true);
   194             sendCheckUpdateResult(UpdateServiceHelper.CheckUpdateResult.NOT_AVAILABLE);
   195             return;
   196         }
   198         registerForUpdates(false);
   200         UpdateInfo info = findUpdate(hasFlag(flags, UpdateServiceHelper.FLAG_REINSTALL));
   201         boolean haveUpdate = (info != null);
   203         if (!haveUpdate) {
   204             Log.i(LOGTAG, "no update available");
   205             sendCheckUpdateResult(UpdateServiceHelper.CheckUpdateResult.NOT_AVAILABLE);
   206             return;
   207         }
   209         Log.i(LOGTAG, "update available, buildID = " + info.buildID);
   211         int connectionType = netInfo.getType();
   212         int autoDownloadPolicy = getAutoDownloadPolicy();
   215         /**
   216          * We only start a download automatically if one of following criteria are met:
   217          *
   218          * - We have a FORCE_DOWNLOAD flag passed in
   219          * - The preference is set to 'always'
   220          * - The preference is set to 'wifi' and we are actually using wifi (or regular ethernet)
   221          */
   222         boolean shouldStartDownload = hasFlag(flags, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD) ||
   223             autoDownloadPolicy == UpdateServiceHelper.AUTODOWNLOAD_ENABLED ||
   224             (autoDownloadPolicy == UpdateServiceHelper.AUTODOWNLOAD_WIFI &&
   225              (connectionType == ConnectivityManager.TYPE_WIFI || connectionType == ConnectivityManager.TYPE_ETHERNET));
   227         if (!shouldStartDownload) {
   228             Log.i(LOGTAG, "not initiating automatic update download due to policy " + autoDownloadPolicy);
   229             sendCheckUpdateResult(UpdateServiceHelper.CheckUpdateResult.AVAILABLE);
   231             // We aren't autodownloading here, so prompt to start the update
   232             Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
   234             Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE);
   235             notificationIntent.setClass(this, UpdateService.class);
   237             PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
   238             notification.flags = Notification.FLAG_AUTO_CANCEL;
   240             notification.setLatestEventInfo(this, getResources().getString(R.string.updater_start_title),
   241                                             getResources().getString(R.string.updater_start_select),
   242                                             contentIntent);
   244             mNotificationManager.notify(NOTIFICATION_ID, notification);
   246             return;
   247         }
   249         File pkg = downloadUpdatePackage(info, hasFlag(flags, UpdateServiceHelper.FLAG_OVERWRITE_EXISTING));
   250         if (pkg == null) {
   251             sendCheckUpdateResult(UpdateServiceHelper.CheckUpdateResult.NOT_AVAILABLE);
   252             return;
   253         }
   255         Log.i(LOGTAG, "have update package at " + pkg);
   257         saveUpdateInfo(info, pkg);
   258         sendCheckUpdateResult(UpdateServiceHelper.CheckUpdateResult.DOWNLOADED);
   260         if (mApplyImmediately) {
   261             applyUpdate(pkg);
   262         } else {
   263             // Prompt to apply the update
   264             Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
   266             Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
   267             notificationIntent.setClass(this, UpdateService.class);
   268             notificationIntent.putExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME, pkg.getAbsolutePath());
   270             PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
   271             notification.flags = Notification.FLAG_AUTO_CANCEL;
   273             notification.setLatestEventInfo(this, getResources().getString(R.string.updater_apply_title),
   274                                             getResources().getString(R.string.updater_apply_select),
   275                                             contentIntent);
   277             mNotificationManager.notify(NOTIFICATION_ID, notification);
   278         }
   279     }
   281     private URLConnection openConnectionWithProxy(URL url) throws java.net.URISyntaxException, java.io.IOException {
   282         Log.i(LOGTAG, "opening connection with url: " + url);
   284         ProxySelector ps = ProxySelector.getDefault();
   285         Proxy proxy = Proxy.NO_PROXY;
   286         if (ps != null) {
   287             List<Proxy> proxies = ps.select(url.toURI());
   288             if (proxies != null && !proxies.isEmpty()) {
   289                 proxy = proxies.get(0);
   290             }
   291         }
   293         return url.openConnection(proxy);
   294     }
   296     private UpdateInfo findUpdate(boolean force) {
   297         try {
   298             URL url = UpdateServiceHelper.getUpdateUrl(this, force);
   300             DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
   301             Document dom = builder.parse(openConnectionWithProxy(url).getInputStream());
   303             NodeList nodes = dom.getElementsByTagName("update");
   304             if (nodes == null || nodes.getLength() == 0)
   305                 return null;
   307             Node updateNode = nodes.item(0);
   308             Node buildIdNode = updateNode.getAttributes().getNamedItem("buildID");
   309             if (buildIdNode == null)
   310                 return null;
   312             nodes = dom.getElementsByTagName("patch");
   313             if (nodes == null || nodes.getLength() == 0)
   314                 return null;
   316             Node patchNode = nodes.item(0);
   317             Node urlNode = patchNode.getAttributes().getNamedItem("URL");
   318             Node hashFunctionNode = patchNode.getAttributes().getNamedItem("hashFunction");
   319             Node hashValueNode = patchNode.getAttributes().getNamedItem("hashValue");
   320             Node sizeNode = patchNode.getAttributes().getNamedItem("size");
   322             if (urlNode == null || hashFunctionNode == null ||
   323                 hashValueNode == null || sizeNode == null) {
   324                 return null;
   325             }
   327             // Fill in UpdateInfo from the XML data
   328             UpdateInfo info = new UpdateInfo();
   329             info.url = new URL(urlNode.getTextContent());
   330             info.buildID = buildIdNode.getTextContent();
   331             info.hashFunction = hashFunctionNode.getTextContent();
   332             info.hashValue = hashValueNode.getTextContent();
   334             try {
   335                 info.size = Integer.parseInt(sizeNode.getTextContent());
   336             } catch (NumberFormatException e) {
   337                 Log.e(LOGTAG, "Failed to find APK size: ", e);
   338                 return null;
   339             }
   341             // Make sure we have all the stuff we need to apply the update
   342             if (!info.isValid()) {
   343                 Log.e(LOGTAG, "missing some required update information, have: " + info);
   344                 return null;
   345             }
   347             return info;
   348         } catch (Exception e) {
   349             Log.e(LOGTAG, "failed to check for update: ", e);
   350             return null;
   351         }
   352     }
   354     private MessageDigest createMessageDigest(String hashFunction) {
   355         String javaHashFunction = null;
   357         if ("sha512".equalsIgnoreCase(hashFunction)) {
   358             javaHashFunction = "SHA-512";
   359         } else {
   360             Log.e(LOGTAG, "Unhandled hash function: " + hashFunction);
   361             return null;
   362         }
   364         try {
   365             return MessageDigest.getInstance(javaHashFunction);
   366         } catch (java.security.NoSuchAlgorithmException e) {
   367             Log.e(LOGTAG, "Couldn't find algorithm " + javaHashFunction, e);
   368             return null;
   369         }
   370     }
   372     private void showDownloadNotification() {
   373         showDownloadNotification(null);
   374     }
   376     private void showDownloadNotification(File downloadFile) {
   378         Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
   379         notificationIntent.setClass(this, UpdateService.class);
   381         Intent cancelIntent = new Intent(UpdateServiceHelper.ACTION_CANCEL_DOWNLOAD);
   382         cancelIntent.setClass(this, UpdateService.class);
   384         if (downloadFile != null)
   385             notificationIntent.putExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME, downloadFile.getAbsolutePath());
   387         PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
   388         PendingIntent deleteIntent = PendingIntent.getService(this, 0, cancelIntent, PendingIntent.FLAG_CANCEL_CURRENT);
   390         mBuilder = new NotificationCompat.Builder(this);
   391         mBuilder.setContentTitle(getResources().getString(R.string.updater_downloading_title))
   392     	    .setContentText(mApplyImmediately ? "" : getResources().getString(R.string.updater_downloading_select))
   393     	    .setSmallIcon(android.R.drawable.stat_sys_download)
   394     	    .setContentIntent(contentIntent)
   395             .setDeleteIntent(deleteIntent);
   397         mBuilder.setProgress(100, 0, true);
   398         mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
   399     }
   401     private void showDownloadFailure() {
   402         Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
   404         Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE);
   405         notificationIntent.setClass(this, UpdateService.class);
   407         PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
   409         notification.setLatestEventInfo(this, getResources().getString(R.string.updater_downloading_title_failed),
   410                                         getResources().getString(R.string.updater_downloading_retry),
   411                                         contentIntent);
   413         mNotificationManager.notify(NOTIFICATION_ID, notification);
   414     }
   416     private File downloadUpdatePackage(UpdateInfo info, boolean overwriteExisting) {
   417         File downloadFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), new File(info.url.getFile()).getName());
   419         if (!overwriteExisting && info.buildID.equals(getLastBuildID()) && downloadFile.exists()) {
   420             // The last saved buildID is the same as the one for the current update. We also have a file
   421             // already downloaded, so it's probably the package we want. Verify it to be sure and just
   422             // return that if it matches.
   424             if (verifyDownloadedPackage(downloadFile)) {
   425                 Log.i(LOGTAG, "using existing update package");
   426                 return downloadFile;
   427             } else {
   428                 // Didn't match, so we're going to download a new one.
   429                 downloadFile.delete();
   430             }
   431         }
   433         Log.i(LOGTAG, "downloading update package");
   434         sendCheckUpdateResult(UpdateServiceHelper.CheckUpdateResult.DOWNLOADING);
   436         OutputStream output = null;
   437         InputStream input = null;
   439         mDownloading = true;
   440         mCancelDownload = false;
   441         showDownloadNotification(downloadFile);
   443         try {
   444             URLConnection conn = openConnectionWithProxy(info.url);
   445             int length = conn.getContentLength();
   447             output = new BufferedOutputStream(new FileOutputStream(downloadFile));
   448             input = new BufferedInputStream(conn.getInputStream());
   450             byte[] buf = new byte[BUFSIZE];
   451             int len = 0;
   453             int bytesRead = 0;
   454             int lastNotify = 0;
   456             while ((len = input.read(buf, 0, BUFSIZE)) > 0 && !mCancelDownload) {
   457                 output.write(buf, 0, len);
   458                 bytesRead += len;
   459                 // Updating the notification takes time so only do it every 1MB
   460                 if(bytesRead - lastNotify > 1048576) {
   461 	                mBuilder.setProgress(length, bytesRead, false);
   462 	                mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
   463 	                lastNotify = bytesRead;
   464                 }
   465             }
   467             mNotificationManager.cancel(NOTIFICATION_ID);
   469             // if the download was canceled by the user
   470             // delete the update package
   471             if (mCancelDownload) {
   472                 Log.i(LOGTAG, "download canceled by user!");
   473                 downloadFile.delete();
   475                 return null;
   476             } else {
   477                 Log.i(LOGTAG, "completed update download!");
   478                 return downloadFile;
   479             }
   480         } catch (Exception e) {
   481             downloadFile.delete();
   482             showDownloadFailure();
   484             Log.e(LOGTAG, "failed to download update: ", e);
   485             return null;
   486         } finally {
   487             try {
   488                 if (input != null)
   489                     input.close();
   490             } catch (java.io.IOException e) {}
   492             try {
   493                 if (output != null)
   494                     output.close();
   495             } catch (java.io.IOException e) {}
   497             mDownloading = false;
   498         }
   499     }
   501     private boolean verifyDownloadedPackage(File updateFile) {
   502         MessageDigest digest = createMessageDigest(getLastHashFunction());
   503         if (digest == null)
   504             return false;
   506         InputStream input = null;
   508         try {
   509             input = new BufferedInputStream(new FileInputStream(updateFile));
   511             byte[] buf = new byte[BUFSIZE];
   512             int len;
   513             while ((len = input.read(buf, 0, BUFSIZE)) > 0) {
   514                 digest.update(buf, 0, len);
   515             }
   516         } catch (java.io.IOException e) {
   517             Log.e(LOGTAG, "Failed to verify update package: ", e);
   518             return false;
   519         } finally {
   520             try {
   521                 if (input != null)
   522                     input.close();
   523             } catch(java.io.IOException e) {}
   524         }
   526         String hex = Hex.encodeHexString(digest.digest());
   527         if (!hex.equals(getLastHashValue())) {
   528             Log.e(LOGTAG, "Package hash does not match");
   529             return false;
   530         }
   532         return true;
   533     }
   535     private void applyUpdate(String updatePath) {
   536         if (updatePath == null) {
   537             updatePath = mPrefs.getString(KEY_LAST_FILE_NAME, null);
   538         }
   539         applyUpdate(new File(updatePath));
   540     }
   542     private void applyUpdate(File updateFile) {
   543         mApplyImmediately = false;
   545         if (!updateFile.exists())
   546             return;
   548         Log.i(LOGTAG, "Verifying package: " + updateFile);
   550         if (!verifyDownloadedPackage(updateFile)) {
   551             Log.e(LOGTAG, "Not installing update, failed verification");
   552             return;
   553         }
   555         Intent intent = new Intent(Intent.ACTION_VIEW);
   556         intent.setDataAndType(Uri.fromFile(updateFile), "application/vnd.android.package-archive");
   557         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   558         startActivity(intent);
   559     }
   561     private String getLastBuildID() {
   562         return mPrefs.getString(KEY_LAST_BUILDID, null);
   563     }
   565     private String getLastHashFunction() {
   566         return mPrefs.getString(KEY_LAST_HASH_FUNCTION, null);
   567     }
   569     private String getLastHashValue() {
   570         return mPrefs.getString(KEY_LAST_HASH_VALUE, null);
   571     }
   573     private Calendar getLastAttemptDate() {
   574         long lastAttempt = mPrefs.getLong(KEY_LAST_ATTEMPT_DATE, -1);
   575         if (lastAttempt < 0)
   576             return null;
   578         GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
   579         cal.setTimeInMillis(lastAttempt);
   580         return cal;
   581     }
   583     private void setLastAttemptDate() {
   584         SharedPreferences.Editor editor = mPrefs.edit();
   585         editor.putLong(KEY_LAST_ATTEMPT_DATE, System.currentTimeMillis());
   586         editor.commit();
   587     }
   589     private int getAutoDownloadPolicy() {
   590         return mPrefs.getInt(KEY_AUTODOWNLOAD_POLICY, UpdateServiceHelper.AUTODOWNLOAD_WIFI);
   591     }
   593     private void setAutoDownloadPolicy(int policy) {
   594         SharedPreferences.Editor editor = mPrefs.edit();
   595         editor.putInt(KEY_AUTODOWNLOAD_POLICY, policy);
   596         editor.commit();
   597     }
   599     private void saveUpdateInfo(UpdateInfo info, File downloaded) {
   600         SharedPreferences.Editor editor = mPrefs.edit();
   601         editor.putString(KEY_LAST_BUILDID, info.buildID);
   602         editor.putString(KEY_LAST_HASH_FUNCTION, info.hashFunction);
   603         editor.putString(KEY_LAST_HASH_VALUE, info.hashValue);
   604         editor.putString(KEY_LAST_FILE_NAME, downloaded.toString());
   605         editor.commit();
   606     }
   608     private class UpdateInfo {
   609         public URL url;
   610         public String buildID;
   611         public String hashFunction;
   612         public String hashValue;
   613         public int size;
   615         private boolean isNonEmpty(String s) {
   616             return s != null && s.length() > 0;
   617         }
   619         public boolean isValid() {
   620             return url != null && isNonEmpty(buildID) &&
   621                 isNonEmpty(hashFunction) && isNonEmpty(hashValue) && size > 0;
   622         }
   624         @Override
   625         public String toString() {
   626             return "url = " + url + ", buildID = " + buildID + ", hashFunction = " + hashFunction + ", hashValue = " + hashValue + ", size = " + size;
   627         }
   628     }
   629 }

mercurial