blob: 3d6fb6bf8a18ab83731b21b20242009a23cb2c68 [file] [log] [blame]
Kim Hansen086964d2013-12-10 11:33:28 +00001/*
2 * Copyright (C) 2013 Fairphone Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.fairphone.updater;
18
19import android.app.DownloadManager;
20import android.app.DownloadManager.Request;
21import android.app.Notification;
22import android.app.NotificationManager;
23import android.app.PendingIntent;
24import android.app.Service;
25import android.app.TaskStackBuilder;
26import android.content.BroadcastReceiver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.SharedPreferences;
31import android.content.SharedPreferences.Editor;
32import android.content.res.Resources;
33import android.database.Cursor;
34import android.graphics.BitmapFactory;
35import android.net.ConnectivityManager;
36import android.net.Uri;
Jose Pascoalcf13fde2014-05-21 20:17:17 +010037import android.os.Build;
Kim Hansen086964d2013-12-10 11:33:28 +000038import android.os.Environment;
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +000039import android.os.Handler;
Kim Hansen086964d2013-12-10 11:33:28 +000040import android.os.IBinder;
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +000041import android.os.SystemClock;
Kim Hansen086964d2013-12-10 11:33:28 +000042import android.support.v4.app.NotificationCompat;
Tiago Costac14ea242014-04-24 10:14:41 +010043import android.util.Log;
Jose Pascoal0e966282014-08-12 18:49:05 +010044import android.widget.Toast;
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +000045
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +000046import com.fairphone.updater.data.UpdaterData;
Jose Pascoal7bf83a02014-10-13 18:30:18 +010047import com.fairphone.updater.data.Version;
48import com.fairphone.updater.data.VersionParserHelper;
Jose Pascoalac6a8462014-09-10 20:12:08 +010049import com.fairphone.updater.gappsinstaller.GappsInstallerHelper;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000050import com.fairphone.updater.tools.PrivilegeChecker;
Jose Pascoal02b849e2014-06-26 17:07:51 +010051import com.fairphone.updater.tools.RSAUtils;
52import com.fairphone.updater.tools.Utils;
Jose Pascoalcf13fde2014-05-21 20:17:17 +010053import com.stericson.RootTools.execution.CommandCapture;
54import com.stericson.RootTools.execution.Shell;
Kim Hansen086964d2013-12-10 11:33:28 +000055
Filipe72b5a7f2015-09-01 08:59:54 +000056import java.io.File;
57import java.io.FileInputStream;
58import java.io.FileOutputStream;
59import java.io.IOException;
60import java.io.UnsupportedEncodingException;
61import java.net.URLEncoder;
62import java.nio.channels.FileChannel;
63import java.nio.charset.Charset;
64import java.util.concurrent.TimeoutException;
65
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +000066
Jose Pascoal810950b2014-10-09 17:16:08 +010067public class UpdaterService extends Service
68{
Kim Hansen086964d2013-12-10 11:33:28 +000069
Filipe Gonçalves6ce7b142015-01-22 14:39:29 +000070 public static final String LAST_CONFIG_DOWNLOAD_IN_MS = "LAST_CONFIG_DOWNLOAD_IN_MS";
Filipe Gonçalves8b263ad2015-01-12 12:30:11 +000071 private static final int CONFIG_FILE_DOWNLOAD_TIMEOUT_MILLIS = 23500;
Filipe Gonçalves8f226b02014-12-12 11:21:58 +000072 public static final String EXTRA_FORCE_CONFIG_FILE_DOWNLOAD = "FORCE_DOWNLOAD";
73
Jose Pascoal810950b2014-10-09 17:16:08 +010074 private static final String TAG = UpdaterService.class.getSimpleName();
75
Jose Pascoalc2545cc2014-12-18 16:51:52 +000076 public static final String PREFERENCE_LAST_CONFIG_DOWNLOAD_ID = "LastConfigDownloadId";
Tiago Costa87925fe2014-12-02 17:57:51 +000077 public static final String PREFERENCE_REINSTALL_GAPPS = "ReinstallGappsOmnStartUp";
Filipe72b5a7f2015-09-01 08:59:54 +000078 public static final String PREFERENCE_CONFIG_MD_5 = "CONFIG_MD5";
Jose Pascoal810950b2014-10-09 17:16:08 +010079 private DownloadManager mDownloadManager = null;
80 private DownloadBroadCastReceiver mDownloadBroadCastReceiver = null;
Kim Hansen086964d2013-12-10 11:33:28 +000081
Jose Pascoal1ee56e22014-05-21 16:58:20 +010082 private static final int MAX_DOWNLOAD_RETRIES = 3;
83 private int mDownloadRetries;
Jose Pascoal810950b2014-10-09 17:16:08 +010084 private long mLatestFileDownloadId;
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +000085 private boolean mInternetConnectionAvailable;
Kim Hansen086964d2013-12-10 11:33:28 +000086
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000087 private SharedPreferences mSharedPreferences;
Jose Pascoal810950b2014-10-09 17:16:08 +010088
Jose Pascoalb03b55c2015-02-19 17:34:07 +000089 private final static long DOWNLOAD_GRACE_PERIOD_IN_MS = 24 /* hour */ * Utils.MINUTES_IN_HOUR /* minute */ * Utils.SECONDS_IN_MINUTE /* second */ * 1000 /* millisecond */;
Filipe72b5a7f2015-09-01 08:59:54 +000090 private BroadcastReceiver networkStateReceiver;
Jose Pascoal29ee1012014-09-12 17:25:36 +010091
Filipe72b5a7f2015-09-01 08:59:54 +000092 @Override
Jose Pascoal810950b2014-10-09 17:16:08 +010093 public int onStartCommand(Intent intent, int flags, int startId)
94 {
Jose Pascoal810950b2014-10-09 17:16:08 +010095 // remove the logs
96 clearDataLogs();
Jose Pascoalda00b382015-01-20 18:39:28 +000097
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000098 if(Utils.isDeviceUnsupported(getApplicationContext())){
Jose Pascoalda00b382015-01-20 18:39:28 +000099 stopSelf();
100 return START_NOT_STICKY;
101 }
Jose Pascoal29ee1012014-09-12 17:25:36 +0100102
Jose Pascoal7bf83a02014-10-13 18:30:18 +0100103 mSharedPreferences = getApplicationContext().getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
Jose Pascoal29ee1012014-09-12 17:25:36 +0100104
Jose Pascoal810950b2014-10-09 17:16:08 +0100105 mLatestFileDownloadId = mSharedPreferences.getLong(PREFERENCE_LAST_CONFIG_DOWNLOAD_ID, 0);
Tiago Costa87925fe2014-12-02 17:57:51 +0000106
Jose Pascoalaa579a82014-11-05 22:17:16 +0000107 setupDownloadManager();
Tiago Costa87925fe2014-12-02 17:57:51 +0000108
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000109 setupConnectivityMonitoring();
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100110
Filipe72b5a7f2015-09-01 08:59:54 +0000111 if (Utils.isWiFiEnabled(getApplicationContext()))
Jose Pascoal810950b2014-10-09 17:16:08 +0100112 {
Filipe72b5a7f2015-09-01 08:59:54 +0000113 downloadConfigFile(intent != null && intent.getBooleanExtra(EXTRA_FORCE_CONFIG_FILE_DOWNLOAD, false));
Jose Pascoal810950b2014-10-09 17:16:08 +0100114 }
115
116 // setup the gapps installer
Jose Pascoal40916302015-02-06 18:43:47 +0000117 GappsInstallerHelper.checkGappsAreInstalled(getApplicationContext());
Jose Pascoal810950b2014-10-09 17:16:08 +0100118
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000119 runInstallationDisclaimer(getApplicationContext());
Tiago Costa87925fe2014-12-02 17:57:51 +0000120
Jose Pascoal810950b2014-10-09 17:16:08 +0100121 return Service.START_STICKY;
122 }
123
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000124 private static void runInstallationDisclaimer(Context context)
Tiago Costa87925fe2014-12-02 17:57:51 +0000125 {
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000126 SharedPreferences sharedPreferences = context.getApplicationContext().getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
127 if (sharedPreferences.getBoolean(PREFERENCE_REINSTALL_GAPPS, true) && !UpdaterData.getInstance().isAppStoreListEmpty())
Tiago Costa87925fe2014-12-02 17:57:51 +0000128 {
Tiago Costa87925fe2014-12-02 17:57:51 +0000129 if(!GappsInstallerHelper.areGappsInstalled()){
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000130 showReinstallAlert(context);
Tiago Costa87925fe2014-12-02 17:57:51 +0000131 }
132
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000133 Editor editor = sharedPreferences.edit();
Tiago Costa87925fe2014-12-02 17:57:51 +0000134 editor.putBoolean(PREFERENCE_REINSTALL_GAPPS, false);
135
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000136 editor.apply();
Tiago Costa87925fe2014-12-02 17:57:51 +0000137 }
138 }
139
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000140 private static void showReinstallAlert(Context context)
Tiago Costa87925fe2014-12-02 17:57:51 +0000141 {
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000142 if ( FairphoneUpdater.BETA_MODE_ENABLED )
Jose Pascoal40916302015-02-06 18:43:47 +0000143 {
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000144 return;
Jose Pascoal40916302015-02-06 18:43:47 +0000145 }
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000146
147 NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Tiago Costa87925fe2014-12-02 17:57:51 +0000148
Tiago Costa73575aa2015-01-19 15:48:26 +0000149 //Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getResources().getString(R.string.supportAppStoreUrl)));
Tiago Costa87925fe2014-12-02 17:57:51 +0000150
Tiago Costa73575aa2015-01-19 15:48:26 +0000151 Intent notificationIntent = new Intent(context, FairphoneUpdater.class);
152 notificationIntent.setAction(GappsInstallerHelper.EXTRA_START_GAPPS_INSTALL);
Tiago Costa87925fe2014-12-02 17:57:51 +0000153
Tiago Costa73575aa2015-01-19 15:48:26 +0000154 PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000155
Filipe Gonçalves6dc4c0d2015-10-14 17:41:43 +0100156 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.updater)
Filipe72b5a7f2015-09-01 08:59:54 +0000157 .setContentTitle(context.getResources().getString(R.string.app_full_name))
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000158 .setContentText(context.getResources().getString(R.string.appStoreReinstall))
159 .setAutoCancel(true)
160 .setDefaults(Notification.DEFAULT_SOUND)
161 .setContentIntent(contentIntent);
Tiago Costa87925fe2014-12-02 17:57:51 +0000162
163 mNotificationManager.notify(0, mBuilder.build());
164 }
165
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000166 private void downloadConfigFile(boolean forceDownload)
Jose Pascoal810950b2014-10-09 17:16:08 +0100167 {
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000168 long now = System.currentTimeMillis();
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000169 long last_download = mSharedPreferences.getLong(LAST_CONFIG_DOWNLOAD_IN_MS, 0L);
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000170 if( forceDownload || now > (last_download + DOWNLOAD_GRACE_PERIOD_IN_MS) ) {
Filipe72b5a7f2015-09-01 08:59:54 +0000171 Log.i(TAG, "Downloading updater configuration file.");
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000172 // remove the old file if its still there for some reason
173 removeLatestFileDownload(getApplicationContext());
174
175 // start the download of the latest file
176 startDownloadLatest();
Jose Pascoal810950b2014-10-09 17:16:08 +0100177
Jose Pascoal0b48f8d2015-02-06 16:06:41 +0000178 mSharedPreferences.edit().putLong(LAST_CONFIG_DOWNLOAD_IN_MS, now).apply();
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000179 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100180 }
181
Jose Pascoalcdd82042015-02-06 13:04:26 +0000182// --Commented out by Inspection START (06/02/2015 12:27):
183// public void updateGoogleAppsIntallerWidgets()
184// {
185// AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
186// int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(this, GoogleAppsInstallerWidget.class));
187// if (appWidgetIds.length > 0)
188// {
189// new GoogleAppsInstallerWidget().onUpdate(this, appWidgetManager, appWidgetIds);
190// }
191// }
192// --Commented out by Inspection STOP (06/02/2015 12:27)
Jose Pascoal810950b2014-10-09 17:16:08 +0100193
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000194 private static void clearDataLogs()
Jose Pascoal810950b2014-10-09 17:16:08 +0100195 {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000196 if (PrivilegeChecker.isPrivilegedApp()) {
197 clearDataLogsPrivileged();
198 } else {
199 clearDataLogsUnprivileged();
Tiago Costac14ea242014-04-24 10:14:41 +0100200 }
201 }
Kim Hansen086964d2013-12-10 11:33:28 +0000202
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000203 private static void clearDataLogsPrivileged()
204 {
205 try
206 {
207 Log.d(TAG, "Clearing dump log data...");
208 Process p = Runtime.getRuntime().exec("rm /data/log_other_mode/*_log");
209 try
210 {
211 p.waitFor();
212 } catch (InterruptedException e)
213 {
214 e.printStackTrace();
215 }
216 } catch (IOException e)
217 {
218 Log.d(TAG, "Clearing dump log data failed: " + e.getLocalizedMessage());
219 }
220 }
221
222 private static void clearDataLogsUnprivileged()
223 {
224 try
225 {
226 Log.d(TAG, "Clearing dump log data...");
227 Shell.runCommand(new CommandCapture(0, "rm /data/log_other_mode/*_log"));
228 } catch (IOException | TimeoutException e)
229 {
230 Log.d(TAG, "Clearing dump log data failed: " + e.getLocalizedMessage());
231 }
232 }
233
234
Jose Pascoal810950b2014-10-09 17:16:08 +0100235 @Override
236 public IBinder onBind(Intent intent)
237 {
238 return null;
239 }
Kim Hansen086964d2013-12-10 11:33:28 +0000240
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000241 void startDownloadLatest()
Jose Pascoal810950b2014-10-09 17:16:08 +0100242 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000243 Resources resources = getApplicationContext().getResources();
244 String downloadLink = getConfigDownloadLink(getApplicationContext());
245 // set the download for the latest version on the download manager
246 Request request = createDownloadRequest(downloadLink, resources.getString(R.string.configFilename) + resources.getString(R.string.config_zip));
Jose Pascoalfd3e43c2014-05-22 20:29:17 +0100247
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000248 if (request != null && mDownloadManager != null)
249 {
Jose Pascoal87758742015-01-28 20:00:22 +0000250 //Guarantee that only we have only one download
251 long oldDownloadId = mSharedPreferences.getLong(PREFERENCE_LAST_CONFIG_DOWNLOAD_ID, 0);
252 if(oldDownloadId != 0){
253 mDownloadManager.remove(oldDownloadId);
254 saveLatestDownloadId(0);
255 }
256
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000257 mLatestFileDownloadId = mDownloadManager.enqueue(request);
258 saveLatestDownloadId(mLatestFileDownloadId);
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +0000259
260 final long currentId = mLatestFileDownloadId;
261 // Cancel download if it is stuck since DownloadManager doesn't seem able to do it.
262 new Handler().postAtTime(new Runnable() {
263 @Override
264 public void run() {
Jose Pascoal46fdb062015-02-05 18:59:32 +0000265 onDownloadStatus(currentId, new Runnable() {
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000266 @Override
267 public void run() {
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +0000268 Log.w(TAG, "Configuration file download timed out");
269 mDownloadManager.remove(currentId);
Jose Pascoal0b48f8d2015-02-06 16:06:41 +0000270 mSharedPreferences.edit().remove(LAST_CONFIG_DOWNLOAD_IN_MS).apply();
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +0000271 }
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000272 });
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +0000273 }
Filipe Gonçalves8b263ad2015-01-12 12:30:11 +0000274 }, SystemClock.uptimeMillis() + CONFIG_FILE_DOWNLOAD_TIMEOUT_MILLIS);
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000275 }
276 else
277 {
278 Log.e(TAG, "Invalid request for link " + downloadLink);
279 Intent i = new Intent(FairphoneUpdater.FAIRPHONE_UPDATER_CONFIG_DOWNLOAD_FAILED);
280 i.putExtra(FairphoneUpdater.FAIRPHONE_UPDATER_CONFIG_DOWNLOAD_LINK, downloadLink);
281 sendBroadcast(i);
Jose Pascoal810950b2014-10-09 17:16:08 +0100282 }
283 }
284
Jose Pascoalc2545cc2014-12-18 16:51:52 +0000285 private void saveLatestDownloadId(long id)
286 {
287 mLatestFileDownloadId = id;
288 Editor editor = mSharedPreferences.edit();
289 editor.putLong(PREFERENCE_LAST_CONFIG_DOWNLOAD_ID, id);
290 editor.commit();
291 }
292
Jose Pascoal810950b2014-10-09 17:16:08 +0100293 private String getConfigDownloadLink(Context context)
294 {
295
296 Resources resources = context.getResources();
297
298 StringBuilder sb = new StringBuilder();
Filipe Gonçalvesafeac2c2015-01-27 17:49:53 +0000299 String download_url = mSharedPreferences.getString(FairphoneUpdater.PREFERENCE_OTA_DOWNLOAD_URL, getResources().getString(R.string.downloadUrl));
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000300
301 sb.append(download_url);
Jose Pascoal810950b2014-10-09 17:16:08 +0100302 sb.append(Build.MODEL.replaceAll("\\s", ""));
Jose Pascoal0a5be012014-11-17 16:55:40 +0000303 sb.append(Utils.getPartitionDownloadPath(resources));
Jose Pascoal810950b2014-10-09 17:16:08 +0100304 sb.append("/");
305
306 sb.append(resources.getString(R.string.configFilename));
307
308 sb.append(resources.getString(R.string.config_zip));
309
310 addModelAndOS(context, sb);
311
312 String downloadLink = sb.toString();
313
314 Log.d(TAG, "Download link: " + downloadLink);
315
316 return downloadLink;
317 }
Jose Pascoalcf13fde2014-05-21 20:17:17 +0100318
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000319 private static void addModelAndOS(Context context, StringBuilder sb)
Tiago Costab24ef1c2014-07-25 12:02:34 +0100320 {
321 // attach the model and the os
Jose Pascoal810950b2014-10-09 17:16:08 +0100322 sb.append("?");
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000323 sb.append("model=").append(Build.MODEL.replaceAll("\\s", ""));
Jose Pascoal810950b2014-10-09 17:16:08 +0100324 Version currentVersion = VersionParserHelper.getDeviceVersion(context.getApplicationContext());
325
326 if (currentVersion != null)
327 {
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000328 try {
329 final String defaultCharset = Charset.defaultCharset().displayName();
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000330 sb.append("&os=").append(URLEncoder.encode(currentVersion.getAndroidVersion(), defaultCharset));
331 sb.append("&b_n=").append(URLEncoder.encode(currentVersion.getBuildNumber(), defaultCharset));
332 sb.append("&ota_v_n=").append(URLEncoder.encode(String.valueOf(currentVersion.getNumber()), defaultCharset));
333 sb.append("&d=").append(URLEncoder.encode(currentVersion.getReleaseDate(), defaultCharset));
334 sb.append("&beta=").append(URLEncoder.encode(currentVersion.getBetaStatus(), defaultCharset));
335 sb.append("&dev=").append(FairphoneUpdater.DEV_MODE_ENABLED ? "1" : "0");
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000336 } catch (UnsupportedEncodingException e) {
337 Log.e(TAG, "Failed to add extra info on update request: "+e.getLocalizedMessage());
338 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100339 }
Tiago Costab24ef1c2014-07-25 12:02:34 +0100340 }
341
Jose Pascoal810950b2014-10-09 17:16:08 +0100342 private static void setNotification(Context currentContext)
343 {
Kim Hansen086964d2013-12-10 11:33:28 +0000344
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000345 if ( FairphoneUpdater.BETA_MODE_ENABLED )
Jose Pascoal40916302015-02-06 18:43:47 +0000346 {
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000347 return;
Jose Pascoal40916302015-02-06 18:43:47 +0000348 }
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000349
Jose Pascoal810950b2014-10-09 17:16:08 +0100350 Context context = currentContext.getApplicationContext();
Kim Hansen086964d2013-12-10 11:33:28 +0000351
Jose Pascoal810950b2014-10-09 17:16:08 +0100352 NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Kim Hansen086964d2013-12-10 11:33:28 +0000353
Jose Pascoal810950b2014-10-09 17:16:08 +0100354 NotificationCompat.Builder builder =
Filipe Gonçalves6dc4c0d2015-10-14 17:41:43 +0100355 new NotificationCompat.Builder(context).setSmallIcon(R.drawable.updater)
Filipe72b5a7f2015-09-01 08:59:54 +0000356 .setContentTitle(context.getResources().getString(R.string.app_full_name))
Pedro Arelo773bd822014-10-10 11:57:34 +0100357 .setContentText(context.getResources().getString(R.string.fairphone_update_message));
Kim Hansen086964d2013-12-10 11:33:28 +0000358
Jose Pascoal7bf83a02014-10-13 18:30:18 +0100359 Intent resultIntent = new Intent(context, FairphoneUpdater.class);
Jose Pascoal810950b2014-10-09 17:16:08 +0100360 TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
Kim Hansen086964d2013-12-10 11:33:28 +0000361
Jose Pascoal7bf83a02014-10-13 18:30:18 +0100362 stackBuilder.addParentStack(FairphoneUpdater.class);
Kim Hansen086964d2013-12-10 11:33:28 +0000363
Jose Pascoal810950b2014-10-09 17:16:08 +0100364 stackBuilder.addNextIntent(resultIntent);
365 PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
Kim Hansen086964d2013-12-10 11:33:28 +0000366
Jose Pascoal810950b2014-10-09 17:16:08 +0100367 builder.setContentIntent(resultPendingIntent);
Kim Hansen086964d2013-12-10 11:33:28 +0000368
Jose Pascoal810950b2014-10-09 17:16:08 +0100369 Notification notificationWhileRunnig = builder.build();
Jose Pascoalfd3e43c2014-05-22 20:29:17 +0100370
Jose Pascoal810950b2014-10-09 17:16:08 +0100371 // Add notification
372 manager.notify(0, notificationWhileRunnig);
373 }
Kim Hansen086964d2013-12-10 11:33:28 +0000374
Jose Pascoal810950b2014-10-09 17:16:08 +0100375 private Request createDownloadRequest(String url, String fileName)
376 {
Kim Hansen086964d2013-12-10 11:33:28 +0000377
Jose Pascoal810950b2014-10-09 17:16:08 +0100378 Resources resources = getApplicationContext().getResources();
379 Request request;
Kim Hansen086964d2013-12-10 11:33:28 +0000380
Jose Pascoal810950b2014-10-09 17:16:08 +0100381 try
382 {
383 request = new Request(Uri.parse(url));
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000384 final File externalStoragePublicDirectory = Environment.getExternalStoragePublicDirectory(Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder));
Jose Pascoal46fdb062015-02-05 18:59:32 +0000385 final boolean notMkDirs = !externalStoragePublicDirectory.mkdirs();
386 if(notMkDirs && !externalStoragePublicDirectory.exists()) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000387 throw new Exception("Couldn't create updater dir structures.");
388 }
Kim Hansen086964d2013-12-10 11:33:28 +0000389
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000390 request.setDestinationInExternalPublicDir(resources.getString(R.string.updaterFolder), fileName);
Jose Pascoal810950b2014-10-09 17:16:08 +0100391 request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
392 request.setAllowedOverRoaming(false);
Pedro Arelo773bd822014-10-10 11:57:34 +0100393 request.setTitle(resources.getString(R.string.fairphone_update_message_title));
Jose Pascoal810950b2014-10-09 17:16:08 +0100394 } catch (Exception e)
395 {
396 Log.e(TAG, "Error creating request: " + e.getMessage());
397 request = null;
398 }
Kim Hansen086964d2013-12-10 11:33:28 +0000399
Jose Pascoal810950b2014-10-09 17:16:08 +0100400 return request;
401 }
Kim Hansen086964d2013-12-10 11:33:28 +0000402
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000403 private void setupConnectivityMonitoring()
404 {
405
Filipe72b5a7f2015-09-01 08:59:54 +0000406 if (networkStateReceiver == null) {
407 // Check current connectivity status
408 mInternetConnectionAvailable = Utils.isWiFiEnabled(getApplicationContext());
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000409
Filipe72b5a7f2015-09-01 08:59:54 +0000410 // Setup monitoring for future connectivity status changes
411 networkStateReceiver = new BroadcastReceiver()
Tiago Costa87925fe2014-12-02 17:57:51 +0000412 {
Filipe72b5a7f2015-09-01 08:59:54 +0000413 @Override
414 public void onReceive(Context context, Intent intent)
Tiago Costa87925fe2014-12-02 17:57:51 +0000415 {
Filipe72b5a7f2015-09-01 08:59:54 +0000416 if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
417 Log.i(TAG, "Lost network connectivity.");
418 mInternetConnectionAvailable = false;
419 if (mLatestFileDownloadId != 0 && mDownloadManager != null)
420 {
421 onDownloadStatus(mLatestFileDownloadId, new Runnable() {
422 @Override
423 public void run() {
424 Log.d(TAG, "Removing pending download.");
425 mDownloadManager.remove(mLatestFileDownloadId);
426 saveLatestDownloadId(0);
427 }
428 });
429 }
430 }
431 else
Tiago Costa87925fe2014-12-02 17:57:51 +0000432 {
Filipe72b5a7f2015-09-01 08:59:54 +0000433 int conn_type = intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_DUMMY);
434 if( conn_type == ConnectivityManager.TYPE_WIFI ) {
435 Log.i(TAG, "Network connectivity potentially available.");
436 if (!mInternetConnectionAvailable) {
437 downloadConfigFile(false);
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000438 }
Filipe72b5a7f2015-09-01 08:59:54 +0000439 mInternetConnectionAvailable = true;
440 }
Tiago Costa87925fe2014-12-02 17:57:51 +0000441 }
442 }
Filipe72b5a7f2015-09-01 08:59:54 +0000443 };
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000444
Filipe72b5a7f2015-09-01 08:59:54 +0000445 IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
446 registerReceiver(networkStateReceiver, filter);
Tiago Costa87925fe2014-12-02 17:57:51 +0000447
Filipe72b5a7f2015-09-01 08:59:54 +0000448 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100449 }
450
451 private void setupDownloadManager()
452 {
453 if (mDownloadManager == null)
454 {
455 mDownloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
456 }
457
Filipe Gonçalvesd0dfe912014-11-04 16:24:25 +0000458 if (mDownloadBroadCastReceiver == null)
Jose Pascoal810950b2014-10-09 17:16:08 +0100459 {
460 mDownloadBroadCastReceiver = new DownloadBroadCastReceiver();
461
462 getApplicationContext().registerReceiver(mDownloadBroadCastReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
463 }
464 }
465
Filipe72b5a7f2015-09-01 08:59:54 +0000466 private static void copyConfigToData(Context context) throws IOException {
467 Resources resources = context.getApplicationContext().getResources();
468 String targetPath = Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder);
469 FileInputStream inStream = new FileInputStream(targetPath + resources.getString(R.string.configFilename) + resources.getString(R.string.config_xml));
470 FileOutputStream outStream = context.openFileOutput(resources.getString(R.string.configFilename) + resources.getString(R.string.config_xml), MODE_PRIVATE);
471 FileChannel inChannel = inStream.getChannel();
472 FileChannel outChannel = outStream.getChannel();
473 inChannel.transferTo(0, inChannel.size(), outChannel);
474 inStream.close();
475 outStream.close();
476 }
477
Jose Pascoal810950b2014-10-09 17:16:08 +0100478 private static void checkVersionValidation(Context context)
479 {
Jose Pascoal810950b2014-10-09 17:16:08 +0100480 Version latestVersion = VersionParserHelper.getLatestVersion(context.getApplicationContext());
481 Version currentVersion = VersionParserHelper.getDeviceVersion(context.getApplicationContext());
Jose Pascoal810950b2014-10-09 17:16:08 +0100482 if (latestVersion != null)
483 {
484 if (latestVersion.isNewerVersionThan(currentVersion))
485 {
486 setNotification(context);
487 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100488 }
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000489 runInstallationDisclaimer(context);
Jose Pascoald5e4d4a2015-03-13 18:17:48 +0000490
491 // to update the activity
492 Intent updateIntent = new Intent(FairphoneUpdater.FAIRPHONE_UPDATER_NEW_VERSION_RECEIVED);
493 context.sendBroadcast(updateIntent);
Jose Pascoal810950b2014-10-09 17:16:08 +0100494 }
495
Filipe72b5a7f2015-09-01 08:59:54 +0000496 public static boolean readUpdaterData(Context context){
497 Version latestVersion = VersionParserHelper.getLatestVersion(context.getApplicationContext());
498 return latestVersion != null;
499 }
500
501 public static boolean updateUpdaterData(Context context)
Jose Pascoal810950b2014-10-09 17:16:08 +0100502 {
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100503
504 boolean retVal = false;
505 Resources resources = context.getApplicationContext().getResources();
Jose Pascoal810950b2014-10-09 17:16:08 +0100506 String targetPath = Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder);
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100507
Jose Pascoal810950b2014-10-09 17:16:08 +0100508 String filePath = targetPath + resources.getString(R.string.configFilename) + resources.getString(R.string.config_zip);
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100509
510 File file = new File(filePath);
511
Jose Pascoal810950b2014-10-09 17:16:08 +0100512 if (file.exists())
513 {
Filipe72b5a7f2015-09-01 08:59:54 +0000514 String md5sum = Utils.calculateMD5(file);
515 SharedPreferences sp = context.getApplicationContext().getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
516 if(sp.getString(PREFERENCE_CONFIG_MD_5, "").equals(md5sum)){
Jose Pascoal810950b2014-10-09 17:16:08 +0100517 retVal = true;
Filipe72b5a7f2015-09-01 08:59:54 +0000518 } else {
519 if (RSAUtils.checkFileSignature(context, filePath, targetPath)) {
520 try {
521 copyConfigToData(context);
522 checkVersionValidation(context);
523 retVal = true;
524 sp.edit().putString(PREFERENCE_CONFIG_MD_5, md5sum).apply();
525 } catch (IOException e) {
526 Log.e(TAG, "Failed to store configuration " + e.getLocalizedMessage());
527 retVal = false;
528 }
529 } else {
530 //Toast.makeText(context, resources.getString(R.string.invalid_signature_download_message), Toast.LENGTH_LONG).show();
531 final boolean notDeleted = !file.delete();
532 if(notDeleted) {
533 Log.d(TAG, "Unable to delete "+file.getAbsolutePath());
534 }
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000535
Filipe72b5a7f2015-09-01 08:59:54 +0000536 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100537 }
Filipe72b5a7f2015-09-01 08:59:54 +0000538 } else {
539 Log.wtf(TAG, "No file");
Jose Pascoal810950b2014-10-09 17:16:08 +0100540 }
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100541
542 return retVal;
543 }
Kim Hansen086964d2013-12-10 11:33:28 +0000544
Jose Pascoal810950b2014-10-09 17:16:08 +0100545 private void removeLatestFileDownload(Context context)
546 {
547 if (mLatestFileDownloadId != 0 && mDownloadManager != null)
548 {
549 mDownloadManager.remove(mLatestFileDownloadId);
Jose Pascoalc2545cc2014-12-18 16:51:52 +0000550 saveLatestDownloadId(0);
Kim Hansen086964d2013-12-10 11:33:28 +0000551 }
Jose Pascoalc2545cc2014-12-18 16:51:52 +0000552 VersionParserHelper.removeConfigFiles(context);
Kim Hansen086964d2013-12-10 11:33:28 +0000553 }
554
Jose Pascoal810950b2014-10-09 17:16:08 +0100555 private boolean retryDownload(Context context)
556 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000557 Log.d(TAG, "Retry "+mDownloadRetries+" of "+MAX_DOWNLOAD_RETRIES);
Jose Pascoal810950b2014-10-09 17:16:08 +0100558 // invalid file
559 boolean removeReceiver = true;
560 removeLatestFileDownload(context);
561 if (mDownloadRetries < MAX_DOWNLOAD_RETRIES)
562 {
563 startDownloadLatest();
564 mDownloadRetries++;
565 removeReceiver = false;
566 }
567 if (removeReceiver)
568 {
Pedro Arelo773bd822014-10-10 11:57:34 +0100569 Toast.makeText(getApplicationContext(), getResources().getString(R.string.config_file_download_error_message), Toast.LENGTH_LONG).show();
Jose Pascoal810950b2014-10-09 17:16:08 +0100570 }
571 return removeReceiver;
572 }
Jose Pascoal0e966282014-08-12 18:49:05 +0100573
Jose Pascoal46fdb062015-02-05 18:59:32 +0000574 private void onDownloadStatus(long id, Runnable ifRunning) {
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000575 Cursor cursor = mDownloadManager != null ? mDownloadManager.query(new DownloadManager.Query().setFilterById(id)) : null;
576 if (cursor != null && cursor.moveToFirst())
577 {
578 int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
579 switch(status){
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000580 case DownloadManager.STATUS_PAUSED:
581 if (ifRunning != null) {
582 ifRunning.run();
583 }
584 break;
585 case DownloadManager.STATUS_PENDING:
586 if (ifRunning != null) {
587 ifRunning.run();
588 }
589 break;
590 case DownloadManager.STATUS_RUNNING:
591 if (ifRunning != null) {
592 ifRunning.run();
593 }
594 break;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000595 case DownloadManager.STATUS_FAILED:
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000596 case DownloadManager.STATUS_SUCCESSFUL:
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000597 default:
598 break;
599 }
600 }
601 if (cursor != null)
602 {
603 cursor.close();
604 }
605 }
606
Jose Pascoal810950b2014-10-09 17:16:08 +0100607 private class DownloadBroadCastReceiver extends BroadcastReceiver
608 {
Kim Hansen086964d2013-12-10 11:33:28 +0000609
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100610 @Override
Jose Pascoal810950b2014-10-09 17:16:08 +0100611 public void onReceive(Context context, Intent intent)
612 {
Kim Hansen086964d2013-12-10 11:33:28 +0000613
Jose Pascoal810950b2014-10-09 17:16:08 +0100614 boolean removeReceiver = false;
Kim Hansen086964d2013-12-10 11:33:28 +0000615
Jose Pascoal810950b2014-10-09 17:16:08 +0100616 DownloadManager.Query query = new DownloadManager.Query();
617
618 query.setFilterById(mLatestFileDownloadId);
Jose Pascoal810950b2014-10-09 17:16:08 +0100619
Jose Pascoalaa579a82014-11-05 22:17:16 +0000620 Cursor cursor = mDownloadManager != null ? mDownloadManager.query(query) : null;
621
622 if (cursor != null && cursor.moveToFirst())
Jose Pascoal810950b2014-10-09 17:16:08 +0100623 {
624 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
625 int status = cursor.getInt(columnIndex);
626 Resources resources = context.getApplicationContext().getResources();
627
628 switch (status)
629 {
630 case DownloadManager.STATUS_SUCCESSFUL:
631 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000632 Log.d(TAG, "Download successful.");
Jose Pascoal810950b2014-10-09 17:16:08 +0100633
Filipe72b5a7f2015-09-01 08:59:54 +0000634 if (updateUpdaterData(context))
Jose Pascoal810950b2014-10-09 17:16:08 +0100635 {
Filipe72b5a7f2015-09-01 08:59:54 +0000636 removeLatestFileDownload(context);
Jose Pascoal810950b2014-10-09 17:16:08 +0100637 }
638 else
639 {
Pedro Arelo773bd822014-10-10 11:57:34 +0100640 Toast.makeText(getApplicationContext(), resources.getString(R.string.invalid_signature_download_message), Toast.LENGTH_LONG).show();
Jose Pascoal810950b2014-10-09 17:16:08 +0100641 removeReceiver = retryDownload(context);
642 }
643 break;
644 }
645 case DownloadManager.STATUS_FAILED:
646 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000647 Log.d(TAG, "Download failed.");
Jose Pascoal810950b2014-10-09 17:16:08 +0100648 removeReceiver = retryDownload(context);
649 break;
650 }
Filipe Gonçalves42ffc322015-01-28 12:51:14 +0000651 default:
652 {
653 Log.d(TAG, "Status broadcast on mLatestFileDownloadId ("+mLatestFileDownloadId+"): "+ status);
654 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100655 }
656 }
Tiago Costa87925fe2014-12-02 17:57:51 +0000657
Jose Pascoalaa579a82014-11-05 22:17:16 +0000658 if (cursor != null)
659 {
660 cursor.close();
661 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100662
663 if (removeReceiver)
664 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000665 Log.d(TAG, "Configuration download failed. Clearing grace period.");
Jose Pascoal0b48f8d2015-02-06 16:06:41 +0000666 mSharedPreferences.edit().remove(LAST_CONFIG_DOWNLOAD_IN_MS).apply();
Jose Pascoal810950b2014-10-09 17:16:08 +0100667 }
668 }
669 }
Kim Hansen086964d2013-12-10 11:33:28 +0000670}