blob: cd757dc4460c22d15ad43882a3e1ba49d6c1376d [file] [log] [blame]
Jose Pascoal02b849e2014-06-26 17:07:51 +01001/*
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.tools;
18
Maarten Derksa02f4f12016-05-26 17:04:58 +020019import android.app.Activity;
20import android.app.AlarmManager;
Jose Pascoal4fe7ba52015-03-13 16:54:31 +000021import android.app.DownloadManager;
Maarten Derksa02f4f12016-05-26 17:04:58 +020022import android.app.PendingIntent;
Jose Pascoal61e24762014-09-30 19:44:36 +010023import android.content.Context;
Jose Pascoale4c48e32014-10-03 19:55:39 +010024import android.content.Intent;
Maarten Derkse2f0ffc2016-04-26 11:58:36 +020025import android.content.IntentFilter;
Maarten Derksa02f4f12016-05-26 17:04:58 +020026import android.content.SharedPreferences;
Maarten Derks417de7a2016-08-25 17:27:59 +020027import android.content.pm.PackageManager;
Jose Pascoal0a5be012014-11-17 16:55:40 +000028import android.content.res.Resources;
Jose Pascoal2a8283b2015-03-13 14:43:42 +000029import android.database.Cursor;
Filipe Gonçalves72817782015-03-19 15:29:05 +000030import android.net.ConnectivityManager;
Maarten Derksbf5f51c2016-05-31 14:38:26 +020031import android.net.NetworkInfo;
Jose Pascoal2a8283b2015-03-13 14:43:42 +000032import android.net.Uri;
Maarten Derkse2f0ffc2016-04-26 11:58:36 +020033import android.os.BatteryManager;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010034import android.os.Build;
Jose Pascoal0a5be012014-11-17 16:55:40 +000035import android.os.Environment;
Jose Pascoal49f058a2015-02-13 16:58:01 +000036import android.os.PowerManager;
Jose Pascoalda00b382015-01-20 18:39:28 +000037import android.text.TextUtils;
Jose Pascoal02b849e2014-06-26 17:07:51 +010038import android.util.Log;
Jose Pascoal49f058a2015-02-13 16:58:01 +000039import android.widget.Toast;
Jose Pascoal02b849e2014-06-26 17:07:51 +010040
Maarten Derksa02f4f12016-05-26 17:04:58 +020041import com.fairphone.updater.FairphoneUpdater;
Jose Pascoal0a5be012014-11-17 16:55:40 +000042import com.fairphone.updater.R;
Jose Pascoale4c48e32014-10-03 19:55:39 +010043import com.fairphone.updater.UpdaterService;
Jose Pascoal02d86242014-12-17 18:50:08 +000044import com.fairphone.updater.data.DownloadableItem;
45import com.fairphone.updater.data.Store;
Jose Pascoal055562d2015-03-19 19:31:38 +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;
Filipe Gonçalves837e1572015-01-23 17:52:35 +000049import com.stericson.RootTools.RootTools;
50import com.stericson.RootTools.exceptions.RootDeniedException;
51import com.stericson.RootTools.execution.CommandCapture;
52import com.stericson.RootTools.execution.Shell;
Jose Pascoal61e24762014-09-30 19:44:36 +010053
Tiago Costad7e951f2015-03-02 17:37:33 +000054import java.io.DataOutputStream;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000055import java.io.File;
56import java.io.FileInputStream;
57import java.io.FileNotFoundException;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000058import java.io.FileOutputStream;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000059import java.io.IOException;
60import java.io.InputStream;
Jose Pascoal49f058a2015-02-13 16:58:01 +000061import java.io.PrintWriter;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000062import java.math.BigInteger;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000063import java.nio.channels.FileChannel;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000064import java.security.MessageDigest;
65import java.security.NoSuchAlgorithmException;
Filipe72b5a7f2015-09-01 08:59:54 +000066import java.util.HashMap;
67import java.util.Map;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000068import java.util.NoSuchElementException;
69import java.util.Scanner;
70import java.util.concurrent.TimeoutException;
Filipe72b5a7f2015-09-01 08:59:54 +000071import java.util.regex.Matcher;
72import java.util.regex.Pattern;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000073
Jose Pascoal810950b2014-10-09 17:16:08 +010074public class Utils
75{
Jose Pascoal810950b2014-10-09 17:16:08 +010076 private static final String TAG = Utils.class.getSimpleName();
Jose Pascoal46fdb062015-02-05 18:59:32 +000077 private static final int DELAY_100_MILLIS = 100;
78 public static final int DELAY_HALF_SECOND = 500;
Jose Pascoalcfc2dd42015-02-09 18:00:05 +000079 public static final long SECONDS_IN_MINUTE = 60L;
80 public static final long MINUTES_IN_HOUR = 60L;
Jose Pascoal46fdb062015-02-05 18:59:32 +000081
82 private static final double BUFFER_1024_BYTES = 1024d;
Jose Pascoalcdd82042015-02-06 13:04:26 +000083 // --Commented out by Inspection (06/02/2015 12:27):public static final int BUFFER_SIZE_4_KBYTES = 4096;
Jose Pascoal46fdb062015-02-05 18:59:32 +000084 public static final int BUFFER_SIZE_2_KBYTES = 2048;
85 private static final int BUFFER_SIZE_8_KBYTES = 8192;
86 public static final int BUFFER_SIZE_10_MBYTES = 10240;
87 private static final int RADIX_BASE_16 = 16;
88 private static final double PERCENT_100 = 100d;
Jose Pascoalcfc2dd42015-02-09 18:00:05 +000089 private static final char CHAR_SPACE = ' ';
90 private static final char CHAR_ZERO = '0';
Maarten Derksbb287762015-10-12 17:22:45 +020091 public static final String GAPPS_STORE_NUMBER = "0";
Jose Pascoal02b849e2014-06-26 17:07:51 +010092
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000093 private static double getPartitionSizeInGBytes(File path)
Jose Pascoal810950b2014-10-09 17:16:08 +010094 {
95 double availableBlocks = getPartitionSizeInBytes(path);
Jose Pascoal46fdb062015-02-05 18:59:32 +000096 double sizeInGB = ((availableBlocks / BUFFER_1024_BYTES) / BUFFER_1024_BYTES) / BUFFER_1024_BYTES;
Jose Pascoal810950b2014-10-09 17:16:08 +010097 Log.d(TAG, path.getPath() + " size(GB): " + sizeInGB);
98 return sizeInGB;
99 }
Jose Pascoal02b849e2014-06-26 17:07:51 +0100100
Jose Pascoalcdd82042015-02-06 13:04:26 +0000101// --Commented out by Inspection START (06/02/2015 12:26):
102// public static double getPartitionSizeInMBytes(File path)
103// {
104// double availableBlocks = getPartitionSizeInBytes(path);
105// double sizeInMB = ((availableBlocks / BUFFER_1024_BYTES)) / BUFFER_1024_BYTES;
106// return sizeInMB;
107// }
108// --Commented out by Inspection STOP (06/02/2015 12:26)
Jose Pascoale4c48e32014-10-03 19:55:39 +0100109
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000110 private static long getPartitionSizeInBytes(File path)
Jose Pascoal810950b2014-10-09 17:16:08 +0100111 {
112 android.os.StatFs stat = new android.os.StatFs(path.getPath());
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000113 long blockSize, blockCount;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000114 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000115 blockSize = stat.getBlockSizeLong();
116 blockCount = stat.getBlockCountLong();
117 } else {
118 // deprectation warnings disabled due to the need to support SDK 17 (FP1)
119 //noinspection deprecation
120 blockSize = stat.getBlockSize();
121 //noinspection deprecation
122 blockCount = stat.getBlockCount();
123 }
124 return blockCount * blockSize;
Jose Pascoal810950b2014-10-09 17:16:08 +0100125 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100126
Jose Pascoal810950b2014-10-09 17:16:08 +0100127 public static long getAvailablePartitionSizeInBytes(File path)
128 {
129 android.os.StatFs stat = new android.os.StatFs(path.getPath());
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000130
131 long blockSize, blockCount;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000132 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000133 blockSize = stat.getBlockSizeLong();
134 blockCount = stat.getAvailableBlocksLong();
135 } else {
136 // deprectation warnings disabled due to the need to support SDK 17 (FP1)
137 //noinspection deprecation
138 blockSize = stat.getBlockSize();
139 //noinspection deprecation
140 blockCount = stat.getAvailableBlocks();
141 }
142 return blockCount * blockSize;
Jose Pascoal810950b2014-10-09 17:16:08 +0100143 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100144
Jose Pascoal810950b2014-10-09 17:16:08 +0100145 public static void startUpdaterService(Context context, boolean forceDownload)
146 {
Filipe72b5a7f2015-09-01 08:59:54 +0000147 Intent i = new Intent(context, UpdaterService.class);
148 i.putExtra(UpdaterService.EXTRA_FORCE_CONFIG_FILE_DOWNLOAD, forceDownload);
149 context.startService(i);
Jose Pascoal810950b2014-10-09 17:16:08 +0100150 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100151
Jose Pascoalcdd82042015-02-06 13:04:26 +0000152// --Commented out by Inspection START (06/02/2015 12:26):
153// public static void stopUpdaterService(Context context)
154// {
155// boolean isRunning = isServiceRunning(context);
156//
157// if (isRunning)
158// {
159// Log.i(TAG, "Stoping Updater Service...");
160// Intent i = new Intent(context, UpdaterService.class);
161// context.stopService(i);
162// try
163// {
164// Thread.sleep(DELAY_100_MILLIS * 2);
165// } catch (InterruptedException e)
166// {
167// Log.w(TAG, "Stop Updater service delay error: " + e.getLocalizedMessage());
168// }
169// }
170// }
171// --Commented out by Inspection STOP (06/02/2015 12:26)
Jose Pascoale4c48e32014-10-03 19:55:39 +0100172
Jose Pascoal810950b2014-10-09 17:16:08 +0100173 // **************************************************************************************************************
174 // HELPERS
175 // **************************************************************************************************************
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100176
Jose Pascoal810950b2014-10-09 17:16:08 +0100177 public static boolean checkMD5(String md5, File updateFile)
178 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100179
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000180 if (updateFile == null || !updateFile.exists())
Jose Pascoal810950b2014-10-09 17:16:08 +0100181 {
182 return false;
183 }
184
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000185 if (md5 == null || md5.isEmpty())
Jose Pascoal810950b2014-10-09 17:16:08 +0100186 {
187 Log.e(TAG, "MD5 String NULL or UpdateFile NULL");
188 return false;
189 }
190
191 String calculatedDigest = calculateMD5(updateFile);
192 if (calculatedDigest == null)
193 {
194 Log.e(TAG, "calculatedDigest NULL");
195 return false;
196 }
197
198 return calculatedDigest.equalsIgnoreCase(md5);
199 }
200
Filipe72b5a7f2015-09-01 08:59:54 +0000201 public static String calculateMD5(File updateFile)
Jose Pascoal810950b2014-10-09 17:16:08 +0100202 {
203 MessageDigest digest;
204 try
205 {
206 digest = MessageDigest.getInstance("MD5");
207 } catch (NoSuchAlgorithmException e)
208 {
209 Log.e(TAG, "Exception while getting Digest", e);
210 return null;
211 }
212
213 InputStream is;
214 try
215 {
216 is = new FileInputStream(updateFile);
217 } catch (FileNotFoundException e)
218 {
219 Log.e(TAG, "Exception while getting FileInputStream", e);
220 return null;
221 }
222
Jose Pascoal46fdb062015-02-05 18:59:32 +0000223 byte[] buffer = new byte[BUFFER_SIZE_8_KBYTES];
Jose Pascoal810950b2014-10-09 17:16:08 +0100224 int read;
225 try
226 {
227 while ((read = is.read(buffer)) > 0)
228 {
229 digest.update(buffer, 0, read);
230 }
231 byte[] md5sum = digest.digest();
232 BigInteger bigInt = new BigInteger(1, md5sum);
Jose Pascoal46fdb062015-02-05 18:59:32 +0000233 String output = bigInt.toString(RADIX_BASE_16);
Jose Pascoal810950b2014-10-09 17:16:08 +0100234 // Fill to 32 chars
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000235 output = String.format("%32s", output).replace(CHAR_SPACE, CHAR_ZERO);
Jose Pascoal810950b2014-10-09 17:16:08 +0100236 return output;
237 } catch (IOException e)
238 {
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000239 Log.e(TAG, "Error digesting MD5: " + e.getLocalizedMessage());
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000240 return null;
241// throw new RuntimeException("Unable to process file for MD5", e);
Jose Pascoal810950b2014-10-09 17:16:08 +0100242 } finally
243 {
244 try
245 {
246 is.close();
247 } catch (IOException e)
248 {
249 Log.e(TAG, "Exception on closing MD5 input stream", e);
250 }
251 }
252 }
253
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000254 public static void copy(File src, File dst) throws IOException {
255 if (PrivilegeChecker.isPrivilegedApp()) {
256 copyPrivileged(src, dst);
257 } else {
258 copyUnprivileged(src, dst);
259 }
260 }
261
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000262 private static void copyUnprivileged(File src, File dst) throws IOException {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000263 if (RootTools.isAccessGiven()) {
264 RootTools.copyFile(src.getPath(), dst.getPath(), false, false);
265 } else {
266 throw new IOException("No root permissions granted.");
267 }
268 }
269
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000270 private static void copyPrivileged(File src, File dst) throws IOException {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000271 FileInputStream inStream = new FileInputStream(src);
272 FileOutputStream outStream = new FileOutputStream(dst);
273 FileChannel inChannel = inStream.getChannel();
274 FileChannel outChannel = outStream.getChannel();
275 inChannel.transferTo(0, inChannel.size(), outChannel);
276 inStream.close();
277 outStream.close();
278 }
279
Jose Pascoal0a5be012014-11-17 16:55:40 +0000280 public static void clearCache()
281 {
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000282 if(PrivilegeChecker.isPrivilegedApp()) {
283 File f = Environment.getDownloadCacheDirectory();
284 File[] files = f.listFiles();
285 if (files != null) {
286 Log.d(TAG, "Size: " + files.length);
287 for (File file : files) {
288 String filename = file.getName();
Jose Pascoal0a5be012014-11-17 16:55:40 +0000289
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000290 if (filename.endsWith(".zip")) {
291 final boolean delete = file.delete();
292 if (delete) {
293 Log.d(TAG, "Deleted file " + filename);
294 } else {
295 Log.d(TAG, "Failed to delete file " + filename);
296 }
297 }
298 }
299 }
300 } else {
301 if(RootTools.isAccessGiven()) {
302 try {
303 Shell.runRootCommand(new CommandCapture(0, "rm -f *.zip"));
304 } catch (IOException | TimeoutException |RootDeniedException e) {
305 Log.w(TAG, "Failed to clear cache: " + e.getLocalizedMessage());
306 }
307 }
Jose Pascoal0a5be012014-11-17 16:55:40 +0000308 }
309 }
310
311 public static boolean hasUnifiedPartition(Resources resources)
312 {
313 File path = Environment.getDataDirectory();
314 double sizeInGB = Utils.getPartitionSizeInGBytes(path);
Jose Pascoal46fdb062015-02-05 18:59:32 +0000315 double roundedSize = Math.ceil(sizeInGB * PERCENT_100) / PERCENT_100;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000316 Log.d(TAG, "/data size: " + roundedSize + "Gb");
317
Jose Pascoal8d4458f2015-03-17 20:03:59 +0000318 double fp1DataPartitionSize = (double) resources.getInteger(R.integer.FP1DataPartitionSizeMb) / BUFFER_1024_BYTES;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000319 // Add a little buffer to the 1gb default just in case
320 return roundedSize > fp1DataPartitionSize;
321 }
322
323 public static String getPartitionDownloadPath(Resources resources)
324 {
325 String downloadPath = "";
326 if (Build.MODEL.equals(resources.getString(R.string.FP1Model)))
327 {
328 downloadPath =
329 Utils.hasUnifiedPartition(resources) ? resources.getString(R.string.unifiedDataPartition) : resources
330 .getString(R.string.oneGBDataPartition);
331 }
332 return downloadPath;
333 }
334
335 public static boolean canCopyToCache(File file)
336 {
337 double fileSize = file.length();
338 double cacheSize = Utils.getPartitionSizeInBytes(Environment.getDownloadCacheDirectory());
Jose Pascoalb980c862015-03-12 20:22:02 +0000339 return fileSize > 0 && cacheSize >= fileSize;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000340 }
Jose Pascoal40916302015-02-06 18:43:47 +0000341
342 public static String getFilenameFromDownloadableItem(DownloadableItem item, boolean isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000343 {
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000344 StringBuilder filename;
Jose Pascoal40916302015-02-06 18:43:47 +0000345
346 if(isVersion)
347 {
348 filename = getFilenameForItem(item, "update_");
349 }
350 else
351 {
352 filename = getFilenameForItem(item, "store_");
353 }
354
355 return filename.toString();
356 }
357
358 private static StringBuilder getFilenameForItem(DownloadableItem item, String type) {
Jose Pascoal02d86242014-12-17 18:50:08 +0000359 StringBuilder filename = new StringBuilder();
360 filename.append("fp_");
361 if (item != null)
362 {
Jose Pascoal40916302015-02-06 18:43:47 +0000363 filename.append(type);
Jose Pascoal02d86242014-12-17 18:50:08 +0000364 }
365 filename.append(".zip");
Jose Pascoal40916302015-02-06 18:43:47 +0000366 return filename;
Jose Pascoal02d86242014-12-17 18:50:08 +0000367 }
368
Jose Pascoal40916302015-02-06 18:43:47 +0000369 public static String getDownloadTitleFromDownloadableItem(Resources resources, DownloadableItem item, boolean isVersion){
Jose Pascoal02d86242014-12-17 18:50:08 +0000370 String title = "";
371 if (item != null)
372 {
Jose Pascoal40916302015-02-06 18:43:47 +0000373 if (isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000374 {
375 Version version = (Version) item;
Maarten Derks1b51ea52016-03-08 09:02:14 +0100376 title = version.getHumanReadableName();
Jose Pascoal02d86242014-12-17 18:50:08 +0000377 }
Jose Pascoal40916302015-02-06 18:43:47 +0000378 else
Jose Pascoal02d86242014-12-17 18:50:08 +0000379 {
380 Store store = (Store) item;
381 title = store.getName();
382 }
383 }
384 return title;
385 }
Jose Pascoalda00b382015-01-20 18:39:28 +0000386
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000387 public static boolean isDeviceUnsupported(Context context)
Jose Pascoalda00b382015-01-20 18:39:28 +0000388 {
389 Version deviceVersion = VersionParserHelper.getDeviceVersion(context);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000390 return deviceVersion == null || TextUtils.isEmpty(deviceVersion.getName());
Jose Pascoalda00b382015-01-20 18:39:28 +0000391 }
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000392
Filipe72b5a7f2015-09-01 08:59:54 +0000393 private static Map<String,String> buildProps;
394 public static Map<String,String> getpropAll(){
395
396 if(buildProps==null) {
397 buildProps = new HashMap<>();
398 ProcessBuilder pb = new ProcessBuilder("/system/bin/getprop");
399 pb.redirectErrorStream(true);
400 Pattern propRegex = Pattern.compile("\\[([^\\]]+)\\]: \\[([^\\]]+)\\]");
401
402 Process p;
403 InputStream is = null;
404 try {
405 p = pb.start();
406 is = p.getInputStream();
407 Scanner scan = new Scanner(is);
408 String prop;
409 do {
410 prop = scan.nextLine();
411 Matcher match = propRegex.matcher(prop);
412 if(match.find()){
413 buildProps.put(match.group(1), match.group(2));
414 }
415 } while(!prop.isEmpty());
416 } catch (NoSuchElementException e) {
417 } catch (IOException e) {
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000418 }
419 }
Filipe72b5a7f2015-09-01 08:59:54 +0000420 return buildProps;
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000421 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000422
Filipe72b5a7f2015-09-01 08:59:54 +0000423 public static String getprop(String name, String defaultValue)
424 {
425 String result;
426 if (getpropAll().containsKey(name)){
427 result = getpropAll().get(name);
428 } else {
429 result = defaultValue;
430 }
431 return result;
432 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000433
Maarten Derksa02f4f12016-05-26 17:04:58 +0200434 public static void enableBeta(Context context) {
435 SharedPreferences settings = context.getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, Context.MODE_PRIVATE);
436 SharedPreferences.Editor editor = settings.edit();
437 editor.putBoolean(FairphoneUpdater.PREFERENCE_BETA_MODE, true);
438 editor.commit();
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000439 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000440
Jose Pascoal60c99372015-03-17 15:52:59 +0000441 public static String getOtaPackagePath(Resources resources, DownloadableItem item, boolean isVersion, boolean isZipInstall){
Jose Pascoal49f058a2015-02-13 16:58:01 +0000442 String path;
443
444 if (Utils.hasUnifiedPartition(resources))
445 {
446 path = resources.getString(R.string.recoveryCachePath) + Utils.getFilenameFromDownloadableItem(item, isVersion);
447 }
448 else
449 {
Jose Pascoal60c99372015-03-17 15:52:59 +0000450 if(isZipInstall && Build.MODEL.equalsIgnoreCase(resources.getString(R.string.FP1Model)))
451 {
452 //TODO: Find a way to not have this hardcoded
453 String zipPath = item.getDownloadLink();
454 path = zipPath.replace("/storage/sdcard0", resources.getString(R.string.recoverySdCardPath));
455 }
456 else
457 {
458 path = resources.getString(R.string.recoverySdCardPath) + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, isVersion);
459 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000460 }
461
462 return path;
463 }
464
465 public static void writeCacheCommand(Context context, String otaPackagePath) throws IOException, TimeoutException, RootDeniedException, Resources.NotFoundException {
466 if (PrivilegeChecker.isPrivilegedApp()) {
467 File recovery_dir = new File("/cache/recovery/");
468 final boolean mkdirs = recovery_dir.mkdirs();
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000469 if(! (mkdirs || recovery_dir.exists()) ) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000470 String errorMessage = context.getResources().getString(R.string.failed_mkdirs_cache_message);
471 Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
472 throw new IOException(errorMessage);
473 }
474
475 File command = new File("/cache/recovery/command");
476 File extendedCommand = new File("/cache/recovery/extendedcommand");
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000477 final boolean deleteFailed = !extendedCommand.delete();
478 if (deleteFailed) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000479 Log.d(TAG, "Couldn't delete "+extendedCommand.getAbsolutePath());
480 }
481
482 String updateCommand = "--update_package=" + otaPackagePath;
483 PrintWriter writer = new PrintWriter(command, "UTF-8");
484 writer.println("--wipe_cache");
485 writer.println(updateCommand);
486 writer.flush();
487 writer.close();
488 }else {
489 if(RootTools.isAccessGiven()) {
490 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/command"));
491 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/extendedcommand"));
492 Shell.runRootCommand(new CommandCapture(0, "echo '--wipe_cache' >> /cache/recovery/command"));
493 Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=" + otaPackagePath + "' >> /cache/recovery/command"));
494 }else{
495 throw new RootDeniedException("Root Denied");
496 }
497 }
498 }
499
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000500 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
Jose Pascoal49f058a2015-02-13 16:58:01 +0000501 public static boolean rebootToRecovery(Context context) {
502 boolean result;
503 if (PrivilegeChecker.isPrivilegedApp()) {
504 ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).reboot("recovery");
505 result = false;
506 } else {
507 if(RootTools.isAccessGiven()) {
508 try {
509 Shell.runRootCommand(new CommandCapture(0, "reboot recovery"));
510 result = true;
511 } catch (IOException | TimeoutException | RootDeniedException e) {
512 Log.e(TAG, "Error rebooting to recovery: " + e.getLocalizedMessage());
513 result = false;
514 }
515 }else{
516 result = false;
517 }
518
519 }
520 return result;
521 }
Jose Pascoal87758742015-01-28 20:00:22 +0000522
Jose Pascoalcdd82042015-02-06 13:04:26 +0000523// --Commented out by Inspection START (06/02/2015 12:25):
524// public static void printStack(String moreLogs)
525// {
526// StringBuilder sb = new StringBuilder(moreLogs);
527// sb.append("\nStack --> ");
528// for (StackTraceElement ste : Thread.currentThread().getStackTrace())
529// {
530// sb.append(ste.getFileName()).append(" : ").append(ste.getMethodName()).append(":").append(ste.getLineNumber()).append(" -- ");
531// }
532// Log.wtf(TAG, sb.toString());
533// }
534// --Commented out by Inspection STOP (06/02/2015 12:25)
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000535
536 public static boolean fileExists(String otaPackagePath) {
537 boolean fileExists;
538 if(PrivilegeChecker.isPrivilegedApp()){
539 File f = new File(otaPackagePath);
540 fileExists = f.exists();
541 }else {
542 fileExists = RootTools.exists(otaPackagePath);
543 }
544 return fileExists;
545 }
Tiago Costad7e951f2015-03-02 17:37:33 +0000546
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000547 private final static String[] SHELL_COMMANDS_ERASE_DATA = {
Tiago Costad7e951f2015-03-02 17:37:33 +0000548 // remove data
549 "rm -rf /data/data/com.android.providers.media*",
550 "rm -rf /data/data/com.android.keychain*",
551 "rm -rf /data/data/com.android.location.fused*",
552 "rm -rf /data/data/com.android.providers.applications*",
553 "rm -rf /data/data/com.android.providers.media*",
554 "rm -rf /data/data/com.android.vending*",
555 "rm -rf /data/data/com.google.android.apps.genie.geniewidget*",
556 "rm -rf /data/data/com.google.android.apps.plus*",
557 "rm -rf /data/data/com.google.android.ears*",
558 "rm -rf /data/data/com.google.android.gms*",
559 "rm -rf /data/data/com.google.android.googlequicksearchbox*",
560 "rm -rf /data/data/com.google.android.location*",
561 "rm -rf /data/data/com.google.android.marvin.talkback*",
562 // remove cache
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000563// "rm -rf /data/dalvik-cache",
Tiago Costad7e951f2015-03-02 17:37:33 +0000564 // remove data/app
565 "rm -rf /data/app/com.android.apps.plus*",
566 "rm -rf /data/app/com.android.vending*",
567 "rm -rf /data/app/com.android.easr*",
568 "rm -rf /data/app/com.android.gms*",
569 "rm -rf /data/app/com.android.tts*"
570 };
571
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000572 public final static String SHELL_COMMAND_ERASE_DALVIK_CACHE = "rm -rf /data/dalvik-cache";
Tiago Costad7e951f2015-03-02 17:37:33 +0000573 public final static String SHELL_COMMAND_EXIT = "exit";
574
575 public static void clearGappsData() throws RootDeniedException, IOException, InterruptedException {
576
577 if (PrivilegeChecker.isPrivilegedApp()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000578 Process p = Runtime.getRuntime().exec(SHELL_COMMAND_ERASE_DALVIK_CACHE);
Tiago Costad7e951f2015-03-02 17:37:33 +0000579 DataOutputStream os = new DataOutputStream(p.getOutputStream());
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000580 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000581 os.writeBytes(tmpCmd+"\n");
582 }
583 os.writeBytes(SHELL_COMMAND_EXIT+"\n");
584 os.flush();
585 p.waitFor();
586 }else {
587 if(RootTools.isAccessGiven()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000588 try {
589 Shell.runRootCommand(new CommandCapture(0, SHELL_COMMAND_ERASE_DALVIK_CACHE));
590 } catch (TimeoutException e) {
591 e.printStackTrace();
592 }
593 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000594 try {
595 Shell.runRootCommand(new CommandCapture(0, tmpCmd));
596 } catch (TimeoutException e) {
597 e.printStackTrace();
598 }
599 }
600 }else{
601 throw new RootDeniedException("Root Denied");
602 }
603 }
604 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000605
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000606 public static String getPath(final Context context, final Uri uri)
607 {
608 String filePath = uri.getPath();
609 if ("content".equalsIgnoreCase(uri.getScheme())) {
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000610
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000611 //Get the zip file name
612 String[] path = filePath.split("/");
613 String downloadIdStr = "";
614 if (path != null && path.length > 0) {
615 downloadIdStr = path[path.length - 1];
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000616 }
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000617 long downloadId = 0;
618 try {
619 downloadId = Long.parseLong(downloadIdStr);
620 } catch (NumberFormatException nfe) {
621 Log.w(TAG, "NumberFormatException: " + nfe.getMessage());
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000622 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000623
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000624 DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
625 DownloadManager.Query query = new DownloadManager.Query();
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000626
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000627 query.setFilterById(downloadId);
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000628
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000629 Cursor cursor = downloadManager != null ? downloadManager.query(query) : null;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000630
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000631 if (cursor != null && cursor.moveToFirst()) {
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000632 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
633 int status = cursor.getInt(columnIndex);
634
635 switch (status) {
636 case DownloadManager.STATUS_SUCCESSFUL: {
637 filePath = downloadManager.getUriForDownloadedFile(downloadId).getPath();
638 break;
639 }
640 case DownloadManager.STATUS_FAILED:
641 case DownloadManager.STATUS_PAUSED:
642 case DownloadManager.STATUS_PENDING:
643 case DownloadManager.STATUS_RUNNING:
644 default:
645 filePath = "";
646 break;
647 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000648 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000649 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000650
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000651 return filePath;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000652 }
Filipe Gonçalves72817782015-03-19 15:29:05 +0000653
Maarten Derksbf5f51c2016-05-31 14:38:26 +0200654 public static boolean isInternetEnabled(Context context) {
655 ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
656 NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
657 boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
658 return isConnected;
659 }
660
Filipe Gonçalves72817782015-03-19 15:29:05 +0000661 public static boolean isWiFiEnabled(Context context)
662 {
663
664 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
665
666 return manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
667 }
Jose Pascoal055562d2015-03-19 19:31:38 +0000668
Maarten Derkse2f0ffc2016-04-26 11:58:36 +0200669 public static boolean isBatteryLevelOk(Context context) {
670 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
671 Intent batteryStatus = context.registerReceiver(null, ifilter);
672 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
673 int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
674
675 float batteryPct = level / (float)scale;
676
677 if(batteryPct >= Float.parseFloat(context.getResources().getString(R.string.minimumBatteryLevel))) {
678 return true;
679 }
680 return false;
681 }
682
Jose Pascoal055562d2015-03-19 19:31:38 +0000683 public static Store getGappsStore()
684 {
685 return UpdaterData.getInstance().getStore(GAPPS_STORE_NUMBER);
686 }
Maarten Derksa02f4f12016-05-26 17:04:58 +0200687
688 public static void restartUpdater(Activity activity) {
689 Intent startActivity = new Intent(activity.getApplicationContext(), FairphoneUpdater.class);
690 int pendingIntentId = 123456;
691 PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), pendingIntentId, startActivity, PendingIntent.FLAG_CANCEL_CURRENT);
692 AlarmManager mgr = (AlarmManager)activity.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
693 mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent);
694 System.exit(0);
695 }
Maarten Derks417de7a2016-08-25 17:27:59 +0200696
697 public static int getVersionCode(Context context) {
698 PackageManager packageManager = context.getPackageManager();
699 String packageName = context.getPackageName();
700
701 int versionCode = 0;
702
703 try {
704 versionCode = packageManager.getPackageInfo(packageName, 0).versionCode;
705 } catch (PackageManager.NameNotFoundException e) {
706 Log.e(TAG, "App versionCode cannot be retrieved", e);
707 }
708
709 return versionCode;
710 }
Jose Pascoal02b849e2014-06-26 17:07:51 +0100711}