blob: 04d12fc4980d812e519cf4c0854c54da8c09db33 [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;
Kim Hansen086964d2013-12-10 11:33:28 +000034import android.net.ConnectivityManager;
35import android.net.Uri;
Jose Pascoalcf13fde2014-05-21 20:17:17 +010036import android.os.Build;
Kim Hansen086964d2013-12-10 11:33:28 +000037import android.os.Environment;
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +000038import android.os.Handler;
Kim Hansen086964d2013-12-10 11:33:28 +000039import android.os.IBinder;
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +000040import android.os.SystemClock;
Kim Hansen086964d2013-12-10 11:33:28 +000041import android.support.v4.app.NotificationCompat;
Tiago Costac14ea242014-04-24 10:14:41 +010042import android.util.Log;
Jose Pascoal0e966282014-08-12 18:49:05 +010043import android.widget.Toast;
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +000044
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +000045import com.fairphone.updater.data.UpdaterData;
Jose Pascoal7bf83a02014-10-13 18:30:18 +010046import com.fairphone.updater.data.Version;
47import com.fairphone.updater.data.VersionParserHelper;
Jose Pascoalac6a8462014-09-10 20:12:08 +010048import com.fairphone.updater.gappsinstaller.GappsInstallerHelper;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000049import com.fairphone.updater.tools.PrivilegeChecker;
Jose Pascoal02b849e2014-06-26 17:07:51 +010050import com.fairphone.updater.tools.RSAUtils;
51import com.fairphone.updater.tools.Utils;
Jose Pascoalcf13fde2014-05-21 20:17:17 +010052import com.stericson.RootTools.execution.CommandCapture;
53import com.stericson.RootTools.execution.Shell;
Kim Hansen086964d2013-12-10 11:33:28 +000054
Filipe72b5a7f2015-09-01 08:59:54 +000055import java.io.File;
56import java.io.FileInputStream;
57import java.io.FileOutputStream;
58import java.io.IOException;
59import java.io.UnsupportedEncodingException;
60import java.net.URLEncoder;
61import java.nio.channels.FileChannel;
62import java.nio.charset.Charset;
63import java.util.concurrent.TimeoutException;
64
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +000065
Jose Pascoal810950b2014-10-09 17:16:08 +010066public class UpdaterService extends Service
67{
Kim Hansen086964d2013-12-10 11:33:28 +000068
Filipe Gonçalves6ce7b142015-01-22 14:39:29 +000069 public static final String LAST_CONFIG_DOWNLOAD_IN_MS = "LAST_CONFIG_DOWNLOAD_IN_MS";
Filipe Gonçalves8b263ad2015-01-12 12:30:11 +000070 private static final int CONFIG_FILE_DOWNLOAD_TIMEOUT_MILLIS = 23500;
Filipe Gonçalves8f226b02014-12-12 11:21:58 +000071 public static final String EXTRA_FORCE_CONFIG_FILE_DOWNLOAD = "FORCE_DOWNLOAD";
72
Jose Pascoal810950b2014-10-09 17:16:08 +010073 private static final String TAG = UpdaterService.class.getSimpleName();
74
Jose Pascoalc2545cc2014-12-18 16:51:52 +000075 public static final String PREFERENCE_LAST_CONFIG_DOWNLOAD_ID = "LastConfigDownloadId";
Tiago Costa87925fe2014-12-02 17:57:51 +000076 public static final String PREFERENCE_REINSTALL_GAPPS = "ReinstallGappsOmnStartUp";
Filipe72b5a7f2015-09-01 08:59:54 +000077 public static final String PREFERENCE_CONFIG_MD_5 = "CONFIG_MD5";
Jose Pascoal810950b2014-10-09 17:16:08 +010078 private DownloadManager mDownloadManager = null;
79 private DownloadBroadCastReceiver mDownloadBroadCastReceiver = null;
Kim Hansen086964d2013-12-10 11:33:28 +000080
Jose Pascoal1ee56e22014-05-21 16:58:20 +010081 private static final int MAX_DOWNLOAD_RETRIES = 3;
82 private int mDownloadRetries;
Jose Pascoal810950b2014-10-09 17:16:08 +010083 private long mLatestFileDownloadId;
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +000084 private boolean mInternetConnectionAvailable;
Kim Hansen086964d2013-12-10 11:33:28 +000085
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000086 private SharedPreferences mSharedPreferences;
Jose Pascoal810950b2014-10-09 17:16:08 +010087
Jose Pascoalb03b55c2015-02-19 17:34:07 +000088 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 +000089 private BroadcastReceiver networkStateReceiver;
Jose Pascoal29ee1012014-09-12 17:25:36 +010090
Filipe72b5a7f2015-09-01 08:59:54 +000091 @Override
Jose Pascoal810950b2014-10-09 17:16:08 +010092 public int onStartCommand(Intent intent, int flags, int startId)
93 {
Jose Pascoal810950b2014-10-09 17:16:08 +010094 // remove the logs
95 clearDataLogs();
Jose Pascoalda00b382015-01-20 18:39:28 +000096
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000097 if(Utils.isDeviceUnsupported(getApplicationContext())){
Jose Pascoalda00b382015-01-20 18:39:28 +000098 stopSelf();
99 return START_NOT_STICKY;
100 }
Jose Pascoal29ee1012014-09-12 17:25:36 +0100101
Jose Pascoal7bf83a02014-10-13 18:30:18 +0100102 mSharedPreferences = getApplicationContext().getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
Jose Pascoal29ee1012014-09-12 17:25:36 +0100103
Jose Pascoal810950b2014-10-09 17:16:08 +0100104 mLatestFileDownloadId = mSharedPreferences.getLong(PREFERENCE_LAST_CONFIG_DOWNLOAD_ID, 0);
Tiago Costa87925fe2014-12-02 17:57:51 +0000105
Jose Pascoalaa579a82014-11-05 22:17:16 +0000106 setupDownloadManager();
Tiago Costa87925fe2014-12-02 17:57:51 +0000107
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000108 setupConnectivityMonitoring();
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100109
Maarten Derks0d5083c2016-05-31 14:38:26 +0200110 if(Utils.isInternetEnabled(getApplicationContext()))
Jose Pascoal810950b2014-10-09 17:16:08 +0100111 {
Filipe72b5a7f2015-09-01 08:59:54 +0000112 downloadConfigFile(intent != null && intent.getBooleanExtra(EXTRA_FORCE_CONFIG_FILE_DOWNLOAD, false));
Jose Pascoal810950b2014-10-09 17:16:08 +0100113 }
114
115 // setup the gapps installer
Jose Pascoal40916302015-02-06 18:43:47 +0000116 GappsInstallerHelper.checkGappsAreInstalled(getApplicationContext());
Jose Pascoal810950b2014-10-09 17:16:08 +0100117
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000118 runInstallationDisclaimer(getApplicationContext());
Tiago Costa87925fe2014-12-02 17:57:51 +0000119
Jose Pascoal810950b2014-10-09 17:16:08 +0100120 return Service.START_STICKY;
121 }
122
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000123 private static void runInstallationDisclaimer(Context context)
Tiago Costa87925fe2014-12-02 17:57:51 +0000124 {
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000125 SharedPreferences sharedPreferences = context.getApplicationContext().getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
126 if (sharedPreferences.getBoolean(PREFERENCE_REINSTALL_GAPPS, true) && !UpdaterData.getInstance().isAppStoreListEmpty())
Tiago Costa87925fe2014-12-02 17:57:51 +0000127 {
Tiago Costa87925fe2014-12-02 17:57:51 +0000128 if(!GappsInstallerHelper.areGappsInstalled()){
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000129 showReinstallAlert(context);
Tiago Costa87925fe2014-12-02 17:57:51 +0000130 }
131
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000132 Editor editor = sharedPreferences.edit();
Tiago Costa87925fe2014-12-02 17:57:51 +0000133 editor.putBoolean(PREFERENCE_REINSTALL_GAPPS, false);
134
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000135 editor.apply();
Tiago Costa87925fe2014-12-02 17:57:51 +0000136 }
137 }
138
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000139 private static void showReinstallAlert(Context context)
Tiago Costa87925fe2014-12-02 17:57:51 +0000140 {
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000141 if ( FairphoneUpdater.BETA_MODE_ENABLED )
Jose Pascoal40916302015-02-06 18:43:47 +0000142 {
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000143 return;
Jose Pascoal40916302015-02-06 18:43:47 +0000144 }
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000145
146 NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Tiago Costa87925fe2014-12-02 17:57:51 +0000147
Tiago Costa73575aa2015-01-19 15:48:26 +0000148 //Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getResources().getString(R.string.supportAppStoreUrl)));
Tiago Costa87925fe2014-12-02 17:57:51 +0000149
Tiago Costa73575aa2015-01-19 15:48:26 +0000150 Intent notificationIntent = new Intent(context, FairphoneUpdater.class);
151 notificationIntent.setAction(GappsInstallerHelper.EXTRA_START_GAPPS_INSTALL);
Tiago Costa87925fe2014-12-02 17:57:51 +0000152
Tiago Costa73575aa2015-01-19 15:48:26 +0000153 PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000154
telmo.agostinho1378e442016-01-22 15:50:04 +0000155 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.updater_white)
Filipe72b5a7f2015-09-01 08:59:54 +0000156 .setContentTitle(context.getResources().getString(R.string.app_full_name))
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000157 .setContentText(context.getResources().getString(R.string.appStoreReinstall))
158 .setAutoCancel(true)
159 .setDefaults(Notification.DEFAULT_SOUND)
160 .setContentIntent(contentIntent);
Tiago Costa87925fe2014-12-02 17:57:51 +0000161
162 mNotificationManager.notify(0, mBuilder.build());
163 }
164
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000165 private void downloadConfigFile(boolean forceDownload)
Jose Pascoal810950b2014-10-09 17:16:08 +0100166 {
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000167 long now = System.currentTimeMillis();
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000168 long last_download = mSharedPreferences.getLong(LAST_CONFIG_DOWNLOAD_IN_MS, 0L);
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000169 if( forceDownload || now > (last_download + DOWNLOAD_GRACE_PERIOD_IN_MS) ) {
Filipe72b5a7f2015-09-01 08:59:54 +0000170 Log.i(TAG, "Downloading updater configuration file.");
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000171 // remove the old file if its still there for some reason
172 removeLatestFileDownload(getApplicationContext());
173
174 // start the download of the latest file
175 startDownloadLatest();
Jose Pascoal810950b2014-10-09 17:16:08 +0100176
Jose Pascoal0b48f8d2015-02-06 16:06:41 +0000177 mSharedPreferences.edit().putLong(LAST_CONFIG_DOWNLOAD_IN_MS, now).apply();
Filipe Gonçalves8f226b02014-12-12 11:21:58 +0000178 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100179 }
180
Jose Pascoalcdd82042015-02-06 13:04:26 +0000181// --Commented out by Inspection START (06/02/2015 12:27):
182// public void updateGoogleAppsIntallerWidgets()
183// {
184// AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
185// int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(this, GoogleAppsInstallerWidget.class));
186// if (appWidgetIds.length > 0)
187// {
188// new GoogleAppsInstallerWidget().onUpdate(this, appWidgetManager, appWidgetIds);
189// }
190// }
191// --Commented out by Inspection STOP (06/02/2015 12:27)
Jose Pascoal810950b2014-10-09 17:16:08 +0100192
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000193 private static void clearDataLogs()
Jose Pascoal810950b2014-10-09 17:16:08 +0100194 {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000195 if (PrivilegeChecker.isPrivilegedApp()) {
196 clearDataLogsPrivileged();
197 } else {
198 clearDataLogsUnprivileged();
Tiago Costac14ea242014-04-24 10:14:41 +0100199 }
200 }
Kim Hansen086964d2013-12-10 11:33:28 +0000201
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000202 private static void clearDataLogsPrivileged()
203 {
204 try
205 {
206 Log.d(TAG, "Clearing dump log data...");
207 Process p = Runtime.getRuntime().exec("rm /data/log_other_mode/*_log");
208 try
209 {
210 p.waitFor();
211 } catch (InterruptedException e)
212 {
213 e.printStackTrace();
214 }
215 } catch (IOException e)
216 {
217 Log.d(TAG, "Clearing dump log data failed: " + e.getLocalizedMessage());
218 }
219 }
220
221 private static void clearDataLogsUnprivileged()
222 {
223 try
224 {
225 Log.d(TAG, "Clearing dump log data...");
226 Shell.runCommand(new CommandCapture(0, "rm /data/log_other_mode/*_log"));
227 } catch (IOException | TimeoutException e)
228 {
229 Log.d(TAG, "Clearing dump log data failed: " + e.getLocalizedMessage());
230 }
231 }
232
233
Jose Pascoal810950b2014-10-09 17:16:08 +0100234 @Override
235 public IBinder onBind(Intent intent)
236 {
237 return null;
238 }
Kim Hansen086964d2013-12-10 11:33:28 +0000239
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000240 void startDownloadLatest()
Jose Pascoal810950b2014-10-09 17:16:08 +0100241 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000242 Resources resources = getApplicationContext().getResources();
243 String downloadLink = getConfigDownloadLink(getApplicationContext());
244 // set the download for the latest version on the download manager
245 Request request = createDownloadRequest(downloadLink, resources.getString(R.string.configFilename) + resources.getString(R.string.config_zip));
Maarten Derks5c3014d2016-06-16 09:12:24 +0200246 request.setNotificationVisibility(Request.VISIBILITY_HIDDEN);
Jose Pascoalfd3e43c2014-05-22 20:29:17 +0100247
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000248 if (request != null && mDownloadManager != null)
249 {
Maarten Derks0d5083c2016-05-31 14:38:26 +0200250 // Allow download over mobile data and Wi-Fi
251 request.setAllowedNetworkTypes(Request.NETWORK_MOBILE|Request.NETWORK_WIFI);
252
Jose Pascoal87758742015-01-28 20:00:22 +0000253 //Guarantee that only we have only one download
254 long oldDownloadId = mSharedPreferences.getLong(PREFERENCE_LAST_CONFIG_DOWNLOAD_ID, 0);
255 if(oldDownloadId != 0){
256 mDownloadManager.remove(oldDownloadId);
257 saveLatestDownloadId(0);
258 }
259
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000260 mLatestFileDownloadId = mDownloadManager.enqueue(request);
261 saveLatestDownloadId(mLatestFileDownloadId);
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +0000262
263 final long currentId = mLatestFileDownloadId;
264 // Cancel download if it is stuck since DownloadManager doesn't seem able to do it.
265 new Handler().postAtTime(new Runnable() {
266 @Override
267 public void run() {
Jose Pascoal46fdb062015-02-05 18:59:32 +0000268 onDownloadStatus(currentId, new Runnable() {
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000269 @Override
270 public void run() {
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +0000271 Log.w(TAG, "Configuration file download timed out");
272 mDownloadManager.remove(currentId);
Jose Pascoal0b48f8d2015-02-06 16:06:41 +0000273 mSharedPreferences.edit().remove(LAST_CONFIG_DOWNLOAD_IN_MS).apply();
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +0000274 }
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000275 });
Filipe Gonçalvesb2536c02015-01-12 11:50:25 +0000276 }
Filipe Gonçalves8b263ad2015-01-12 12:30:11 +0000277 }, SystemClock.uptimeMillis() + CONFIG_FILE_DOWNLOAD_TIMEOUT_MILLIS);
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000278 }
279 else
280 {
281 Log.e(TAG, "Invalid request for link " + downloadLink);
282 Intent i = new Intent(FairphoneUpdater.FAIRPHONE_UPDATER_CONFIG_DOWNLOAD_FAILED);
283 i.putExtra(FairphoneUpdater.FAIRPHONE_UPDATER_CONFIG_DOWNLOAD_LINK, downloadLink);
284 sendBroadcast(i);
Jose Pascoal810950b2014-10-09 17:16:08 +0100285 }
286 }
287
Jose Pascoalc2545cc2014-12-18 16:51:52 +0000288 private void saveLatestDownloadId(long id)
289 {
290 mLatestFileDownloadId = id;
291 Editor editor = mSharedPreferences.edit();
292 editor.putLong(PREFERENCE_LAST_CONFIG_DOWNLOAD_ID, id);
293 editor.commit();
294 }
295
Jose Pascoal810950b2014-10-09 17:16:08 +0100296 private String getConfigDownloadLink(Context context)
297 {
298
299 Resources resources = context.getResources();
300
301 StringBuilder sb = new StringBuilder();
Filipe Gonçalvesafeac2c2015-01-27 17:49:53 +0000302 String download_url = mSharedPreferences.getString(FairphoneUpdater.PREFERENCE_OTA_DOWNLOAD_URL, getResources().getString(R.string.downloadUrl));
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000303
304 sb.append(download_url);
Jose Pascoal810950b2014-10-09 17:16:08 +0100305 sb.append(Build.MODEL.replaceAll("\\s", ""));
Jose Pascoal0a5be012014-11-17 16:55:40 +0000306 sb.append(Utils.getPartitionDownloadPath(resources));
Jose Pascoal810950b2014-10-09 17:16:08 +0100307 sb.append("/");
308
309 sb.append(resources.getString(R.string.configFilename));
310
311 sb.append(resources.getString(R.string.config_zip));
312
Maarten Derks1f59c9e2016-04-19 11:38:41 +0200313 addUrlParameters(context, sb);
Jose Pascoal810950b2014-10-09 17:16:08 +0100314
315 String downloadLink = sb.toString();
316
317 Log.d(TAG, "Download link: " + downloadLink);
318
319 return downloadLink;
320 }
Jose Pascoalcf13fde2014-05-21 20:17:17 +0100321
Maarten Derks1f59c9e2016-04-19 11:38:41 +0200322 private static void addUrlParameters(Context context, StringBuilder sb)
Tiago Costab24ef1c2014-07-25 12:02:34 +0100323 {
Jose Pascoal810950b2014-10-09 17:16:08 +0100324 sb.append("?");
Jose Pascoal810950b2014-10-09 17:16:08 +0100325 Version currentVersion = VersionParserHelper.getDeviceVersion(context.getApplicationContext());
326
Maarten Derks1f59c9e2016-04-19 11:38:41 +0200327 if (currentVersion != null) {
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000328 try {
329 final String defaultCharset = Charset.defaultCharset().displayName();
Maarten Derks1f59c9e2016-04-19 11:38:41 +0200330
331 String modelWithoutSpaces = Build.MODEL.replaceAll("\\s", "");
332 if(modelWithoutSpaces.startsWith(context.getResources().getString(R.string.FP1Model))) {
333 sb.append("&b_n=").append(URLEncoder.encode(currentVersion.getBuildNumber(), defaultCharset));
334 }
Maarten Derksf17e6db2016-02-09 15:10:53 +0100335 sb.append("&ota_v_n=").append(URLEncoder.encode(String.valueOf(currentVersion.getId()), defaultCharset));
Maarten Derks4dc5b032016-05-26 17:04:58 +0200336 sb.append("&beta=").append(FairphoneUpdater.BETA_MODE_ENABLED ? BetaEnabler.BETA_ENABLED : BetaEnabler.BETA_DISABLED);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000337 sb.append("&dev=").append(FairphoneUpdater.DEV_MODE_ENABLED ? "1" : "0");
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000338 } catch (UnsupportedEncodingException e) {
339 Log.e(TAG, "Failed to add extra info on update request: "+e.getLocalizedMessage());
340 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100341 }
Tiago Costab24ef1c2014-07-25 12:02:34 +0100342 }
343
Jose Pascoal810950b2014-10-09 17:16:08 +0100344 private static void setNotification(Context currentContext)
345 {
Kim Hansen086964d2013-12-10 11:33:28 +0000346
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000347 if ( FairphoneUpdater.BETA_MODE_ENABLED )
Jose Pascoal40916302015-02-06 18:43:47 +0000348 {
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000349 return;
Jose Pascoal40916302015-02-06 18:43:47 +0000350 }
Filipe Gonçalves825414a2015-01-26 14:52:48 +0000351
Jose Pascoal810950b2014-10-09 17:16:08 +0100352 Context context = currentContext.getApplicationContext();
Kim Hansen086964d2013-12-10 11:33:28 +0000353
Jose Pascoal810950b2014-10-09 17:16:08 +0100354 NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Kim Hansen086964d2013-12-10 11:33:28 +0000355
Jose Pascoal810950b2014-10-09 17:16:08 +0100356 NotificationCompat.Builder builder =
telmo.agostinho1378e442016-01-22 15:50:04 +0000357 new NotificationCompat.Builder(context).setSmallIcon(R.drawable.updater_white)
Filipe72b5a7f2015-09-01 08:59:54 +0000358 .setContentTitle(context.getResources().getString(R.string.app_full_name))
Pedro Arelo773bd822014-10-10 11:57:34 +0100359 .setContentText(context.getResources().getString(R.string.fairphone_update_message));
Kim Hansen086964d2013-12-10 11:33:28 +0000360
Jose Pascoal7bf83a02014-10-13 18:30:18 +0100361 Intent resultIntent = new Intent(context, FairphoneUpdater.class);
Jose Pascoal810950b2014-10-09 17:16:08 +0100362 TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
Kim Hansen086964d2013-12-10 11:33:28 +0000363
Jose Pascoal7bf83a02014-10-13 18:30:18 +0100364 stackBuilder.addParentStack(FairphoneUpdater.class);
Kim Hansen086964d2013-12-10 11:33:28 +0000365
Jose Pascoal810950b2014-10-09 17:16:08 +0100366 stackBuilder.addNextIntent(resultIntent);
367 PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
Kim Hansen086964d2013-12-10 11:33:28 +0000368
Jose Pascoal810950b2014-10-09 17:16:08 +0100369 builder.setContentIntent(resultPendingIntent);
Kim Hansen086964d2013-12-10 11:33:28 +0000370
Jose Pascoal810950b2014-10-09 17:16:08 +0100371 Notification notificationWhileRunnig = builder.build();
Jose Pascoalfd3e43c2014-05-22 20:29:17 +0100372
Jose Pascoal810950b2014-10-09 17:16:08 +0100373 // Add notification
374 manager.notify(0, notificationWhileRunnig);
375 }
Kim Hansen086964d2013-12-10 11:33:28 +0000376
Jose Pascoal810950b2014-10-09 17:16:08 +0100377 private Request createDownloadRequest(String url, String fileName)
378 {
Kim Hansen086964d2013-12-10 11:33:28 +0000379
Jose Pascoal810950b2014-10-09 17:16:08 +0100380 Resources resources = getApplicationContext().getResources();
381 Request request;
Kim Hansen086964d2013-12-10 11:33:28 +0000382
Jose Pascoal810950b2014-10-09 17:16:08 +0100383 try
384 {
385 request = new Request(Uri.parse(url));
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000386 final File externalStoragePublicDirectory = Environment.getExternalStoragePublicDirectory(Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder));
Jose Pascoal46fdb062015-02-05 18:59:32 +0000387 final boolean notMkDirs = !externalStoragePublicDirectory.mkdirs();
388 if(notMkDirs && !externalStoragePublicDirectory.exists()) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000389 throw new Exception("Couldn't create updater dir structures.");
390 }
Kim Hansen086964d2013-12-10 11:33:28 +0000391
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000392 request.setDestinationInExternalPublicDir(resources.getString(R.string.updaterFolder), fileName);
Jose Pascoal810950b2014-10-09 17:16:08 +0100393 request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
394 request.setAllowedOverRoaming(false);
Pedro Arelo773bd822014-10-10 11:57:34 +0100395 request.setTitle(resources.getString(R.string.fairphone_update_message_title));
Jose Pascoal810950b2014-10-09 17:16:08 +0100396 } catch (Exception e)
397 {
398 Log.e(TAG, "Error creating request: " + e.getMessage());
399 request = null;
400 }
Kim Hansen086964d2013-12-10 11:33:28 +0000401
Jose Pascoal810950b2014-10-09 17:16:08 +0100402 return request;
403 }
Kim Hansen086964d2013-12-10 11:33:28 +0000404
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000405 private void setupConnectivityMonitoring()
406 {
407
Filipe72b5a7f2015-09-01 08:59:54 +0000408 if (networkStateReceiver == null) {
409 // Check current connectivity status
Maarten Derks0d5083c2016-05-31 14:38:26 +0200410 mInternetConnectionAvailable = Utils.isInternetEnabled(getApplicationContext());
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000411
Filipe72b5a7f2015-09-01 08:59:54 +0000412 // Setup monitoring for future connectivity status changes
413 networkStateReceiver = new BroadcastReceiver()
Tiago Costa87925fe2014-12-02 17:57:51 +0000414 {
Filipe72b5a7f2015-09-01 08:59:54 +0000415 @Override
416 public void onReceive(Context context, Intent intent)
Tiago Costa87925fe2014-12-02 17:57:51 +0000417 {
Filipe72b5a7f2015-09-01 08:59:54 +0000418 if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
419 Log.i(TAG, "Lost network connectivity.");
420 mInternetConnectionAvailable = false;
421 if (mLatestFileDownloadId != 0 && mDownloadManager != null)
422 {
423 onDownloadStatus(mLatestFileDownloadId, new Runnable() {
424 @Override
425 public void run() {
426 Log.d(TAG, "Removing pending download.");
427 mDownloadManager.remove(mLatestFileDownloadId);
428 saveLatestDownloadId(0);
429 }
430 });
431 }
432 }
433 else
Tiago Costa87925fe2014-12-02 17:57:51 +0000434 {
Filipe72b5a7f2015-09-01 08:59:54 +0000435 int conn_type = intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_DUMMY);
Maarten Derks0d5083c2016-05-31 14:38:26 +0200436 if( conn_type == ConnectivityManager.TYPE_WIFI || conn_type == ConnectivityManager.TYPE_MOBILE) {
Filipe72b5a7f2015-09-01 08:59:54 +0000437 Log.i(TAG, "Network connectivity potentially available.");
438 if (!mInternetConnectionAvailable) {
439 downloadConfigFile(false);
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000440 }
Filipe72b5a7f2015-09-01 08:59:54 +0000441 mInternetConnectionAvailable = true;
442 }
Tiago Costa87925fe2014-12-02 17:57:51 +0000443 }
444 }
Filipe72b5a7f2015-09-01 08:59:54 +0000445 };
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000446
Filipe72b5a7f2015-09-01 08:59:54 +0000447 IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
448 registerReceiver(networkStateReceiver, filter);
Tiago Costa87925fe2014-12-02 17:57:51 +0000449
Filipe72b5a7f2015-09-01 08:59:54 +0000450 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100451 }
452
453 private void setupDownloadManager()
454 {
455 if (mDownloadManager == null)
456 {
457 mDownloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
458 }
459
Filipe Gonçalvesd0dfe912014-11-04 16:24:25 +0000460 if (mDownloadBroadCastReceiver == null)
Jose Pascoal810950b2014-10-09 17:16:08 +0100461 {
462 mDownloadBroadCastReceiver = new DownloadBroadCastReceiver();
463
464 getApplicationContext().registerReceiver(mDownloadBroadCastReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
465 }
466 }
467
Filipe72b5a7f2015-09-01 08:59:54 +0000468 private static void copyConfigToData(Context context) throws IOException {
469 Resources resources = context.getApplicationContext().getResources();
470 String targetPath = Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder);
471 FileInputStream inStream = new FileInputStream(targetPath + resources.getString(R.string.configFilename) + resources.getString(R.string.config_xml));
472 FileOutputStream outStream = context.openFileOutput(resources.getString(R.string.configFilename) + resources.getString(R.string.config_xml), MODE_PRIVATE);
473 FileChannel inChannel = inStream.getChannel();
474 FileChannel outChannel = outStream.getChannel();
475 inChannel.transferTo(0, inChannel.size(), outChannel);
476 inStream.close();
477 outStream.close();
478 }
479
Jose Pascoal810950b2014-10-09 17:16:08 +0100480 private static void checkVersionValidation(Context context)
481 {
Jose Pascoal810950b2014-10-09 17:16:08 +0100482 Version latestVersion = VersionParserHelper.getLatestVersion(context.getApplicationContext());
483 Version currentVersion = VersionParserHelper.getDeviceVersion(context.getApplicationContext());
Jose Pascoal810950b2014-10-09 17:16:08 +0100484 if (latestVersion != null)
485 {
486 if (latestVersion.isNewerVersionThan(currentVersion))
487 {
488 setNotification(context);
489 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100490 }
Filipe Gonçalvesa75e1812015-03-19 12:12:07 +0000491 runInstallationDisclaimer(context);
Jose Pascoald5e4d4a2015-03-13 18:17:48 +0000492
493 // to update the activity
494 Intent updateIntent = new Intent(FairphoneUpdater.FAIRPHONE_UPDATER_NEW_VERSION_RECEIVED);
495 context.sendBroadcast(updateIntent);
Jose Pascoal810950b2014-10-09 17:16:08 +0100496 }
497
Filipe72b5a7f2015-09-01 08:59:54 +0000498 public static boolean readUpdaterData(Context context){
499 Version latestVersion = VersionParserHelper.getLatestVersion(context.getApplicationContext());
500 return latestVersion != null;
501 }
502
503 public static boolean updateUpdaterData(Context context)
Jose Pascoal810950b2014-10-09 17:16:08 +0100504 {
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100505
506 boolean retVal = false;
507 Resources resources = context.getApplicationContext().getResources();
Jose Pascoal810950b2014-10-09 17:16:08 +0100508 String targetPath = Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder);
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100509
Jose Pascoal810950b2014-10-09 17:16:08 +0100510 String filePath = targetPath + resources.getString(R.string.configFilename) + resources.getString(R.string.config_zip);
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100511
512 File file = new File(filePath);
513
Jose Pascoal810950b2014-10-09 17:16:08 +0100514 if (file.exists())
515 {
Filipe72b5a7f2015-09-01 08:59:54 +0000516 String md5sum = Utils.calculateMD5(file);
517 SharedPreferences sp = context.getApplicationContext().getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
518 if(sp.getString(PREFERENCE_CONFIG_MD_5, "").equals(md5sum)){
Jose Pascoal810950b2014-10-09 17:16:08 +0100519 retVal = true;
Filipe72b5a7f2015-09-01 08:59:54 +0000520 } else {
521 if (RSAUtils.checkFileSignature(context, filePath, targetPath)) {
522 try {
523 copyConfigToData(context);
524 checkVersionValidation(context);
525 retVal = true;
526 sp.edit().putString(PREFERENCE_CONFIG_MD_5, md5sum).apply();
527 } catch (IOException e) {
528 Log.e(TAG, "Failed to store configuration " + e.getLocalizedMessage());
529 retVal = false;
530 }
531 } else {
532 //Toast.makeText(context, resources.getString(R.string.invalid_signature_download_message), Toast.LENGTH_LONG).show();
533 final boolean notDeleted = !file.delete();
534 if(notDeleted) {
535 Log.d(TAG, "Unable to delete "+file.getAbsolutePath());
536 }
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000537
Filipe72b5a7f2015-09-01 08:59:54 +0000538 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100539 }
Filipe72b5a7f2015-09-01 08:59:54 +0000540 } else {
Filipe Gonçalves1b736622016-05-11 14:49:16 +0100541 Log.w(TAG, "No file");
Jose Pascoal810950b2014-10-09 17:16:08 +0100542 }
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100543
544 return retVal;
545 }
Kim Hansen086964d2013-12-10 11:33:28 +0000546
Jose Pascoal810950b2014-10-09 17:16:08 +0100547 private void removeLatestFileDownload(Context context)
548 {
549 if (mLatestFileDownloadId != 0 && mDownloadManager != null)
550 {
551 mDownloadManager.remove(mLatestFileDownloadId);
Jose Pascoalc2545cc2014-12-18 16:51:52 +0000552 saveLatestDownloadId(0);
Kim Hansen086964d2013-12-10 11:33:28 +0000553 }
Jose Pascoalc2545cc2014-12-18 16:51:52 +0000554 VersionParserHelper.removeConfigFiles(context);
Kim Hansen086964d2013-12-10 11:33:28 +0000555 }
556
Jose Pascoal810950b2014-10-09 17:16:08 +0100557 private boolean retryDownload(Context context)
558 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000559 Log.d(TAG, "Retry "+mDownloadRetries+" of "+MAX_DOWNLOAD_RETRIES);
Jose Pascoal810950b2014-10-09 17:16:08 +0100560 // invalid file
561 boolean removeReceiver = true;
562 removeLatestFileDownload(context);
563 if (mDownloadRetries < MAX_DOWNLOAD_RETRIES)
564 {
565 startDownloadLatest();
566 mDownloadRetries++;
567 removeReceiver = false;
568 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100569 return removeReceiver;
570 }
Jose Pascoal0e966282014-08-12 18:49:05 +0100571
Jose Pascoal46fdb062015-02-05 18:59:32 +0000572 private void onDownloadStatus(long id, Runnable ifRunning) {
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000573 Cursor cursor = mDownloadManager != null ? mDownloadManager.query(new DownloadManager.Query().setFilterById(id)) : null;
574 if (cursor != null && cursor.moveToFirst())
575 {
576 int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
577 switch(status){
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000578 case DownloadManager.STATUS_PAUSED:
579 if (ifRunning != null) {
580 ifRunning.run();
581 }
582 break;
583 case DownloadManager.STATUS_PENDING:
584 if (ifRunning != null) {
585 ifRunning.run();
586 }
587 break;
588 case DownloadManager.STATUS_RUNNING:
589 if (ifRunning != null) {
590 ifRunning.run();
591 }
592 break;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000593 case DownloadManager.STATUS_FAILED:
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000594 case DownloadManager.STATUS_SUCCESSFUL:
Filipe Gonçalves712d5482015-01-13 17:00:44 +0000595 default:
596 break;
597 }
598 }
599 if (cursor != null)
600 {
601 cursor.close();
602 }
603 }
604
Jose Pascoal810950b2014-10-09 17:16:08 +0100605 private class DownloadBroadCastReceiver extends BroadcastReceiver
606 {
Kim Hansen086964d2013-12-10 11:33:28 +0000607
Jose Pascoal1ee56e22014-05-21 16:58:20 +0100608 @Override
Jose Pascoal810950b2014-10-09 17:16:08 +0100609 public void onReceive(Context context, Intent intent)
610 {
Kim Hansen086964d2013-12-10 11:33:28 +0000611
Jose Pascoal810950b2014-10-09 17:16:08 +0100612 boolean removeReceiver = false;
Jose Pascoal810950b2014-10-09 17:16:08 +0100613 DownloadManager.Query query = new DownloadManager.Query();
614
615 query.setFilterById(mLatestFileDownloadId);
Jose Pascoal810950b2014-10-09 17:16:08 +0100616
Jose Pascoalaa579a82014-11-05 22:17:16 +0000617 Cursor cursor = mDownloadManager != null ? mDownloadManager.query(query) : null;
618
619 if (cursor != null && cursor.moveToFirst())
Jose Pascoal810950b2014-10-09 17:16:08 +0100620 {
621 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
622 int status = cursor.getInt(columnIndex);
623 Resources resources = context.getApplicationContext().getResources();
624
625 switch (status)
626 {
627 case DownloadManager.STATUS_SUCCESSFUL:
628 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000629 Log.d(TAG, "Download successful.");
Jose Pascoal810950b2014-10-09 17:16:08 +0100630
Filipe72b5a7f2015-09-01 08:59:54 +0000631 if (updateUpdaterData(context))
Jose Pascoal810950b2014-10-09 17:16:08 +0100632 {
Filipe72b5a7f2015-09-01 08:59:54 +0000633 removeLatestFileDownload(context);
Jose Pascoal810950b2014-10-09 17:16:08 +0100634 }
635 else
636 {
Pedro Arelo773bd822014-10-10 11:57:34 +0100637 Toast.makeText(getApplicationContext(), resources.getString(R.string.invalid_signature_download_message), Toast.LENGTH_LONG).show();
Jose Pascoal810950b2014-10-09 17:16:08 +0100638 removeReceiver = retryDownload(context);
639 }
640 break;
641 }
642 case DownloadManager.STATUS_FAILED:
643 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000644 Log.d(TAG, "Download failed.");
Jose Pascoal810950b2014-10-09 17:16:08 +0100645 removeReceiver = retryDownload(context);
646 break;
647 }
Filipe Gonçalves42ffc322015-01-28 12:51:14 +0000648 default:
649 {
650 Log.d(TAG, "Status broadcast on mLatestFileDownloadId ("+mLatestFileDownloadId+"): "+ status);
651 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100652 }
653 }
Tiago Costa87925fe2014-12-02 17:57:51 +0000654
Jose Pascoalaa579a82014-11-05 22:17:16 +0000655 if (cursor != null)
656 {
657 cursor.close();
658 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100659
660 if (removeReceiver)
661 {
Filipe Gonçalves5b65c012015-01-08 13:49:42 +0000662 Log.d(TAG, "Configuration download failed. Clearing grace period.");
Jose Pascoal0b48f8d2015-02-06 16:06:41 +0000663 mSharedPreferences.edit().remove(LAST_CONFIG_DOWNLOAD_IN_MS).apply();
Jose Pascoal810950b2014-10-09 17:16:08 +0100664 }
665 }
666 }
telmo.agostinho1378e442016-01-22 15:50:04 +0000667}