blob: c8b3930bdc8dc373e434f0f682dc328192a6bbbd [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;
Jose Pascoal0a5be012014-11-17 16:55:40 +000027import android.content.res.Resources;
Jose Pascoal2a8283b2015-03-13 14:43:42 +000028import android.database.Cursor;
Filipe Gonçalves72817782015-03-19 15:29:05 +000029import android.net.ConnectivityManager;
Maarten Derksbf5f51c2016-05-31 14:38:26 +020030import android.net.NetworkInfo;
Jose Pascoal2a8283b2015-03-13 14:43:42 +000031import android.net.Uri;
Maarten Derkse2f0ffc2016-04-26 11:58:36 +020032import android.os.BatteryManager;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010033import android.os.Build;
Jose Pascoal0a5be012014-11-17 16:55:40 +000034import android.os.Environment;
Jose Pascoal49f058a2015-02-13 16:58:01 +000035import android.os.PowerManager;
Jose Pascoalda00b382015-01-20 18:39:28 +000036import android.text.TextUtils;
Jose Pascoal02b849e2014-06-26 17:07:51 +010037import android.util.Log;
Jose Pascoal49f058a2015-02-13 16:58:01 +000038import android.widget.Toast;
Jose Pascoal02b849e2014-06-26 17:07:51 +010039
Maarten Derksa02f4f12016-05-26 17:04:58 +020040import com.fairphone.updater.FairphoneUpdater;
Jose Pascoal0a5be012014-11-17 16:55:40 +000041import com.fairphone.updater.R;
Jose Pascoale4c48e32014-10-03 19:55:39 +010042import com.fairphone.updater.UpdaterService;
Jose Pascoal02d86242014-12-17 18:50:08 +000043import com.fairphone.updater.data.DownloadableItem;
44import com.fairphone.updater.data.Store;
Jose Pascoal055562d2015-03-19 19:31:38 +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;
Filipe Gonçalves837e1572015-01-23 17:52:35 +000048import com.stericson.RootTools.RootTools;
49import com.stericson.RootTools.exceptions.RootDeniedException;
50import com.stericson.RootTools.execution.CommandCapture;
51import com.stericson.RootTools.execution.Shell;
Jose Pascoal61e24762014-09-30 19:44:36 +010052
Tiago Costad7e951f2015-03-02 17:37:33 +000053import java.io.DataOutputStream;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000054import java.io.File;
55import java.io.FileInputStream;
56import java.io.FileNotFoundException;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000057import java.io.FileOutputStream;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000058import java.io.IOException;
59import java.io.InputStream;
Jose Pascoal49f058a2015-02-13 16:58:01 +000060import java.io.PrintWriter;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000061import java.math.BigInteger;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000062import java.nio.channels.FileChannel;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000063import java.security.MessageDigest;
64import java.security.NoSuchAlgorithmException;
Filipe72b5a7f2015-09-01 08:59:54 +000065import java.util.HashMap;
66import java.util.Map;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000067import java.util.NoSuchElementException;
68import java.util.Scanner;
69import java.util.concurrent.TimeoutException;
Filipe72b5a7f2015-09-01 08:59:54 +000070import java.util.regex.Matcher;
71import java.util.regex.Pattern;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000072
Jose Pascoal810950b2014-10-09 17:16:08 +010073public class Utils
74{
Jose Pascoal810950b2014-10-09 17:16:08 +010075 private static final String TAG = Utils.class.getSimpleName();
Jose Pascoal46fdb062015-02-05 18:59:32 +000076 private static final int DELAY_100_MILLIS = 100;
77 public static final int DELAY_HALF_SECOND = 500;
Jose Pascoalcfc2dd42015-02-09 18:00:05 +000078 public static final long SECONDS_IN_MINUTE = 60L;
79 public static final long MINUTES_IN_HOUR = 60L;
Jose Pascoal46fdb062015-02-05 18:59:32 +000080
81 private static final double BUFFER_1024_BYTES = 1024d;
Jose Pascoalcdd82042015-02-06 13:04:26 +000082 // --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 +000083 public static final int BUFFER_SIZE_2_KBYTES = 2048;
84 private static final int BUFFER_SIZE_8_KBYTES = 8192;
85 public static final int BUFFER_SIZE_10_MBYTES = 10240;
86 private static final int RADIX_BASE_16 = 16;
87 private static final double PERCENT_100 = 100d;
Jose Pascoalcfc2dd42015-02-09 18:00:05 +000088 private static final char CHAR_SPACE = ' ';
89 private static final char CHAR_ZERO = '0';
Maarten Derksbb287762015-10-12 17:22:45 +020090 public static final String GAPPS_STORE_NUMBER = "0";
Jose Pascoal02b849e2014-06-26 17:07:51 +010091
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000092 private static double getPartitionSizeInGBytes(File path)
Jose Pascoal810950b2014-10-09 17:16:08 +010093 {
94 double availableBlocks = getPartitionSizeInBytes(path);
Jose Pascoal46fdb062015-02-05 18:59:32 +000095 double sizeInGB = ((availableBlocks / BUFFER_1024_BYTES) / BUFFER_1024_BYTES) / BUFFER_1024_BYTES;
Jose Pascoal810950b2014-10-09 17:16:08 +010096 Log.d(TAG, path.getPath() + " size(GB): " + sizeInGB);
97 return sizeInGB;
98 }
Jose Pascoal02b849e2014-06-26 17:07:51 +010099
Jose Pascoalcdd82042015-02-06 13:04:26 +0000100// --Commented out by Inspection START (06/02/2015 12:26):
101// public static double getPartitionSizeInMBytes(File path)
102// {
103// double availableBlocks = getPartitionSizeInBytes(path);
104// double sizeInMB = ((availableBlocks / BUFFER_1024_BYTES)) / BUFFER_1024_BYTES;
105// return sizeInMB;
106// }
107// --Commented out by Inspection STOP (06/02/2015 12:26)
Jose Pascoale4c48e32014-10-03 19:55:39 +0100108
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000109 private static long getPartitionSizeInBytes(File path)
Jose Pascoal810950b2014-10-09 17:16:08 +0100110 {
111 android.os.StatFs stat = new android.os.StatFs(path.getPath());
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000112 long blockSize, blockCount;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000113 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000114 blockSize = stat.getBlockSizeLong();
115 blockCount = stat.getBlockCountLong();
116 } else {
117 // deprectation warnings disabled due to the need to support SDK 17 (FP1)
118 //noinspection deprecation
119 blockSize = stat.getBlockSize();
120 //noinspection deprecation
121 blockCount = stat.getBlockCount();
122 }
123 return blockCount * blockSize;
Jose Pascoal810950b2014-10-09 17:16:08 +0100124 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100125
Jose Pascoal810950b2014-10-09 17:16:08 +0100126 public static long getAvailablePartitionSizeInBytes(File path)
127 {
128 android.os.StatFs stat = new android.os.StatFs(path.getPath());
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000129
130 long blockSize, blockCount;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000131 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000132 blockSize = stat.getBlockSizeLong();
133 blockCount = stat.getAvailableBlocksLong();
134 } else {
135 // deprectation warnings disabled due to the need to support SDK 17 (FP1)
136 //noinspection deprecation
137 blockSize = stat.getBlockSize();
138 //noinspection deprecation
139 blockCount = stat.getAvailableBlocks();
140 }
141 return blockCount * blockSize;
Jose Pascoal810950b2014-10-09 17:16:08 +0100142 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100143
Jose Pascoal810950b2014-10-09 17:16:08 +0100144 public static void startUpdaterService(Context context, boolean forceDownload)
145 {
Filipe72b5a7f2015-09-01 08:59:54 +0000146 Intent i = new Intent(context, UpdaterService.class);
147 i.putExtra(UpdaterService.EXTRA_FORCE_CONFIG_FILE_DOWNLOAD, forceDownload);
148 context.startService(i);
Jose Pascoal810950b2014-10-09 17:16:08 +0100149 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100150
Jose Pascoalcdd82042015-02-06 13:04:26 +0000151// --Commented out by Inspection START (06/02/2015 12:26):
152// public static void stopUpdaterService(Context context)
153// {
154// boolean isRunning = isServiceRunning(context);
155//
156// if (isRunning)
157// {
158// Log.i(TAG, "Stoping Updater Service...");
159// Intent i = new Intent(context, UpdaterService.class);
160// context.stopService(i);
161// try
162// {
163// Thread.sleep(DELAY_100_MILLIS * 2);
164// } catch (InterruptedException e)
165// {
166// Log.w(TAG, "Stop Updater service delay error: " + e.getLocalizedMessage());
167// }
168// }
169// }
170// --Commented out by Inspection STOP (06/02/2015 12:26)
Jose Pascoale4c48e32014-10-03 19:55:39 +0100171
Jose Pascoal810950b2014-10-09 17:16:08 +0100172 // **************************************************************************************************************
173 // HELPERS
174 // **************************************************************************************************************
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100175
Jose Pascoal810950b2014-10-09 17:16:08 +0100176 public static boolean checkMD5(String md5, File updateFile)
177 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100178
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000179 if (updateFile == null || !updateFile.exists())
Jose Pascoal810950b2014-10-09 17:16:08 +0100180 {
181 return false;
182 }
183
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000184 if (md5 == null || md5.isEmpty())
Jose Pascoal810950b2014-10-09 17:16:08 +0100185 {
186 Log.e(TAG, "MD5 String NULL or UpdateFile NULL");
187 return false;
188 }
189
190 String calculatedDigest = calculateMD5(updateFile);
191 if (calculatedDigest == null)
192 {
193 Log.e(TAG, "calculatedDigest NULL");
194 return false;
195 }
196
197 return calculatedDigest.equalsIgnoreCase(md5);
198 }
199
Filipe72b5a7f2015-09-01 08:59:54 +0000200 public static String calculateMD5(File updateFile)
Jose Pascoal810950b2014-10-09 17:16:08 +0100201 {
202 MessageDigest digest;
203 try
204 {
205 digest = MessageDigest.getInstance("MD5");
206 } catch (NoSuchAlgorithmException e)
207 {
208 Log.e(TAG, "Exception while getting Digest", e);
209 return null;
210 }
211
212 InputStream is;
213 try
214 {
215 is = new FileInputStream(updateFile);
216 } catch (FileNotFoundException e)
217 {
218 Log.e(TAG, "Exception while getting FileInputStream", e);
219 return null;
220 }
221
Jose Pascoal46fdb062015-02-05 18:59:32 +0000222 byte[] buffer = new byte[BUFFER_SIZE_8_KBYTES];
Jose Pascoal810950b2014-10-09 17:16:08 +0100223 int read;
224 try
225 {
226 while ((read = is.read(buffer)) > 0)
227 {
228 digest.update(buffer, 0, read);
229 }
230 byte[] md5sum = digest.digest();
231 BigInteger bigInt = new BigInteger(1, md5sum);
Jose Pascoal46fdb062015-02-05 18:59:32 +0000232 String output = bigInt.toString(RADIX_BASE_16);
Jose Pascoal810950b2014-10-09 17:16:08 +0100233 // Fill to 32 chars
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000234 output = String.format("%32s", output).replace(CHAR_SPACE, CHAR_ZERO);
Jose Pascoal810950b2014-10-09 17:16:08 +0100235 return output;
236 } catch (IOException e)
237 {
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000238 Log.e(TAG, "Error digesting MD5: " + e.getLocalizedMessage());
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000239 return null;
240// throw new RuntimeException("Unable to process file for MD5", e);
Jose Pascoal810950b2014-10-09 17:16:08 +0100241 } finally
242 {
243 try
244 {
245 is.close();
246 } catch (IOException e)
247 {
248 Log.e(TAG, "Exception on closing MD5 input stream", e);
249 }
250 }
251 }
252
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000253 public static void copy(File src, File dst) throws IOException {
254 if (PrivilegeChecker.isPrivilegedApp()) {
255 copyPrivileged(src, dst);
256 } else {
257 copyUnprivileged(src, dst);
258 }
259 }
260
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000261 private static void copyUnprivileged(File src, File dst) throws IOException {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000262 if (RootTools.isAccessGiven()) {
263 RootTools.copyFile(src.getPath(), dst.getPath(), false, false);
264 } else {
265 throw new IOException("No root permissions granted.");
266 }
267 }
268
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000269 private static void copyPrivileged(File src, File dst) throws IOException {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000270 FileInputStream inStream = new FileInputStream(src);
271 FileOutputStream outStream = new FileOutputStream(dst);
272 FileChannel inChannel = inStream.getChannel();
273 FileChannel outChannel = outStream.getChannel();
274 inChannel.transferTo(0, inChannel.size(), outChannel);
275 inStream.close();
276 outStream.close();
277 }
278
Jose Pascoal0a5be012014-11-17 16:55:40 +0000279 public static void clearCache()
280 {
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000281 if(PrivilegeChecker.isPrivilegedApp()) {
282 File f = Environment.getDownloadCacheDirectory();
283 File[] files = f.listFiles();
284 if (files != null) {
285 Log.d(TAG, "Size: " + files.length);
286 for (File file : files) {
287 String filename = file.getName();
Jose Pascoal0a5be012014-11-17 16:55:40 +0000288
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000289 if (filename.endsWith(".zip")) {
290 final boolean delete = file.delete();
291 if (delete) {
292 Log.d(TAG, "Deleted file " + filename);
293 } else {
294 Log.d(TAG, "Failed to delete file " + filename);
295 }
296 }
297 }
298 }
299 } else {
300 if(RootTools.isAccessGiven()) {
301 try {
302 Shell.runRootCommand(new CommandCapture(0, "rm -f *.zip"));
303 } catch (IOException | TimeoutException |RootDeniedException e) {
304 Log.w(TAG, "Failed to clear cache: " + e.getLocalizedMessage());
305 }
306 }
Jose Pascoal0a5be012014-11-17 16:55:40 +0000307 }
308 }
309
310 public static boolean hasUnifiedPartition(Resources resources)
311 {
312 File path = Environment.getDataDirectory();
313 double sizeInGB = Utils.getPartitionSizeInGBytes(path);
Jose Pascoal46fdb062015-02-05 18:59:32 +0000314 double roundedSize = Math.ceil(sizeInGB * PERCENT_100) / PERCENT_100;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000315 Log.d(TAG, "/data size: " + roundedSize + "Gb");
316
Jose Pascoal8d4458f2015-03-17 20:03:59 +0000317 double fp1DataPartitionSize = (double) resources.getInteger(R.integer.FP1DataPartitionSizeMb) / BUFFER_1024_BYTES;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000318 // Add a little buffer to the 1gb default just in case
319 return roundedSize > fp1DataPartitionSize;
320 }
321
322 public static String getPartitionDownloadPath(Resources resources)
323 {
324 String downloadPath = "";
325 if (Build.MODEL.equals(resources.getString(R.string.FP1Model)))
326 {
327 downloadPath =
328 Utils.hasUnifiedPartition(resources) ? resources.getString(R.string.unifiedDataPartition) : resources
329 .getString(R.string.oneGBDataPartition);
330 }
331 return downloadPath;
332 }
333
334 public static boolean canCopyToCache(File file)
335 {
336 double fileSize = file.length();
337 double cacheSize = Utils.getPartitionSizeInBytes(Environment.getDownloadCacheDirectory());
Jose Pascoalb980c862015-03-12 20:22:02 +0000338 return fileSize > 0 && cacheSize >= fileSize;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000339 }
Jose Pascoal40916302015-02-06 18:43:47 +0000340
341 public static String getFilenameFromDownloadableItem(DownloadableItem item, boolean isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000342 {
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000343 StringBuilder filename;
Jose Pascoal40916302015-02-06 18:43:47 +0000344
345 if(isVersion)
346 {
347 filename = getFilenameForItem(item, "update_");
348 }
349 else
350 {
351 filename = getFilenameForItem(item, "store_");
352 }
353
354 return filename.toString();
355 }
356
357 private static StringBuilder getFilenameForItem(DownloadableItem item, String type) {
Jose Pascoal02d86242014-12-17 18:50:08 +0000358 StringBuilder filename = new StringBuilder();
359 filename.append("fp_");
360 if (item != null)
361 {
Jose Pascoal40916302015-02-06 18:43:47 +0000362 filename.append(type);
Jose Pascoal02d86242014-12-17 18:50:08 +0000363 }
364 filename.append(".zip");
Jose Pascoal40916302015-02-06 18:43:47 +0000365 return filename;
Jose Pascoal02d86242014-12-17 18:50:08 +0000366 }
367
Jose Pascoal40916302015-02-06 18:43:47 +0000368 public static String getDownloadTitleFromDownloadableItem(Resources resources, DownloadableItem item, boolean isVersion){
Jose Pascoal02d86242014-12-17 18:50:08 +0000369 String title = "";
370 if (item != null)
371 {
Jose Pascoal40916302015-02-06 18:43:47 +0000372 if (isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000373 {
374 Version version = (Version) item;
Maarten Derks1b51ea52016-03-08 09:02:14 +0100375 title = version.getHumanReadableName();
Jose Pascoal02d86242014-12-17 18:50:08 +0000376 }
Jose Pascoal40916302015-02-06 18:43:47 +0000377 else
Jose Pascoal02d86242014-12-17 18:50:08 +0000378 {
379 Store store = (Store) item;
380 title = store.getName();
381 }
382 }
383 return title;
384 }
Jose Pascoalda00b382015-01-20 18:39:28 +0000385
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000386 public static boolean isDeviceUnsupported(Context context)
Jose Pascoalda00b382015-01-20 18:39:28 +0000387 {
388 Version deviceVersion = VersionParserHelper.getDeviceVersion(context);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000389 return deviceVersion == null || TextUtils.isEmpty(deviceVersion.getName());
Jose Pascoalda00b382015-01-20 18:39:28 +0000390 }
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000391
Filipe72b5a7f2015-09-01 08:59:54 +0000392 private static Map<String,String> buildProps;
393 public static Map<String,String> getpropAll(){
394
395 if(buildProps==null) {
396 buildProps = new HashMap<>();
397 ProcessBuilder pb = new ProcessBuilder("/system/bin/getprop");
398 pb.redirectErrorStream(true);
399 Pattern propRegex = Pattern.compile("\\[([^\\]]+)\\]: \\[([^\\]]+)\\]");
400
401 Process p;
402 InputStream is = null;
403 try {
404 p = pb.start();
405 is = p.getInputStream();
406 Scanner scan = new Scanner(is);
407 String prop;
408 do {
409 prop = scan.nextLine();
410 Matcher match = propRegex.matcher(prop);
411 if(match.find()){
412 buildProps.put(match.group(1), match.group(2));
413 }
414 } while(!prop.isEmpty());
415 } catch (NoSuchElementException e) {
416 } catch (IOException e) {
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000417 }
418 }
Filipe72b5a7f2015-09-01 08:59:54 +0000419 return buildProps;
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000420 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000421
Filipe72b5a7f2015-09-01 08:59:54 +0000422 public static String getprop(String name, String defaultValue)
423 {
424 String result;
425 if (getpropAll().containsKey(name)){
426 result = getpropAll().get(name);
427 } else {
428 result = defaultValue;
429 }
430 return result;
431 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000432
Maarten Derksa02f4f12016-05-26 17:04:58 +0200433 public static void enableBeta(Context context) {
434 SharedPreferences settings = context.getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, Context.MODE_PRIVATE);
435 SharedPreferences.Editor editor = settings.edit();
436 editor.putBoolean(FairphoneUpdater.PREFERENCE_BETA_MODE, true);
437 editor.commit();
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000438 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000439
Jose Pascoal60c99372015-03-17 15:52:59 +0000440 public static String getOtaPackagePath(Resources resources, DownloadableItem item, boolean isVersion, boolean isZipInstall){
Jose Pascoal49f058a2015-02-13 16:58:01 +0000441 String path;
442
443 if (Utils.hasUnifiedPartition(resources))
444 {
445 path = resources.getString(R.string.recoveryCachePath) + Utils.getFilenameFromDownloadableItem(item, isVersion);
446 }
447 else
448 {
Jose Pascoal60c99372015-03-17 15:52:59 +0000449 if(isZipInstall && Build.MODEL.equalsIgnoreCase(resources.getString(R.string.FP1Model)))
450 {
451 //TODO: Find a way to not have this hardcoded
452 String zipPath = item.getDownloadLink();
453 path = zipPath.replace("/storage/sdcard0", resources.getString(R.string.recoverySdCardPath));
454 }
455 else
456 {
457 path = resources.getString(R.string.recoverySdCardPath) + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, isVersion);
458 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000459 }
460
461 return path;
462 }
463
464 public static void writeCacheCommand(Context context, String otaPackagePath) throws IOException, TimeoutException, RootDeniedException, Resources.NotFoundException {
465 if (PrivilegeChecker.isPrivilegedApp()) {
466 File recovery_dir = new File("/cache/recovery/");
467 final boolean mkdirs = recovery_dir.mkdirs();
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000468 if(! (mkdirs || recovery_dir.exists()) ) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000469 String errorMessage = context.getResources().getString(R.string.failed_mkdirs_cache_message);
470 Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
471 throw new IOException(errorMessage);
472 }
473
474 File command = new File("/cache/recovery/command");
475 File extendedCommand = new File("/cache/recovery/extendedcommand");
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000476 final boolean deleteFailed = !extendedCommand.delete();
477 if (deleteFailed) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000478 Log.d(TAG, "Couldn't delete "+extendedCommand.getAbsolutePath());
479 }
480
481 String updateCommand = "--update_package=" + otaPackagePath;
482 PrintWriter writer = new PrintWriter(command, "UTF-8");
483 writer.println("--wipe_cache");
484 writer.println(updateCommand);
485 writer.flush();
486 writer.close();
487 }else {
488 if(RootTools.isAccessGiven()) {
489 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/command"));
490 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/extendedcommand"));
491 Shell.runRootCommand(new CommandCapture(0, "echo '--wipe_cache' >> /cache/recovery/command"));
492 Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=" + otaPackagePath + "' >> /cache/recovery/command"));
493 }else{
494 throw new RootDeniedException("Root Denied");
495 }
496 }
497 }
498
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000499 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
Jose Pascoal49f058a2015-02-13 16:58:01 +0000500 public static boolean rebootToRecovery(Context context) {
501 boolean result;
502 if (PrivilegeChecker.isPrivilegedApp()) {
503 ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).reboot("recovery");
504 result = false;
505 } else {
506 if(RootTools.isAccessGiven()) {
507 try {
508 Shell.runRootCommand(new CommandCapture(0, "reboot recovery"));
509 result = true;
510 } catch (IOException | TimeoutException | RootDeniedException e) {
511 Log.e(TAG, "Error rebooting to recovery: " + e.getLocalizedMessage());
512 result = false;
513 }
514 }else{
515 result = false;
516 }
517
518 }
519 return result;
520 }
Jose Pascoal87758742015-01-28 20:00:22 +0000521
Jose Pascoalcdd82042015-02-06 13:04:26 +0000522// --Commented out by Inspection START (06/02/2015 12:25):
523// public static void printStack(String moreLogs)
524// {
525// StringBuilder sb = new StringBuilder(moreLogs);
526// sb.append("\nStack --> ");
527// for (StackTraceElement ste : Thread.currentThread().getStackTrace())
528// {
529// sb.append(ste.getFileName()).append(" : ").append(ste.getMethodName()).append(":").append(ste.getLineNumber()).append(" -- ");
530// }
531// Log.wtf(TAG, sb.toString());
532// }
533// --Commented out by Inspection STOP (06/02/2015 12:25)
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000534
535 public static boolean fileExists(String otaPackagePath) {
536 boolean fileExists;
537 if(PrivilegeChecker.isPrivilegedApp()){
538 File f = new File(otaPackagePath);
539 fileExists = f.exists();
540 }else {
541 fileExists = RootTools.exists(otaPackagePath);
542 }
543 return fileExists;
544 }
Tiago Costad7e951f2015-03-02 17:37:33 +0000545
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000546 private final static String[] SHELL_COMMANDS_ERASE_DATA = {
Tiago Costad7e951f2015-03-02 17:37:33 +0000547 // remove data
548 "rm -rf /data/data/com.android.providers.media*",
549 "rm -rf /data/data/com.android.keychain*",
550 "rm -rf /data/data/com.android.location.fused*",
551 "rm -rf /data/data/com.android.providers.applications*",
552 "rm -rf /data/data/com.android.providers.media*",
553 "rm -rf /data/data/com.android.vending*",
554 "rm -rf /data/data/com.google.android.apps.genie.geniewidget*",
555 "rm -rf /data/data/com.google.android.apps.plus*",
556 "rm -rf /data/data/com.google.android.ears*",
557 "rm -rf /data/data/com.google.android.gms*",
558 "rm -rf /data/data/com.google.android.googlequicksearchbox*",
559 "rm -rf /data/data/com.google.android.location*",
560 "rm -rf /data/data/com.google.android.marvin.talkback*",
561 // remove cache
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000562// "rm -rf /data/dalvik-cache",
Tiago Costad7e951f2015-03-02 17:37:33 +0000563 // remove data/app
564 "rm -rf /data/app/com.android.apps.plus*",
565 "rm -rf /data/app/com.android.vending*",
566 "rm -rf /data/app/com.android.easr*",
567 "rm -rf /data/app/com.android.gms*",
568 "rm -rf /data/app/com.android.tts*"
569 };
570
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000571 public final static String SHELL_COMMAND_ERASE_DALVIK_CACHE = "rm -rf /data/dalvik-cache";
Tiago Costad7e951f2015-03-02 17:37:33 +0000572 public final static String SHELL_COMMAND_EXIT = "exit";
573
574 public static void clearGappsData() throws RootDeniedException, IOException, InterruptedException {
575
576 if (PrivilegeChecker.isPrivilegedApp()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000577 Process p = Runtime.getRuntime().exec(SHELL_COMMAND_ERASE_DALVIK_CACHE);
Tiago Costad7e951f2015-03-02 17:37:33 +0000578 DataOutputStream os = new DataOutputStream(p.getOutputStream());
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000579 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000580 os.writeBytes(tmpCmd+"\n");
581 }
582 os.writeBytes(SHELL_COMMAND_EXIT+"\n");
583 os.flush();
584 p.waitFor();
585 }else {
586 if(RootTools.isAccessGiven()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000587 try {
588 Shell.runRootCommand(new CommandCapture(0, SHELL_COMMAND_ERASE_DALVIK_CACHE));
589 } catch (TimeoutException e) {
590 e.printStackTrace();
591 }
592 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000593 try {
594 Shell.runRootCommand(new CommandCapture(0, tmpCmd));
595 } catch (TimeoutException e) {
596 e.printStackTrace();
597 }
598 }
599 }else{
600 throw new RootDeniedException("Root Denied");
601 }
602 }
603 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000604
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000605 public static String getPath(final Context context, final Uri uri)
606 {
607 String filePath = uri.getPath();
608 if ("content".equalsIgnoreCase(uri.getScheme())) {
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000609
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000610 //Get the zip file name
611 String[] path = filePath.split("/");
612 String downloadIdStr = "";
613 if (path != null && path.length > 0) {
614 downloadIdStr = path[path.length - 1];
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000615 }
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000616 long downloadId = 0;
617 try {
618 downloadId = Long.parseLong(downloadIdStr);
619 } catch (NumberFormatException nfe) {
620 Log.w(TAG, "NumberFormatException: " + nfe.getMessage());
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000621 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000622
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000623 DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
624 DownloadManager.Query query = new DownloadManager.Query();
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000625
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000626 query.setFilterById(downloadId);
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000627
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000628 Cursor cursor = downloadManager != null ? downloadManager.query(query) : null;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000629
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000630 if (cursor != null && cursor.moveToFirst()) {
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000631 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
632 int status = cursor.getInt(columnIndex);
633
634 switch (status) {
635 case DownloadManager.STATUS_SUCCESSFUL: {
636 filePath = downloadManager.getUriForDownloadedFile(downloadId).getPath();
637 break;
638 }
639 case DownloadManager.STATUS_FAILED:
640 case DownloadManager.STATUS_PAUSED:
641 case DownloadManager.STATUS_PENDING:
642 case DownloadManager.STATUS_RUNNING:
643 default:
644 filePath = "";
645 break;
646 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000647 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000648 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000649
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000650 return filePath;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000651 }
Filipe Gonçalves72817782015-03-19 15:29:05 +0000652
Maarten Derksbf5f51c2016-05-31 14:38:26 +0200653 public static boolean isInternetEnabled(Context context) {
654 ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
655 NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
656 boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
657 return isConnected;
658 }
659
Filipe Gonçalves72817782015-03-19 15:29:05 +0000660 public static boolean isWiFiEnabled(Context context)
661 {
662
663 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
664
665 return manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
666 }
Jose Pascoal055562d2015-03-19 19:31:38 +0000667
Maarten Derkse2f0ffc2016-04-26 11:58:36 +0200668 public static boolean isBatteryLevelOk(Context context) {
669 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
670 Intent batteryStatus = context.registerReceiver(null, ifilter);
671 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
672 int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
673
674 float batteryPct = level / (float)scale;
675
676 if(batteryPct >= Float.parseFloat(context.getResources().getString(R.string.minimumBatteryLevel))) {
677 return true;
678 }
679 return false;
680 }
681
Jose Pascoal055562d2015-03-19 19:31:38 +0000682 public static Store getGappsStore()
683 {
684 return UpdaterData.getInstance().getStore(GAPPS_STORE_NUMBER);
685 }
Maarten Derksa02f4f12016-05-26 17:04:58 +0200686
687 public static void restartUpdater(Activity activity) {
688 Intent startActivity = new Intent(activity.getApplicationContext(), FairphoneUpdater.class);
689 int pendingIntentId = 123456;
690 PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), pendingIntentId, startActivity, PendingIntent.FLAG_CANCEL_CURRENT);
691 AlarmManager mgr = (AlarmManager)activity.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
692 mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent);
693 System.exit(0);
694 }
Jose Pascoal02b849e2014-06-26 17:07:51 +0100695}